From ba10122b811e1f91b0765348389b13d621776249 Mon Sep 17 00:00:00 2001 From: patrick Date: Tue, 1 Aug 2023 14:43:30 +0200 Subject: [PATCH 1/5] add inspect by value for functions and objects that are not simple objects --- app/components/object-inspector.hbs | 16 +++++++++- app/components/object-inspector.js | 14 +++++++++ .../object-inspector/properties-all.hbs | 1 + .../object-inspector/properties-base.js | 10 +++++++ .../object-inspector/properties-grouped.hbs | 1 + app/components/object-inspector/property.hbs | 13 +++++++- app/components/object-inspector/property.ts | 2 ++ app/controllers/component-tree.js | 6 ++++ app/routes/component-tree.js | 6 ---- app/services/adapters/web-extension.js | 6 ++-- app/services/port.js | 4 +++ app/utils/parse-text.ts | 1 + ember_debug/adapters/basic.js | 8 ++--- ember_debug/adapters/web-extension.js | 8 ++--- ember_debug/libs/view-inspection.js | 28 +++++++++++++++++ ember_debug/object-inspector.js | 23 ++++++++++++++ ember_debug/view-debug.js | 2 +- lib/ui/addon/styles/_goto-source.scss | 30 +++++++++++++++++++ lib/ui/addon/styles/addon.scss | 1 + lib/ui/addon/styles/toolbar/_index.scss | 4 +++ public/assets/svg/code-source.svg | 9 ++++++ 21 files changed, 173 insertions(+), 20 deletions(-) create mode 100644 lib/ui/addon/styles/_goto-source.scss create mode 100644 public/assets/svg/code-source.svg diff --git a/app/components/object-inspector.hbs b/app/components/object-inspector.hbs index c9ed9bb7ca..2c62bd0f45 100644 --- a/app/components/object-inspector.hbs +++ b/app/components/object-inspector.hbs @@ -21,11 +21,25 @@ type="button" {{on "click" - (fn this.sendObjectToConsole (get @model (sub @model.length 1))) + (fn this.sendObjectToConsole this.current) }} > {{svg-jar "send-with-text" width="20px" height="10px"}} + {{#if this.isClass}} + + {{/if}} {{#if this.trail}} diff --git a/app/components/object-inspector.js b/app/components/object-inspector.js index c0efb8466d..f8e4a2ed12 100644 --- a/app/components/object-inspector.js +++ b/app/components/object-inspector.js @@ -14,6 +14,13 @@ export default class ObjectInspector extends Component { this.searchInputId = 'custom-filter-input'; } + get isClass() { + return this.current.mixins.length > 1; + } + + get current() { + return this.args.model[this.args.model.length - 1]; + } get trail() { let nested = this.args.model.slice(1); if (nested.length === 0) { @@ -55,6 +62,13 @@ export default class ObjectInspector extends Component { }); } + @action gotoSource(obj) { + let objectId = obj.objectId; + this.port.send('objectInspector:gotoSource', { + objectId, + }); + } + @action popStack() { if (this.isNested) { this.args.popMixinDetails(); diff --git a/app/components/object-inspector/properties-all.hbs b/app/components/object-inspector/properties-all.hbs index d8abb215ef..369d1331be 100644 --- a/app/components/object-inspector/properties-all.hbs +++ b/app/components/object-inspector/properties-all.hbs @@ -11,6 +11,7 @@ @model={{prop}} @saveProperty={{this.saveProperty}} @sendToConsole={{fn this.sendToConsole prop}} + @gotoSource={{fn this.gotoSource prop}} /> {{/each}} diff --git a/app/components/object-inspector/properties-base.js b/app/components/object-inspector/properties-base.js index 6e90afc2ac..a4e0363671 100644 --- a/app/components/object-inspector/properties-base.js +++ b/app/components/object-inspector/properties-base.js @@ -15,6 +15,16 @@ export default class PropertiesBase extends Component { this.port.send('objectInspector:sendToConsole', data); } + @action gotoSource({ name }) { + const data = { + objectId: this.args.model.objectId, + }; + if (name !== '...') { + data.property = name; + } + this.port.send('objectInspector:gotoSource', data); + } + @action digDeeper({ name }) { this.port.send('objectInspector:digDeeper', { objectId: this.args.model.objectId, diff --git a/app/components/object-inspector/properties-grouped.hbs b/app/components/object-inspector/properties-grouped.hbs index e05f4dbcc1..7b2bd70d2d 100644 --- a/app/components/object-inspector/properties-grouped.hbs +++ b/app/components/object-inspector/properties-grouped.hbs @@ -33,6 +33,7 @@ @digDeeper={{fn this.digDeeper prop}} @saveProperty={{this.saveProperty}} @sendToConsole={{fn this.sendToConsole prop}} + @gotoSource={{fn this.gotoSource prop}} /> {{else}}
  • No Properties
  • diff --git a/app/components/object-inspector/property.hbs b/app/components/object-inspector/property.hbs index bf313b6f76..de77328052 100644 --- a/app/components/object-inspector/property.hbs +++ b/app/components/object-inspector/property.hbs @@ -119,12 +119,23 @@ + {{#if this.isFunction}} + + {{/if}} {{#if this.showDependentKeys}} diff --git a/app/components/object-inspector/property.ts b/app/components/object-inspector/property.ts index f621253784..27701979ff 100644 --- a/app/components/object-inspector/property.ts +++ b/app/components/object-inspector/property.ts @@ -8,6 +8,8 @@ import parseText from 'ember-inspector/utils/parse-text'; interface ObjectInspectorPropertyArgs { model: any; digDeeper: () => unknown; + gotoSource: () => void; + sendToConsole: () => void; saveProperty: ( property: unknown, value: unknown, diff --git a/app/controllers/component-tree.js b/app/controllers/component-tree.js index 927beaae61..e5b8352fcd 100644 --- a/app/controllers/component-tree.js +++ b/app/controllers/component-tree.js @@ -439,6 +439,12 @@ class RenderItem { this.send('view:inspectElement', { id: this.id }); } + @action inspectValue(event) { + event.stopPropagation(); + + this.send('view:inspectValue', { id: this.id }); + } + show() { let item = this.parentItem; diff --git a/app/routes/component-tree.js b/app/routes/component-tree.js index 8a3031b431..13851ab0e4 100644 --- a/app/routes/component-tree.js +++ b/app/routes/component-tree.js @@ -31,7 +31,6 @@ export default class ComponentTreeRoute extends TabRoute { this.port.on('view:cancelSelection', this, this.cancelSelection); this.port.on('view:startInspecting', this, this.startInspecting); this.port.on('view:stopInspecting', this, this.stopInspecting); - this.port.on('view:inspectDOMNode', this, this.inspectDOMNode); } deactivate() { @@ -41,7 +40,6 @@ export default class ComponentTreeRoute extends TabRoute { this.port.off('view:cancelSelection', this, this.cancelSelection); this.port.off('view:startInspecting', this, this.startInspecting); this.port.off('view:stopInspecting', this, this.stopInspecting); - this.port.off('view:inspectDOMNode', this, this.inspectDOMNode); } setRenderTree({ tree }) { @@ -59,8 +57,4 @@ export default class ComponentTreeRoute extends TabRoute { stopInspecting() { this.controller.isInspecting = false; } - - inspectDOMNode({ name }) { - this.port.adapter.inspectDOMNode(name); - } } diff --git a/app/services/adapters/web-extension.js b/app/services/adapters/web-extension.js index be53dd7567..a8659973ea 100644 --- a/app/services/adapters/web-extension.js +++ b/app/services/adapters/web-extension.js @@ -107,12 +107,12 @@ export default class WebExtension extends BasicAdapter { } /** - * Open the devtools "Elements" tab and select a specific DOM node. + * Open the devtools "Elements" or "Sources" tab and select a specific DOM node or function. * - * @method inspectDOMNode + * @method inspectJSValue * @param {String} name */ - inspectDOMNode(name) { + inspectJSValue(name) { chrome.devtools.inspectedWindow.eval(` inspect(window[${JSON.stringify(name)}]); delete window[${JSON.stringify(name)}]; diff --git a/app/services/port.js b/app/services/port.js index 0ff7373970..9ac54f9004 100644 --- a/app/services/port.js +++ b/app/services/port.js @@ -49,6 +49,10 @@ export default class PortService extends Service.extend(Evented) { this.trigger(message.type, message, applicationId); } }); + + this.on('view:inspectJSValue', this, ({ name }) => + this.adapter.inspectJSValue(name) + ); } selectApplication(applicationId) { diff --git a/app/utils/parse-text.ts b/app/utils/parse-text.ts index 4e6504eb60..258382cbf8 100644 --- a/app/utils/parse-text.ts +++ b/app/utils/parse-text.ts @@ -18,3 +18,4 @@ export default function parseText(value: string): string { } return parsedValue; } + diff --git a/ember_debug/adapters/basic.js b/ember_debug/adapters/basic.js index ba9074c3eb..b873119cd6 100644 --- a/ember_debug/adapters/basic.js +++ b/ember_debug/adapters/basic.js @@ -61,17 +61,17 @@ export default class BasicAdapter extends BaseObject { } /** - Inspect a specific DOM node. This usually + Inspect a js value or specific DOM node. This usually means using the current environment's tools to inspect the node in the DOM. For example, in chrome, `inspect(node)` will open the Elements tab in dev tools and highlight the DOM node. - - @param {Node} node + For functions, it will open the sources tab and goto the definition + @param {Node|Function} node */ - inspectNode(/* node */) {} + inspectValue(/* value */) {} _messageReceived(message) { this._messageCallbacks.forEach((callback) => { diff --git a/ember_debug/adapters/web-extension.js b/ember_debug/adapters/web-extension.js index 98772cce65..d1749b632d 100644 --- a/ember_debug/adapters/web-extension.js +++ b/ember_debug/adapters/web-extension.js @@ -34,9 +34,9 @@ export default class extends BasicAdapter { /** * Open the devtools "Elements" and select an DOM node. * - * @param {Node} node The DOM node to select + * @param {Node|Function} value The DOM node to select */ - inspectNode(node) { + inspectValue(value) { // NOTE: // // Basically, we are just trying to call `inspect(node)` here. @@ -58,9 +58,9 @@ export default class extends BasicAdapter { let name = `__EMBER_INSPECTOR_${(Math.random() * 100000000).toFixed(0)}`; - window[name] = node; + window[name] = value; - this.namespace.port.send('view:inspectDOMNode', { name }); + this.namespace.port.send('view:inspectJSValue', { name }); } _listen() { diff --git a/ember_debug/libs/view-inspection.js b/ember_debug/libs/view-inspection.js index a0afc12618..685aff62fe 100644 --- a/ember_debug/libs/view-inspection.js +++ b/ember_debug/libs/view-inspection.js @@ -78,6 +78,22 @@ function makeStylesheet(id) { color: rgb(168, 148, 166); } + #${prefix}-tooltip-${id} .${prefix}-tooltip-detail-instance > .${prefix}-tooltip-token-tag { + cursor: pointer; + } + + #${prefix}-tooltip-${id} .${prefix}-tooltip-detail-instance > .${prefix}-tooltip-token-tag:after { + content: "\\1F517" + } + + #${prefix}-tooltip-${id} .${prefix}-tooltip-detail-controller > .${prefix}-tooltip-token-tag { + cursor: pointer; + } + + #${prefix}-tooltip-${id} .${prefix}-tooltip-detail-controller > .${prefix}-tooltip-token-tag:after { + content: "\\1F517" + } + #${prefix}-tooltip-${id} .${prefix}-tooltip-token-name { /* https://github.com/ChromeDevTools/devtools-frontend/blob/103326238685ac582d3bf2a02f1627a80e3fce5f/front_end/ui/inspectorSyntaxHighlight.css#L60 */ color: rgb(136, 18, 128); @@ -505,6 +521,18 @@ export default class ViewInspection { this._tokenizeItem(node.instance) ); } + const detail = + tbody.querySelector( + '.ember-inspector-tooltip-detail-instance > .ember-inspector-tooltip-token-tag' + ) || + tbody.querySelector( + '.ember-inspector-tooltip-detail-controller > .ember-inspector-tooltip-token-tag' + ); + if (detail) { + detail.onclick = () => { + this.objectInspector.sendToConsole(node.instance.id); + }; + } } } diff --git a/ember_debug/object-inspector.js b/ember_debug/object-inspector.js index fec9f0ef51..ca9811118c 100644 --- a/ember_debug/object-inspector.js +++ b/ember_debug/object-inspector.js @@ -431,6 +431,9 @@ export default class extends DebugPort { sendToConsole(message) { this.sendToConsole(message.objectId, message.property); }, + gotoSource(message) { + this.gotoSource(message.objectId, message.property); + }, sendControllerToConsole(message) { const container = this.namespace?.owner; this.sendValueToConsole(container.lookup(`controller:${message.name}`)); @@ -516,6 +519,26 @@ export default class extends DebugPort { }); } + gotoSource(objectId, prop) { + let object = this.sentObjects[objectId]; + let value; + + if (prop === null || prop === undefined) { + value = this.sentObjects[objectId]; + } else { + value = calculateCP(object, { name: prop }, {}); + } + // for functions and classes we want to show the source + if (typeof value === 'function') { + this.adapter.inspectValue(value); + } + // use typeOf to distinguish basic objects/classes and Date, Error etc. + // objects like {...} have the constructor set to Object + if (typeOf(value) === 'object' && value.constructor !== Object) { + this.adapter.inspectValue(value.constructor); + } + } + sendToConsole(objectId, prop) { let object = this.sentObjects[objectId]; let value; diff --git a/ember_debug/view-debug.js b/ember_debug/view-debug.js index d56e7f308e..1b90a8c7db 100644 --- a/ember_debug/view-debug.js +++ b/ember_debug/view-debug.js @@ -132,7 +132,7 @@ export default class extends DebugPort { * @param {Node} node The DOM node to inspect */ inspectNode(node) { - this.adapter.inspectNode(node); + this.adapter.inspectValue(node); } sendTree(immediate = false) { diff --git a/lib/ui/addon/styles/_goto-source.scss b/lib/ui/addon/styles/_goto-source.scss new file mode 100644 index 0000000000..22781eb191 --- /dev/null +++ b/lib/ui/addon/styles/_goto-source.scss @@ -0,0 +1,30 @@ +.goto-source { + background: none; + border: none; + border-bottom: 1px solid transparent; + color: var(--base15); + cursor: pointer; + margin: 0; + outline: none; + padding: 0; + + svg { + vertical-align: middle; + } + + .send-chevron { + fill: var(--focus); + } + + .send-text { + fill: var(--base12); + } +} + +.goto-source:hover { + border-bottom-color: var(--focus); +} + +.goto-source:active { + transform: translateY(1px); +} diff --git a/lib/ui/addon/styles/addon.scss b/lib/ui/addon/styles/addon.scss index 51ebf2e185..97bd84fe8d 100644 --- a/lib/ui/addon/styles/addon.scss +++ b/lib/ui/addon/styles/addon.scss @@ -6,6 +6,7 @@ @import 'nav'; @import 'pill'; @import 'send-to-console'; +@import 'goto-source'; @import 'object-inspector-toggle'; @import 'split'; @import 'toolbar/index'; diff --git a/lib/ui/addon/styles/toolbar/_index.scss b/lib/ui/addon/styles/toolbar/_index.scss index e7d294cd02..9e06b83830 100644 --- a/lib/ui/addon/styles/toolbar/_index.scss +++ b/lib/ui/addon/styles/toolbar/_index.scss @@ -17,6 +17,10 @@ margin-left: 5px; margin-right: 5px; } +.toolbar .goto-source { + margin-left: 5px; + margin-right: 5px; +} @import 'checkbox'; @import 'divider'; diff --git a/public/assets/svg/code-source.svg b/public/assets/svg/code-source.svg new file mode 100644 index 0000000000..d3656a44ee --- /dev/null +++ b/public/assets/svg/code-source.svg @@ -0,0 +1,9 @@ + + + + + + + + + From 4442f6cb28fdd2a9d61afb92d894b5ea0d994d3a Mon Sep 17 00:00:00 2001 From: patrick Date: Mon, 11 Mar 2024 16:18:04 +0100 Subject: [PATCH 2/5] wip add tests --- tests/acceptance/object-inspector-test.js | 68 +++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/tests/acceptance/object-inspector-test.js b/tests/acceptance/object-inspector-test.js index 2e3cc0bb8c..33100f923a 100644 --- a/tests/acceptance/object-inspector-test.js +++ b/tests/acceptance/object-inspector-test.js @@ -785,6 +785,74 @@ module('Object Inspector', function (hooks) { await click('[data-test-send-object-to-console-btn]'); }); + test('Goto Source', async function (assert) { + assert.expect(6); + + await visit('/'); + + await sendMessage({ + type: 'objectInspector:updateObject', + name: 'My Object', + objectId: 'object-id', + details: [ + { + name: 'Own Properties', + expand: true, + properties: [ + { + name: 'myProp', + value: { + inspect: 'func', + type: 'type-function' + }, + }, + ], + }, + { + name: 'prototype', + expand: true, + properties: [ + { + name: 'abc', + value: { + inspect: 'Teddy', + type: 'type-string' + }, + }, + ], + }, + ], + }); + + respondWith('objectInspector:gotoSource', ({ objectId, property }) => { + assert.strictEqual(objectId, 'object-id'); + assert.strictEqual(property, undefined); + return false; + }); + + // Grouped View + await click('[data-test-goto-source-btn]'); + + respondWith('objectInspector:gotoSource', ({ objectId, property }) => { + assert.strictEqual(objectId, 'object-id'); + assert.strictEqual(property, 'myProp'); + return false; + }); + + await click('[data-test-goto-source-btn]'); + + // All View + await click('[data-test-object-display-type-all]'); + + respondWith('objectInspector:gotoSource', ({ objectId, property }) => { + assert.strictEqual(objectId, 'object-id'); + assert.strictEqual(property, 'myProp'); + return false; + }); + + await click('[data-test-goto-source-btn]'); + }); + test('Read only CPs cannot be edited', async function (assert) { await visit('/'); From 8b9388ba0658f63a743c9811d35f7481a9d5eb26 Mon Sep 17 00:00:00 2001 From: Patrick Pircher Date: Mon, 11 Mar 2024 23:04:01 +0100 Subject: [PATCH 3/5] more specific test-data --- app/components/object-inspector.hbs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/components/object-inspector.hbs b/app/components/object-inspector.hbs index 2c62bd0f45..ffe099160a 100644 --- a/app/components/object-inspector.hbs +++ b/app/components/object-inspector.hbs @@ -28,7 +28,7 @@ {{#if this.isClass}}