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 @@
+