Skip to content

Commit 85ba402

Browse files
remo5000ning-y
authored andcommitted
Add "Stop" button (#27)
* Add stop button * Add notification for interruption * Add isRunning state with switching 'Run' button When code is running (`isRunning == true`), Run button changes into a Stop button. The reverse is true. In addition, the soft block for async code execution, `put(actions.handleInterruptExecution())` was removed.
1 parent bac43d1 commit 85ba402

File tree

6 files changed

+52
-11
lines changed

6 files changed

+52
-11
lines changed

src/components/IDE/Control.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ import { Button, IconName, Intent } from '@blueprintjs/core'
77
* of the editor's content, using `slang`
88
*/
99
export interface IControlProps {
10+
isRunning: boolean
1011
handleEvalEditor: () => void
1112
handleEvalRepl: () => void
1213
handleClearReplOutput: () => void
14+
handleInterruptEval: () => void
1315
}
1416

1517
class Control extends React.Component<IControlProps, {}> {
@@ -30,12 +32,20 @@ class Control extends React.Component<IControlProps, {}> {
3032
{label}
3133
</Button>
3234
)
33-
const runButton = genericButton('Run', 'play', this.props.handleEvalEditor)
35+
const runButton = this.props.isRunning
36+
? null
37+
: genericButton('Run', 'play', this.props.handleEvalEditor)
38+
const stopButton = this.props.isRunning
39+
? genericButton('Stop', 'stop', this.props.handleInterruptEval)
40+
: null
3441
const evalButton = genericButton('Eval', 'play', this.props.handleEvalRepl)
3542
const clearButton = genericButton('Clear', 'remove', this.props.handleClearReplOutput)
3643
return (
3744
<div className="row between-xs">
38-
<div className="col-xs-2">{runButton}</div>
45+
<div className="col-xs-2">
46+
{runButton}
47+
{stopButton}
48+
</div>
3949
<div className="col-xs-4">
4050
<div className="row">
4151
<div className="col-xs-6">{evalButton}</div>

src/components/IDE/__tests__/Control.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ import Control, { IControlProps } from '../Control'
66

77
test('Control renders correctly', () => {
88
const props: IControlProps = {
9+
isRunning: false,
910
handleEvalEditor: () => {},
1011
handleEvalRepl: () => {},
11-
handleClearReplOutput: () => {}
12+
handleClearReplOutput: () => {},
13+
handleInterruptEval: () => {}
1214
}
1315
const app = <Control {...props} />
1416
const tree = shallow(app)

src/containers/IDE/ControlContainer.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
11
import { connect, MapDispatchToProps, MapStateToProps } from 'react-redux'
22
import { bindActionCreators, Dispatch } from 'redux'
33

4+
import { handleInterruptExecution } from '../../actions/interpreter'
45
import { clearReplOutput, evalEditor, evalRepl } from '../../actions/playground'
56
import Control, { IControlProps } from '../../components/IDE/Control'
67
import { IState } from '../../reducers/states'
78

9+
type StateProps = Pick<IControlProps, 'isRunning'>
10+
811
type DispatchProps = Pick<IControlProps, 'handleEvalEditor'> &
912
Pick<IControlProps, 'handleEvalRepl'> &
10-
Pick<IControlProps, 'handleClearReplOutput'>
13+
Pick<IControlProps, 'handleClearReplOutput'> &
14+
Pick<IControlProps, 'handleInterruptEval'>
1115

1216
/** No-op mapStateToProps */
13-
const mapStateToProps: MapStateToProps<{}, {}, IState> = state => ({})
17+
const mapStateToProps: MapStateToProps<StateProps, {}, IState> = state => ({
18+
isRunning: state.playground.isRunning
19+
})
1420

1521
/** Provides a callback function `handleEvalEditor`
1622
* to evaluate code in the Editor.
@@ -20,7 +26,8 @@ const mapDispatchToProps: MapDispatchToProps<DispatchProps, {}> = (dispatch: Dis
2026
{
2127
handleEvalEditor: evalEditor,
2228
handleEvalRepl: evalRepl,
23-
handleClearReplOutput: clearReplOutput
29+
handleClearReplOutput: clearReplOutput,
30+
handleInterruptEval: handleInterruptExecution
2431
},
2532
dispatch
2633
)

src/reducers/playground.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@ import {
33
CLEAR_CONTEXT,
44
CLEAR_REPL_INPUT,
55
CLEAR_REPL_OUTPUT,
6+
EVAL_EDITOR,
7+
EVAL_INTERPRETER,
68
EVAL_INTERPRETER_ERROR,
79
EVAL_INTERPRETER_SUCCESS,
810
HANDLE_CONSOLE_LOG,
911
IAction,
12+
INTERRUPT_EXECUTION,
1013
SEND_REPL_INPUT_TO_OUTPUT,
1114
UPDATE_EDITOR_VALUE,
1215
UPDATE_REPL_VALUE
@@ -68,6 +71,16 @@ export const reducer: Reducer<IPlaygroundState> = (state = defaultPlayground, ac
6871
...state,
6972
output: newOutput
7073
}
74+
case EVAL_EDITOR:
75+
return {
76+
...state,
77+
isRunning: true
78+
}
79+
case EVAL_INTERPRETER:
80+
return {
81+
...state,
82+
isRunning: true
83+
}
7184
case EVAL_INTERPRETER_SUCCESS:
7285
lastOutput = state.output.slice(-1)[0]
7386
if (lastOutput !== undefined && lastOutput.type === 'running') {
@@ -83,7 +96,8 @@ export const reducer: Reducer<IPlaygroundState> = (state = defaultPlayground, ac
8396
}
8497
return {
8598
...state,
86-
output: newOutput
99+
output: newOutput,
100+
isRunning: false
87101
}
88102
case EVAL_INTERPRETER_ERROR:
89103
lastOutput = state.output.slice(-1)[0]
@@ -100,7 +114,13 @@ export const reducer: Reducer<IPlaygroundState> = (state = defaultPlayground, ac
100114
}
101115
return {
102116
...state,
103-
output: newOutput
117+
output: newOutput,
118+
isRunning: false
119+
}
120+
case INTERRUPT_EXECUTION:
121+
return {
122+
...state,
123+
isRunning: false
104124
}
105125
default:
106126
return state

src/reducers/states.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export interface IPlaygroundState {
1616
readonly replValue: string
1717
readonly context: Context
1818
readonly output: InterpreterOutput[]
19+
readonly isRunning: boolean
1920
}
2021

2122
/**
@@ -90,5 +91,6 @@ export const defaultPlayground: IPlaygroundState = {
9091
editorValue: '',
9192
replValue: '',
9293
context: createContext(),
93-
output: []
94+
output: [],
95+
isRunning: false
9496
}

src/sagas/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { Context, interrupt, runInContext } from '../slang'
66

77
import * as actions from '../actions'
88
import * as actionTypes from '../actions/actionTypes'
9+
import { showWarningMessage } from '../notification'
910

1011
function* evalCode(code: string, context: Context) {
1112
const { result, interrupted } = yield race({
@@ -20,6 +21,7 @@ function* evalCode(code: string, context: Context) {
2021
}
2122
} else if (interrupted) {
2223
interrupt(context)
24+
yield call(showWarningMessage, 'Execution aborted by user')
2325
}
2426
}
2527

@@ -29,7 +31,6 @@ function* interpreterSaga(): SagaIterator {
2931

3032
yield takeEvery(actionTypes.EVAL_EDITOR, function*() {
3133
const code: string = yield select((state: IState) => state.playground.editorValue)
32-
yield put(actions.handleInterruptExecution())
3334
yield put(actions.clearContext())
3435
yield put(actions.clearReplOutput())
3536
context = yield select((state: IState) => state.playground.context)
@@ -39,7 +40,6 @@ function* interpreterSaga(): SagaIterator {
3940
yield takeEvery(actionTypes.EVAL_REPL, function*() {
4041
const code: string = yield select((state: IState) => state.playground.replValue)
4142
context = yield select((state: IState) => state.playground.context)
42-
yield put(actions.handleInterruptExecution())
4343
yield put(actions.clearReplInput())
4444
yield put(actions.sendReplInputToOutput(code))
4545
yield* evalCode(code, context)

0 commit comments

Comments
 (0)