remove dead code, fix linting errors, get infinite loop functionality working

This commit is contained in:
Cassie Tarakajian 2016-10-06 15:45:26 -04:00
parent f9d487ebe8
commit 58d0dba167
7 changed files with 31 additions and 127 deletions

View file

@ -183,9 +183,10 @@ export function setUnsavedChanges(value) {
}; };
} }
export function detectInfiniteLoops() { export function detectInfiniteLoops(message) {
return { return {
type: ActionTypes.DETECT_INFINITE_LOOPS type: ActionTypes.DETECT_INFINITE_LOOPS,
message
}; };
} }

View file

@ -20,30 +20,23 @@ class Console extends React.Component {
* @type {Array} * @type {Array}
*/ */
this.children = []; this.children = [];
this.appendConsoleEvent = this.appendConsoleEvent.bind(this);
} }
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
if (nextProps.isPlaying && !this.props.isPlaying) { if (nextProps.isPlaying && !this.props.isPlaying) {
this.children = []; this.children = [];
} else if (nextProps.consoleEvent !== this.props.consoleEvent) { } else if (nextProps.consoleEvent !== this.props.consoleEvent && this.props.isPlaying) {
const args = nextProps.consoleEvent.arguments; const args = nextProps.consoleEvent.arguments;
Object.keys(args).forEach((key) => { Object.keys(args).forEach((key) => {
if (args[key].includes('Exiting potential infinite loop')) { if (args[key].includes('Exiting potential infinite loop')) {
// stop sketch
// dispatch infinite loop found
this.props.stopSketch(); this.props.stopSketch();
this.props.detectInfiniteLoops(); this.props.expandConsole();
this.appendConsoleEvent(nextProps.consoleEvent);
} }
}); });
if (nextProps.isExpanded) { if (nextProps.isExpanded) {
// const args = nextProps.consoleEvent.arguments; this.appendConsoleEvent(nextProps.consoleEvent);
const method = nextProps.consoleEvent.method;
const nextChild = (
<div key={this.children.length} className={`preview-console__${method}`}>
{Object.keys(args).map((key) => <span key={`${this.children.length}-${key}`}>{args[key]}</span>)}
</div>
);
this.children.push(nextChild);
} }
} }
} }
@ -58,6 +51,17 @@ class Console extends React.Component {
this.refs.console_messages.scrollTop = this.refs.console_messages.scrollHeight; this.refs.console_messages.scrollTop = this.refs.console_messages.scrollHeight;
} }
appendConsoleEvent(consoleEvent) {
const args = consoleEvent.arguments;
const method = consoleEvent.method;
const nextChild = (
<div key={this.children.length} className={`preview-console__${method}`}>
{Object.keys(args).map((key) => <span key={`${this.children.length}-${key}`}>{args[key]}</span>)}
</div>
);
this.children.push(nextChild);
}
render() { render() {
const childrenToDisplay = this.children.slice(-consoleMax); const childrenToDisplay = this.children.slice(-consoleMax);
const consoleClass = classNames({ const consoleClass = classNames({
@ -92,7 +96,6 @@ Console.propTypes = {
collapseConsole: PropTypes.func.isRequired, collapseConsole: PropTypes.func.isRequired,
expandConsole: PropTypes.func.isRequired, expandConsole: PropTypes.func.isRequired,
stopSketch: PropTypes.func.isRequired, stopSketch: PropTypes.func.isRequired,
detectInfiniteLoops: PropTypes.func.isRequired
}; };
export default Console; export default Console;

View file

@ -28,7 +28,6 @@ const downArrowUrl = require('../../../images/down-arrow.svg');
import classNames from 'classnames'; import classNames from 'classnames';
import { debounce } from 'throttle-debounce'; import { debounce } from 'throttle-debounce';
import loopProtect from 'loop-protect';
class Editor extends React.Component { class Editor extends React.Component {
constructor(props) { constructor(props) {
@ -66,17 +65,12 @@ class Editor extends React.Component {
} }
}); });
this._cm.on('change', debounce(1000, () => { this._cm.on('change', debounce(200, () => {
this.props.setUnsavedChanges(true); this.props.setUnsavedChanges(true);
this.props.updateFileContent(this.props.file.name, this._cm.getValue()); this.props.updateFileContent(this.props.file.name, this._cm.getValue());
if (this.props.autorefresh && this.props.isPlaying) { if (this.props.autorefresh && this.props.isPlaying) {
this.props.startRefreshSketch(); this.props.startRefreshSketch();
} }
// this.checkForInfiniteLoop((infiniteLoop, prevs) => {
// if (!infiniteLoop && prevs && this.props.autorefresh) {
// this.props.startRefreshSketch();
// }
// });
})); }));
this._cm.on('keyup', () => { this._cm.on('keyup', () => {
@ -123,14 +117,6 @@ class Editor extends React.Component {
if (this.props.theme !== prevProps.theme) { if (this.props.theme !== prevProps.theme) {
this._cm.setOption('theme', `p5-${this.props.theme}`); this._cm.setOption('theme', `p5-${this.props.theme}`);
} }
if (this.props.infiniteLoop && this.props.infiniteLoop !== prevProps.infiniteLoop) {
const msg = document.createElement('div');
const loopError = 'Loop is taking too long to run. This might be an infinite loop.';
msg.appendChild(document.createTextNode(loopError));
msg.className = 'lint-error';
this.widgets.push(this._cm.addLineWidget(1, msg, { coverGutter: false, noHScroll: true }));
}
} }
componentWillUnmount() { componentWillUnmount() {
@ -153,77 +139,6 @@ class Editor extends React.Component {
} }
} }
checkForInfiniteLoop(callback) {
const prevIsplaying = this.props.isPlaying;
let infiniteLoop = false;
let prevLine;
this.props.resetInfiniteLoops();
let iframe;
for (let i = 0; i < this.widgets.length; ++i) {
this._cm.removeLineWidget(this.widgets[i]);
}
this.widgets.length = 0;
loopProtect.alias = 'protect';
let foundInfiniteLoop = false;
loopProtect.hit = (line) => {
foundInfiniteLoop = true;
if (line !== prevLine) {
this.props.detectInfiniteLoops();
this.props.stopSketch();
infiniteLoop = true;
callback(infiniteLoop, prevIsplaying);
const msg = document.createElement('div');
const loopError = `line ${line}: This loop is taking too long to run. This might be an infinite loop.`;
msg.appendChild(document.createTextNode(loopError));
msg.className = 'lint-error';
this.widgets.push(this._cm.addLineWidget(line - 1, msg, { coverGutter: false, noHScroll: true }));
prevLine = line;
}
};
const processed = loopProtect(this.props.file.content);
let iframeForLoop = document.getElementById('iframeForLoop');
if (iframeForLoop === null) {
iframe = document.createElement('iframe');
iframe.id = 'iframeForLoop';
iframe.style.display = 'none';
document.body.appendChild(iframe);
iframeForLoop = iframe;
} else {
iframeForLoop.srcdoc = '';
}
const win = iframeForLoop.contentWindow;
const doc = win.document;
doc.open();
win.protect = loopProtect;
doc.write(`<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.2/p5.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.2/addons/p5.dom.min.js"></script>
</head>
<body>
<script>
${processed}
</script>
</body>
</html>`);
win.onerror = () => true;
doc.close();
setTimeout(() => {
if (!foundInfiniteLoop) {
callback(infiniteLoop, prevIsplaying, prevLine);
}
}, 200);
}
_cm: CodeMirror.Editor _cm: CodeMirror.Editor
render() { render() {
@ -289,14 +204,10 @@ Editor.propTypes = {
closeEditorOptions: PropTypes.func.isRequired, closeEditorOptions: PropTypes.func.isRequired,
showKeyboardShortcutModal: PropTypes.func.isRequired, showKeyboardShortcutModal: PropTypes.func.isRequired,
setUnsavedChanges: PropTypes.func.isRequired, setUnsavedChanges: PropTypes.func.isRequired,
infiniteLoop: PropTypes.bool.isRequired,
detectInfiniteLoops: PropTypes.func.isRequired,
resetInfiniteLoops: PropTypes.func.isRequired,
startRefreshSketch: PropTypes.func.isRequired, startRefreshSketch: PropTypes.func.isRequired,
autorefresh: PropTypes.bool.isRequired, autorefresh: PropTypes.bool.isRequired,
isPlaying: PropTypes.bool.isRequired, isPlaying: PropTypes.bool.isRequired,
theme: PropTypes.string.isRequired, theme: PropTypes.string.isRequired,
stopSketch: PropTypes.func.isRequired
}; };
export default Editor; export default Editor;

View file

@ -217,20 +217,16 @@ class PreviewFrame extends React.Component {
let htmlHeadContents = htmlHead[0].split(headRegex)[1]; let htmlHeadContents = htmlHead[0].split(headRegex)[1];
htmlHeadContents = htmlHeadContents.slice(1, htmlHeadContents.length - 2); htmlHeadContents = htmlHeadContents.slice(1, htmlHeadContents.length - 2);
htmlHeadContents += '<script type="text/javascript" src="/loop-protect.min.js"></script>\n'; htmlHeadContents += '<script type="text/javascript" src="/loop-protect.min.js"></script>\n';
htmlFile = htmlFile.replace(/(?:<head.*?>)([\s\S]*?)(?:<\/head>)/gmi, `<head>\n${htmlHeadContents}\n</head>`);
if (this.props.textOutput || this.props.isTextOutputPlaying) { if (this.props.textOutput || this.props.isTextOutputPlaying) {
const htmlHead = htmlFile.match(/(?:<head.*?>)([\s\S]*?)(?:<\/head>)/gmi);
const headRegex = new RegExp('head', 'i');
let htmlHeadContents = htmlHead[0].split(headRegex)[1];
htmlHeadContents = htmlHeadContents.slice(1, htmlHeadContents.length - 2);
htmlHeadContents += '<script src="/loadData.js"></script>\n'; htmlHeadContents += '<script src="/loadData.js"></script>\n';
htmlHeadContents += '<script src="/interceptor-functions.js"></script>\n'; htmlHeadContents += '<script src="/interceptor-functions.js"></script>\n';
htmlHeadContents += '<script src="/intercept-p5.js"></script>\n'; htmlHeadContents += '<script src="/intercept-p5.js"></script>\n';
htmlHeadContents += '<script type="text/javascript" src="/ntc.min.js"></script>'; htmlHeadContents += '<script type="text/javascript" src="/ntc.min.js"></script>';
htmlFile = htmlFile.replace(/(?:<head.*?>)([\s\S]*?)(?:<\/head>)/gmi, `<head>\n${htmlHeadContents}\n</head>`);
} }
htmlFile = htmlFile.replace(/(?:<head.*?>)([\s\S]*?)(?:<\/head>)/gmi, `<head>\n${htmlHeadContents}\n</head>`);
scriptOffs = getAllScriptOffsets(htmlFile); scriptOffs = getAllScriptOffsets(htmlFile);
htmlFile += hijackConsoleErrorsScript(JSON.stringify(scriptOffs)); htmlFile += hijackConsoleErrorsScript(JSON.stringify(scriptOffs));
@ -238,9 +234,8 @@ class PreviewFrame extends React.Component {
} }
renderSketch() { renderSketch() {
this.props.resetInfiniteLoops();
const doc = ReactDOM.findDOMNode(this); const doc = ReactDOM.findDOMNode(this);
if (this.props.isPlaying && !this.props.infiniteLoop) { if (this.props.isPlaying) {
srcDoc.set(doc, this.injectLocalFiles()); srcDoc.set(doc, this.injectLocalFiles());
this.props.endSketchRefresh(); this.props.endSketchRefresh();
} else { } else {
@ -288,8 +283,6 @@ PreviewFrame.propTypes = {
files: PropTypes.array.isRequired, files: PropTypes.array.isRequired,
dispatchConsoleEvent: PropTypes.func, dispatchConsoleEvent: PropTypes.func,
children: PropTypes.element, children: PropTypes.element,
infiniteLoop: PropTypes.bool.isRequired,
resetInfiniteLoops: PropTypes.func.isRequired,
autorefresh: PropTypes.bool.isRequired, autorefresh: PropTypes.bool.isRequired,
endSketchRefresh: PropTypes.func.isRequired, endSketchRefresh: PropTypes.func.isRequired,
previewIsRefreshing: PropTypes.bool.isRequired, previewIsRefreshing: PropTypes.bool.isRequired,

View file

@ -72,7 +72,7 @@ class Toolbar extends React.Component {
> >
<InlineSVG src={stopUrl} alt="Stop Sketch" /> <InlineSVG src={stopUrl} alt="Stop Sketch" />
</button> </button>
{/* <div className="toolbar__autorefresh"> <div className="toolbar__autorefresh">
<input <input
id="autorefresh" id="autorefresh"
type="checkbox" type="checkbox"
@ -84,7 +84,7 @@ class Toolbar extends React.Component {
<label htmlFor="autorefresh" className="toolbar__autorefresh-label"> <label htmlFor="autorefresh" className="toolbar__autorefresh-label">
Auto-refresh Auto-refresh
</label> </label>
</div> */} </div>
<div className={nameContainerClass}> <div className={nameContainerClass}>
<a <a
className="toolbar__project-name" className="toolbar__project-name"

View file

@ -277,9 +277,6 @@ class IDEView extends React.Component {
closeEditorOptions={this.props.closeEditorOptions} closeEditorOptions={this.props.closeEditorOptions}
showKeyboardShortcutModal={this.props.showKeyboardShortcutModal} showKeyboardShortcutModal={this.props.showKeyboardShortcutModal}
setUnsavedChanges={this.props.setUnsavedChanges} setUnsavedChanges={this.props.setUnsavedChanges}
infiniteLoop={this.props.ide.infiniteLoop}
detectInfiniteLoops={this.props.detectInfiniteLoops}
resetInfiniteLoops={this.props.resetInfiniteLoops}
isPlaying={this.props.ide.isPlaying} isPlaying={this.props.ide.isPlaying}
theme={this.props.preferences.theme} theme={this.props.preferences.theme}
startRefreshSketch={this.props.startRefreshSketch} startRefreshSketch={this.props.startRefreshSketch}
@ -293,7 +290,6 @@ class IDEView extends React.Component {
expandConsole={this.props.expandConsole} expandConsole={this.props.expandConsole}
collapseConsole={this.props.collapseConsole} collapseConsole={this.props.collapseConsole}
stopSketch={this.props.stopSketch} stopSketch={this.props.stopSketch}
detectInfiniteLoops={this.props.detectInfiniteLoops}
/> />
</SplitPane> </SplitPane>
<div> <div>
@ -322,8 +318,6 @@ class IDEView extends React.Component {
isTextOutputPlaying={this.props.ide.isTextOutputPlaying} isTextOutputPlaying={this.props.ide.isTextOutputPlaying}
textOutput={this.props.preferences.textOutput} textOutput={this.props.preferences.textOutput}
dispatchConsoleEvent={this.props.dispatchConsoleEvent} dispatchConsoleEvent={this.props.dispatchConsoleEvent}
infiniteLoop={this.props.ide.infiniteLoop}
resetInfiniteLoops={this.props.resetInfiniteLoops}
autorefresh={this.props.preferences.autorefresh} autorefresh={this.props.preferences.autorefresh}
previewIsRefreshing={this.props.ide.previewIsRefreshing} previewIsRefreshing={this.props.ide.previewIsRefreshing}
endSketchRefresh={this.props.endSketchRefresh} endSketchRefresh={this.props.endSketchRefresh}
@ -433,7 +427,8 @@ IDEView.propTypes = {
keyboardShortcutVisible: PropTypes.bool.isRequired, keyboardShortcutVisible: PropTypes.bool.isRequired,
unsavedChanges: PropTypes.bool.isRequired, unsavedChanges: PropTypes.bool.isRequired,
infiniteLoop: PropTypes.bool.isRequired, infiniteLoop: PropTypes.bool.isRequired,
previewIsRefreshing: PropTypes.bool.isRequired previewIsRefreshing: PropTypes.bool.isRequired,
infiniteLoopMessage: PropTypes.string.isRequired
}).isRequired, }).isRequired,
startSketch: PropTypes.func.isRequired, startSketch: PropTypes.func.isRequired,
stopSketch: PropTypes.func.isRequired, stopSketch: PropTypes.func.isRequired,

View file

@ -18,7 +18,8 @@ const initialState = {
keyboardShortcutVisible: false, keyboardShortcutVisible: false,
unsavedChanges: false, unsavedChanges: false,
infiniteLoop: false, infiniteLoop: false,
previewIsRefreshing: false previewIsRefreshing: false,
infiniteLoopMessage: ''
}; };
const ide = (state = initialState, action) => { const ide = (state = initialState, action) => {
@ -74,9 +75,9 @@ const ide = (state = initialState, action) => {
case ActionTypes.SET_UNSAVED_CHANGES: case ActionTypes.SET_UNSAVED_CHANGES:
return Object.assign({}, state, { unsavedChanges: action.value }); return Object.assign({}, state, { unsavedChanges: action.value });
case ActionTypes.DETECT_INFINITE_LOOPS: case ActionTypes.DETECT_INFINITE_LOOPS:
return Object.assign({}, state, { infiniteLoop: true }); return Object.assign({}, state, { infiniteLoop: true, infiniteLoopMessage: action.message });
case ActionTypes.RESET_INFINITE_LOOPS: case ActionTypes.RESET_INFINITE_LOOPS:
return Object.assign({}, state, { infiniteLoop: false }); return Object.assign({}, state, { infiniteLoop: false, infiniteLoopMessage: '' });
case ActionTypes.START_SKETCH_REFRESH: case ActionTypes.START_SKETCH_REFRESH:
return Object.assign({}, state, { previewIsRefreshing: true }); return Object.assign({}, state, { previewIsRefreshing: true });
case ActionTypes.END_SKETCH_REFRESH: case ActionTypes.END_SKETCH_REFRESH: