UNPKG

feeles-ide

Version:

The hackable and serializable IDE to make learning material

201 lines (185 loc) 6.9 kB
import _extends from 'babel-runtime/helpers/extends'; import _slicedToArray from 'babel-runtime/helpers/slicedToArray'; import _Object$entries from 'babel-runtime/core-js/object/entries'; import _getIterator from 'babel-runtime/core-js/get-iterator'; import _Map from 'babel-runtime/core-js/map'; import _Object$getPrototypeOf from 'babel-runtime/core-js/object/get-prototype-of'; import _classCallCheck from 'babel-runtime/helpers/classCallCheck'; import _createClass from 'babel-runtime/helpers/createClass'; import _possibleConstructorReturn from 'babel-runtime/helpers/possibleConstructorReturn'; import _inherits from 'babel-runtime/helpers/inherits'; import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import CodeMirror from 'codemirror'; import deepEqual from 'deep-equal'; import includes from 'lodash/includes'; var CodeMirrorComponent = function (_PureComponent) { _inherits(CodeMirrorComponent, _PureComponent); function CodeMirrorComponent() { var _ref; var _temp, _this, _ret; _classCallCheck(this, CodeMirrorComponent); for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = CodeMirrorComponent.__proto__ || _Object$getPrototypeOf(CodeMirrorComponent)).call.apply(_ref, [this].concat(args))), _this), _this.state = { // File ごとに存在する CodeMirror.Doc インスタンスのキャッシュ docs: new _Map() }, _temp), _possibleConstructorReturn(_this, _ret); } _createClass(CodeMirrorComponent, [{ key: 'componentDidMount', value: function componentDidMount() { // initialize CodeMirror this.codeMirror = CodeMirror.fromTextArea(this.ref, this.props); var id = this.props.id; var doc = this.codeMirror.getDoc(); doc.setValue(this.props.value); // set default value doc.clearHistory(); this.state.docs.set(id, doc); this.props.onDocChanged({ id: id, doc: doc }, null); } }, { key: 'componentWillUnmount', value: function componentWillUnmount() { var id = this.props.id; var doc = this.state.docs.get(id); if (doc) { this.props.onDocChanged(null, { id: id, doc: doc }); } this.state.docs.clear(); this.codeMirror.toTextArea(); this.codeMirror = null; // GC?? } }, { key: 'componentWillReceiveProps', value: function componentWillReceiveProps(nextProps) { // タブ, value の更新 if (this.props.id !== nextProps.id) { // 前回のタブ var prev = this.state.docs.get(this.props.id) || null; // 次のタブ (or undefined) var doc = this.state.docs.get(nextProps.id); if (!doc) { // 新しく開かれたタブ(キャッシュに存在しない) // copy をもとに新しい Doc を作り、 value を更新 doc = this.codeMirror.getDoc().copy(false); doc.setValue(nextProps.value); // value の更新 doc.clearHistory(); this.state.docs.set(nextProps.id, doc); } // 現在のタブと入れ替え this.codeMirror.swapDoc(doc); this.props.onDocChanged({ id: nextProps.id, doc: doc }, { id: this.props.id, doc: prev }); } else { // 同じタブ(ファイル) this.setValueIfDifferent(nextProps.value); // value の更新 } // options の更新 var ignoreKeys = ['id', 'value']; var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = _getIterator(_Object$entries(nextProps)), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var _step$value = _slicedToArray(_step.value, 2), key = _step$value[0], nextValue = _step$value[1]; if (includes(ignoreKeys, key)) continue; if (!deepEqual(this.props[key], nextProps[key])) { // options の変更を CodeMirror に伝える this.codeMirror.setOption(key, nextValue); } } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } } }, { key: 'getCodeMirror', value: function getCodeMirror() { return this.codeMirror; } }, { key: 'setValueIfDifferent', value: function setValueIfDifferent(nextValue) { // 現在エディタに表示されている文章と比較し、違ったら setValue する var value = this.codeMirror.getValue(); if (value !== nextValue) { // スクロール位置を保持する var _codeMirror$getScroll = this.codeMirror.getScrollInfo(), left = _codeMirror$getScroll.left, top = _codeMirror$getScroll.top; this.codeMirror.setValue(value); this.codeMirror.scrollTo(left, top); } } }, { key: 'render', value: function render() { var _this2 = this; return React.createElement('textarea', { ref: function ref(_ref2) { return _this2.ref = _ref2; } }); } }, { key: 'options', get: function get() { var gutters = []; if (this.props.lineNumbers) { gutters.push('CodeMirror-linenumbers'); } if (this.props.foldGutter) { gutters.push('CodeMirror-foldgutter'); } return _extends({}, this.props, { gutters: gutters }); } }]); return CodeMirrorComponent; }(PureComponent); CodeMirrorComponent.propTypes = { id: PropTypes.string.isRequired, onDocChanged: PropTypes.func.isRequired, // CodeMirror options value: PropTypes.string.isRequired, mode: PropTypes.string, lineNumbers: PropTypes.bool.isRequired, indentUnit: PropTypes.number.isRequired, indentWithTabs: PropTypes.bool.isRequired, matchBrackets: PropTypes.bool.isRequired, autoCloseBrackets: PropTypes.bool.isRequired, keyMap: PropTypes.string.isRequired, foldOptions: PropTypes.object, dragDrop: PropTypes.bool.isRequired, extraKeys: PropTypes.object.isRequired, readOnly: PropTypes.bool.isRequired, foldGutter: PropTypes.bool.isRequired }; CodeMirrorComponent.defaultProps = { mode: null, lineNumbers: true, indentUnit: 4, indentWithTabs: true, matchBrackets: true, autoCloseBrackets: true, keyMap: 'default', dragDrop: false, extraKeys: {}, readOnly: false, foldGutter: false, onDocChanged: function onDocChanged() {} }; export default CodeMirrorComponent;