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/routes/whats-new.js b/app/routes/whats-new.js index f619023742..496b8d8083 100644 --- a/app/routes/whats-new.js +++ b/app/routes/whats-new.js @@ -26,6 +26,15 @@ function getLatestEntry(doc) { return ''; } +function patchLernaIcons(text) { + return text + .replace(':breaking:', '💥') + .replace(':rocket:', '🚀') + .replace(':bug:', '🐛') + .replace(':documentation:', '📝') + .replace(':house:', '🏠'); +} + export default class WhatsNewRoute extends TabRoute { @service config; @tracked error = false; @@ -41,7 +50,8 @@ export default class WhatsNewRoute extends TabRoute { return fetch(url) .then(checkStatus) .then((response) => response.text()) - .then((text) => getLatestEntry(text)) + .then(getLatestEntry) + .then(patchLernaIcons) .catch((error) => { this.error = error; }); diff --git a/app/services/adapters/web-extension.js b/app/services/adapters/web-extension.js index 2ba55dc801..1c9e6c490c 100644 --- a/app/services/adapters/web-extension.js +++ b/app/services/adapters/web-extension.js @@ -101,12 +101,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 4743581481..09612ed4d6 100644 --- a/ember_debug/adapters/basic.js +++ b/ember_debug/adapters/basic.js @@ -64,17 +64,17 @@ export default EmberObject.extend({ }, /** - 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 7c2606ead1..a264d5b1a3 100644 --- a/ember_debug/adapters/web-extension.js +++ b/ember_debug/adapters/web-extension.js @@ -39,9 +39,9 @@ export default BasicAdapter.extend({ /** * 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. @@ -63,9 +63,9 @@ export default BasicAdapter.extend({ let name = `__EMBER_INSPECTOR_${(Math.random() * 100000000).toFixed(0)}`; - window[name] = node; + window[name] = value; - this.get('namespace.port').send('view:inspectDOMNode', { name }); + this.get('namespace.port').send('view:inspectJSValue', { name }); }, _listen() { diff --git a/ember_debug/libs/view-inspection.js b/ember_debug/libs/view-inspection.js index 91f41a7342..418853560e 100644 --- a/ember_debug/libs/view-inspection.js +++ b/ember_debug/libs/view-inspection.js @@ -77,6 +77,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); @@ -502,6 +518,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 47502a7bad..0c08ea2696 100644 --- a/ember_debug/object-inspector.js +++ b/ember_debug/object-inspector.js @@ -475,6 +475,9 @@ export default DebugPort.extend({ sendToConsole(message) { this.sendToConsole(message.objectId, message.property); }, + gotoSource(message) { + this.gotoSource(message.objectId, message.property); + }, sendControllerToConsole(message) { const container = this.get('namespace.owner'); this.sendValueToConsole(container.lookup(`controller:${message.name}`)); @@ -550,6 +553,27 @@ export default DebugPort.extend({ join(() => set(object, prop, val)); }, + gotoSource(objectId, prop) { + let object = this.sentObjects[objectId]; + let value; + + if (isNone(prop)) { + value = this.sentObjects[objectId]; + } else { + value = calculateCP(object, { name: prop }, {}); + } + // for functions and classes we want to show the source instead + // as showing it in the console has no use + 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 a01ca77a06..045a6836aa 100644 --- a/ember_debug/view-debug.js +++ b/ember_debug/view-debug.js @@ -130,7 +130,7 @@ export default DebugPort.extend({ * @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/components/send-to-inspect.hbs b/lib/ui/addon/components/send-to-inspect.hbs new file mode 100644 index 0000000000..45945d52e6 --- /dev/null +++ b/lib/ui/addon/components/send-to-inspect.hbs @@ -0,0 +1,10 @@ + + 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/package.json b/package.json index 544838a398..7ebb57d8e7 100644 --- a/package.json +++ b/package.json @@ -110,7 +110,7 @@ "ember-sinon-qunit": "^6.0.0", "ember-source": "~4.12.0", "ember-source-channel-url": "^3.0.0", - "ember-svg-jar": "^2.3.4", + "ember-svg-jar": "^2.4.3", "ember-table": "^5.0.1", "ember-template-lint": "^5.2.0", "ember-test-selectors": "^6.0.0", diff --git a/public/assets/svg/code-source.svg b/public/assets/svg/code-source.svg new file mode 100644 index 0000000000..0af096df80 --- /dev/null +++ b/public/assets/svg/code-source.svg @@ -0,0 +1,6 @@ + + + + + { } +