feeles-ide
Version:
The hackable and serializable IDE to make learning material
201 lines (185 loc) • 6.9 kB
JavaScript
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;