зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1294499
- New console frontend: Add collapsible stacktrace for console.error/trace messages. r=linclark
MozReview-Commit-ID: LctpJdFtxX0
This commit is contained in:
Родитель
6a66311928
Коммит
d8d0dd3e36
|
@ -13,7 +13,9 @@ const { IdGenerator } = require("devtools/client/webconsole/new-console-output/u
|
|||
|
||||
const {
|
||||
MESSAGE_ADD,
|
||||
MESSAGES_CLEAR
|
||||
MESSAGES_CLEAR,
|
||||
MESSAGE_OPEN,
|
||||
MESSAGE_CLOSE,
|
||||
} = require("../constants");
|
||||
|
||||
const defaultIdGenerator = new IdGenerator();
|
||||
|
@ -36,5 +38,21 @@ function messagesClear() {
|
|||
};
|
||||
}
|
||||
|
||||
function messageOpen(id) {
|
||||
return {
|
||||
type: MESSAGE_OPEN,
|
||||
id
|
||||
};
|
||||
}
|
||||
|
||||
function messageClose(id) {
|
||||
return {
|
||||
type: MESSAGE_CLOSE,
|
||||
id
|
||||
};
|
||||
}
|
||||
|
||||
exports.messageAdd = messageAdd;
|
||||
exports.messagesClear = messagesClear;
|
||||
exports.messageOpen = messageOpen;
|
||||
exports.messageClose = messageClose;
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
// React & Redux
|
||||
const {
|
||||
createClass,
|
||||
DOM: dom,
|
||||
PropTypes,
|
||||
} = require("devtools/client/shared/vendor/react");
|
||||
|
||||
const CollapseButton = createClass({
|
||||
|
||||
displayName: "CollapseButton",
|
||||
|
||||
propTypes: {
|
||||
open: PropTypes.bool.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
},
|
||||
|
||||
render: function () {
|
||||
const { title, open, onClick } = this.props;
|
||||
|
||||
let classes = ["theme-twisty"];
|
||||
|
||||
if (open) {
|
||||
classes.push("open");
|
||||
}
|
||||
|
||||
return dom.a({
|
||||
className: classes.join(" "),
|
||||
onClick: onClick,
|
||||
title
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
module.exports.CollapseButton = CollapseButton;
|
|
@ -12,7 +12,7 @@ const {
|
|||
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
|
||||
const { connect } = require("devtools/client/shared/vendor/react-redux");
|
||||
|
||||
const { getAllMessages } = require("devtools/client/webconsole/new-console-output/selectors/messages");
|
||||
const { getAllMessages, getAllMessagesUiById } = require("devtools/client/webconsole/new-console-output/selectors/messages");
|
||||
const MessageContainer = createFactory(require("devtools/client/webconsole/new-console-output/components/message-container").MessageContainer);
|
||||
|
||||
const ConsoleOutput = createClass({
|
||||
|
@ -41,11 +41,24 @@ const ConsoleOutput = createClass({
|
|||
},
|
||||
|
||||
render() {
|
||||
let {messages, sourceMapService, onViewSourceInDebugger} = this.props;
|
||||
let {
|
||||
dispatch,
|
||||
messages,
|
||||
messagesUi,
|
||||
sourceMapService,
|
||||
onViewSourceInDebugger
|
||||
} = this.props;
|
||||
|
||||
let messageNodes = messages.map(function (message) {
|
||||
return (
|
||||
MessageContainer({ message, key: message.id,
|
||||
sourceMapService, onViewSourceInDebugger })
|
||||
MessageContainer({
|
||||
dispatch,
|
||||
message,
|
||||
key: message.id,
|
||||
sourceMapService,
|
||||
onViewSourceInDebugger,
|
||||
open: messagesUi.includes(message.id)
|
||||
})
|
||||
);
|
||||
});
|
||||
return (
|
||||
|
@ -63,7 +76,8 @@ function isScrolledToBottom(outputNode, scrollNode) {
|
|||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
messages: getAllMessages(state)
|
||||
messages: getAllMessages(state),
|
||||
messagesUi: getAllMessagesUiById(state)
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -33,16 +33,31 @@ const MessageContainer = createClass({
|
|||
message: PropTypes.object.isRequired,
|
||||
sourceMapService: PropTypes.object,
|
||||
onViewSourceInDebugger: PropTypes.func.isRequired,
|
||||
open: PropTypes.bool.isRequired,
|
||||
},
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
return this.props.message.repeat !== nextProps.message.repeat;
|
||||
return this.props.message.repeat !== nextProps.message.repeat
|
||||
|| this.props.open !== nextProps.open;
|
||||
},
|
||||
|
||||
render() {
|
||||
const { message, sourceMapService, onViewSourceInDebugger } = this.props;
|
||||
const {
|
||||
dispatch,
|
||||
message,
|
||||
sourceMapService,
|
||||
onViewSourceInDebugger,
|
||||
open
|
||||
} = this.props;
|
||||
|
||||
let MessageComponent = createFactory(getMessageComponent(message));
|
||||
return MessageComponent({ message, sourceMapService, onViewSourceInDebugger });
|
||||
return MessageComponent({
|
||||
dispatch,
|
||||
message,
|
||||
sourceMapService,
|
||||
onViewSourceInDebugger,
|
||||
open
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -17,6 +17,9 @@ const StackTrace = createFactory(require("devtools/client/shared/components/stac
|
|||
const GripMessageBody = createFactory(require("devtools/client/webconsole/new-console-output/components/grip-message-body").GripMessageBody);
|
||||
const MessageRepeat = createFactory(require("devtools/client/webconsole/new-console-output/components/message-repeat").MessageRepeat);
|
||||
const MessageIcon = createFactory(require("devtools/client/webconsole/new-console-output/components/message-icon").MessageIcon);
|
||||
const CollapseButton = createFactory(require("devtools/client/webconsole/new-console-output/components/collapse-button").CollapseButton);
|
||||
const {l10n} = require("devtools/client/webconsole/new-console-output/utils/messages");
|
||||
const actions = require("devtools/client/webconsole/new-console-output/actions/messages");
|
||||
|
||||
ConsoleApiCall.displayName = "ConsoleApiCall";
|
||||
|
||||
|
@ -24,11 +27,13 @@ ConsoleApiCall.propTypes = {
|
|||
message: PropTypes.object.isRequired,
|
||||
sourceMapService: PropTypes.object,
|
||||
onViewSourceInDebugger: PropTypes.func.isRequired,
|
||||
open: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
function ConsoleApiCall(props) {
|
||||
const { message, sourceMapService, onViewSourceInDebugger } = props;
|
||||
const { source, level, stacktrace, type, frame } = message;
|
||||
const { dispatch, message, sourceMapService, onViewSourceInDebugger, open } = props;
|
||||
const {source, level, stacktrace, type, frame } = message;
|
||||
|
||||
let messageBody;
|
||||
if (type === "trace") {
|
||||
messageBody = dom.span({className: "cm-variable"}, "console.trace()");
|
||||
|
@ -41,6 +46,7 @@ function ConsoleApiCall(props) {
|
|||
const icon = MessageIcon({level});
|
||||
const repeat = MessageRepeat({repeat: message.repeat});
|
||||
|
||||
let collapse = "";
|
||||
let attachment = "";
|
||||
if (stacktrace) {
|
||||
attachment = dom.div({className: "stacktrace devtools-monospace"},
|
||||
|
@ -49,6 +55,18 @@ function ConsoleApiCall(props) {
|
|||
onViewSourceInDebugger: onViewSourceInDebugger
|
||||
})
|
||||
);
|
||||
|
||||
collapse = CollapseButton({
|
||||
open: open,
|
||||
title: l10n.getStr("messageToggleDetails"),
|
||||
onClick: function () {
|
||||
if (open) {
|
||||
dispatch(actions.messageClose(message.id));
|
||||
} else {
|
||||
dispatch(actions.messageOpen(message.id));
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const classes = ["message", "cm-s-mozilla"];
|
||||
|
@ -61,7 +79,7 @@ function ConsoleApiCall(props) {
|
|||
classes.push(level);
|
||||
}
|
||||
|
||||
if (type === "trace") {
|
||||
if (open === true) {
|
||||
classes.push("open");
|
||||
}
|
||||
|
||||
|
@ -72,6 +90,7 @@ function ConsoleApiCall(props) {
|
|||
// @TODO add timestamp
|
||||
// @TODO add indent if necessary
|
||||
icon,
|
||||
collapse,
|
||||
dom.span({className: "message-body-wrapper"},
|
||||
dom.span({},
|
||||
dom.span({className: "message-flex-body"},
|
||||
|
|
|
@ -8,6 +8,7 @@ DIRS += [
|
|||
]
|
||||
|
||||
DevToolsModules(
|
||||
'collapse-button.js',
|
||||
'console-output.js',
|
||||
'filter-bar.js',
|
||||
'filter-button.js',
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
const actionTypes = {
|
||||
MESSAGE_ADD: "MESSAGE_ADD",
|
||||
MESSAGES_CLEAR: "MESSAGES_CLEAR",
|
||||
MESSAGE_OPEN: "MESSAGE_OPEN",
|
||||
MESSAGE_CLOSE: "MESSAGE_CLOSE",
|
||||
FILTER_TOGGLE: "FILTER_TOGGLE",
|
||||
FILTER_TEXT_SET: "FILTER_TEXT_SET",
|
||||
FILTERS_CLEAR: "FILTERS_CLEAR",
|
||||
|
|
|
@ -8,26 +8,47 @@
|
|||
const Immutable = require("devtools/client/shared/vendor/immutable");
|
||||
const constants = require("devtools/client/webconsole/new-console-output/constants");
|
||||
|
||||
function messages(state = Immutable.List(), action) {
|
||||
const MessageState = Immutable.Record({
|
||||
messagesById: Immutable.List(),
|
||||
messagesUiById: Immutable.List(),
|
||||
});
|
||||
|
||||
function messages(state = new MessageState(), action) {
|
||||
const messagesById = state.messagesById;
|
||||
const messagesUiById = state.messagesUiById;
|
||||
|
||||
switch (action.type) {
|
||||
case constants.MESSAGE_ADD:
|
||||
let newMessage = action.message;
|
||||
|
||||
if (newMessage.type === "clear") {
|
||||
return Immutable.List([newMessage]);
|
||||
return state.set("messagesById", Immutable.List([newMessage]));
|
||||
}
|
||||
|
||||
if (newMessage.allowRepeating && state.size > 0) {
|
||||
let lastMessage = state.last();
|
||||
if (newMessage.allowRepeating && messagesById.size > 0) {
|
||||
let lastMessage = messagesById.last();
|
||||
if (lastMessage.repeatId === newMessage.repeatId) {
|
||||
return state.pop().push(
|
||||
newMessage.set("repeat", lastMessage.repeat + 1)
|
||||
);
|
||||
return state.withMutations(function (record) {
|
||||
record.set("messagesById", messagesById.pop().push(
|
||||
newMessage.set("repeat", lastMessage.repeat + 1)
|
||||
));
|
||||
});
|
||||
}
|
||||
}
|
||||
return state.push(newMessage);
|
||||
|
||||
return state.withMutations(function (record) {
|
||||
record.set("messagesById", messagesById.push(newMessage));
|
||||
if (newMessage.type === "trace") {
|
||||
record.set("messagesUiById", messagesUiById.push(newMessage.id));
|
||||
}
|
||||
});
|
||||
case constants.MESSAGES_CLEAR:
|
||||
return Immutable.List();
|
||||
return state.set("messagesById", Immutable.List());
|
||||
case constants.MESSAGE_OPEN:
|
||||
return state.set("messagesUiById", messagesUiById.push(action.id));
|
||||
case constants.MESSAGE_CLOSE:
|
||||
let index = state.messagesUiById.indexOf(action.id);
|
||||
return state.deleteIn(["messagesUiById", index]);
|
||||
}
|
||||
|
||||
return state;
|
||||
|
|
|
@ -12,7 +12,7 @@ const {
|
|||
} = require("devtools/client/webconsole/new-console-output/constants");
|
||||
|
||||
function getAllMessages(state) {
|
||||
let messages = state.messages;
|
||||
let messages = state.messages.messagesById;
|
||||
let logLimit = getLogLimit(state);
|
||||
let filters = getAllFilters(state);
|
||||
|
||||
|
@ -25,6 +25,10 @@ function getAllMessages(state) {
|
|||
);
|
||||
}
|
||||
|
||||
function getAllMessagesUiById(state) {
|
||||
return state.messages.messagesUiById;
|
||||
}
|
||||
|
||||
function filterLevel(messages, filters) {
|
||||
return messages.filter((message) => {
|
||||
return filters[message.level] === true
|
||||
|
@ -59,3 +63,4 @@ function prune(messages, logLimit) {
|
|||
}
|
||||
|
||||
exports.getAllMessages = getAllMessages;
|
||||
exports.getAllMessagesUiById = getAllMessagesUiById;
|
||||
|
|
Загрузка…
Ссылка в новой задаче