feeles-ide
Version:
The hackable and serializable IDE to make learning material
539 lines (444 loc) • 18 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
var _getPrototypeOf3 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _react = _interopRequireWildcard(require("react"));
var _propTypes = _interopRequireDefault(require("prop-types"));
var _typestyle = require("typestyle");
var _LinearProgress = _interopRequireDefault(require("@material-ui/core/LinearProgress"));
var _jsBeautify = _interopRequireDefault(require("js-beautify"));
var _codemirror = require("codemirror");
var _LineWidget = _interopRequireDefault(require("./LineWidget"));
var _Editor = _interopRequireDefault(require("./Editor"));
var _MenuBar = _interopRequireDefault(require("./MenuBar"));
var _AssetPane = _interopRequireDefault(require("./AssetPane"));
var _ErrorPane = _interopRequireDefault(require("./ErrorPane"));
var _foldAsset = _interopRequireDefault(require("./foldAsset"));
var _core = require("@material-ui/core");
var _preserveTrailingSpaceBeautify = _interopRequireDefault(require("../../utils/preserveTrailingSpaceBeautify"));
var _dec, _class, _class2, _temp;
var cn = {
root: (0, _typestyle.style)({
position: 'absolute',
width: '100%',
height: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'stretch'
}),
editorContainer: (0, _typestyle.style)({
flex: '1 1 auto',
position: 'relative'
}),
barButton: (0, _typestyle.style)({
padding: 0,
lineHeight: 2
}),
barButtonLabel: (0, _typestyle.style)({
fontSize: '.5rem'
}),
progress: (0, _typestyle.style)({
borderRadius: 0
}),
blank: (0, _typestyle.style)({
flex: '1 1 auto'
})
};
var SourceEditor = (_dec = (0, _core.withTheme)(), _dec(_class = (_temp = _class2 =
/*#__PURE__*/
function (_PureComponent) {
(0, _inherits2.default)(SourceEditor, _PureComponent);
function SourceEditor() {
var _getPrototypeOf2;
var _this;
(0, _classCallCheck2.default)(this, SourceEditor);
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
_this = (0, _possibleConstructorReturn2.default)(this, (_getPrototypeOf2 = (0, _getPrototypeOf3.default)(SourceEditor)).call.apply(_getPrototypeOf2, [this].concat(args)));
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "state", {
file: null,
babelError: null,
showHint: false,
hasHistory: false,
hasChanged: false,
loading: false,
snippets: [],
showLineWidget: true
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "handleCodemirror", function (codemirror) {
_this.codemirror = codemirror; // アセットの入力で全角文字を使うので, この仕様は消している
// this.codemirror.on('beforeChange', zenkakuToHankaku);
var onChange = function onChange(cm) {
_this.setState({
hasHistory: cm.historySize().undo > 0,
hasChanged: cm.getValue('\n') !== _this.state.file.text
});
};
_this.codemirror.on('change', onChange);
_this.codemirror.on('swapDoc', onChange);
_this.codemirror.on('swapDoc', _this.foldAll);
_this.foldAll(codemirror);
_this.forceUpdate();
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "foldAll", function (cm) {
var opt = {
rangeFinder: _foldAsset.default
};
for (var line = cm.lineCount() - 1; line >= 0; line--) {
cm.foldCode(new _codemirror.Pos(line, 0), opt, 'fold');
}
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "runApp",
/*#__PURE__*/
function () {
var _ref = (0, _asyncToGenerator2.default)(
/*#__PURE__*/
_regenerator.default.mark(function _callee(href) {
var file, text, nextFile;
return _regenerator.default.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
file = _this.state.file;
if (!(!_this.codemirror || !file)) {
_context.next = 3;
break;
}
return _context.abrupt("return");
case 3:
_this.beautify(_this.codemirror); // Auto beautify
text = _this.codemirror.getValue();
_this.setState({
loading: true,
babelError: null
}); // Like a watching
_context.prev = 6;
nextFile = file.set({
text: text
});
_context.next = 10;
return nextFile.babel();
case 10:
_context.next = 12;
return _this.props.putFile(file, nextFile);
case 12:
// 再読み込み
_this.props.setLocation(href);
_context.next = 20;
break;
case 15:
_context.prev = 15;
_context.t0 = _context["catch"](6);
_this.props.globalEvent.emit('message.editor', {
data: {
value: file.name
}
}); // もう一度ファイルを開かせる
_this.setState({
babelError: _context.t0
});
console.info(_context.t0);
case 20:
_this.setState({
loading: false
});
case 21:
case "end":
return _context.stop();
}
}
}, _callee, this, [[6, 15]]);
}));
return function (_x) {
return _ref.apply(this, arguments);
};
}());
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "handleUndo", function () {
_this.codemirror.undo();
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "handleRestore", function () {
var file = _this.state.file;
var cm = _this.codemirror;
if (!file || !cm) return;
var _cm$getScrollInfo = cm.getScrollInfo(),
left = _cm$getScrollInfo.left,
top = _cm$getScrollInfo.top;
_this.codemirror.scrollTo(left, top); // 変更を加える前の状態(前回保存したところ)に戻す
while (cm.historySize().undo > 0) {
cm.undo(); // ひとつ前に戻す
if (cm.getValue() === file.text) {
// 前回の保存内容と同じになった
break;
}
}
if (cm.getValue() !== file.text) {
// 履歴を遡っても同じにはならなかった(履歴が混在している)
cm.clearHistory();
cm.setValue(file.text);
}
_this.codemirror.scrollTo(left, top);
_this.runApp();
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "beautify", function () {
var fileView = _this.props.fileView;
var file = _this.state.file;
if (!file) return;
var prevValue = _this.codemirror.getValue();
var setValueWithoutHistory = function setValueWithoutHistory(replacement) {
// undo => beautify => setValue することで history を 1 つに
var _this$codemirror$getS = _this.codemirror.getScrollInfo(),
left = _this$codemirror$getS.left,
top = _this$codemirror$getS.top;
_this.codemirror.undo();
_this.codemirror.setValue(replacement);
_this.codemirror.scrollTo(left, top);
}; // import .jsbeautifyrc
var configs = {};
try {
var runCommand = fileView.getFileByFullPath('.jsbeautifyrc');
if (runCommand) {
configs = JSON.parse(runCommand.text);
}
} catch (error) {
console.info(error);
}
if (file.is('javascript') || file.is('json')) {
setValueWithoutHistory((0, _preserveTrailingSpaceBeautify.default)(prevValue, configs.js || {}));
} else if (file.is('html')) {
setValueWithoutHistory(_jsBeautify.default.html(prevValue, configs.html || {}));
} else if (file.is('css')) {
setValueWithoutHistory(_jsBeautify.default.css(prevValue, configs.css || {}));
}
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "setShowLineWidget", function (showLineWidget) {
_this.setState({
showLineWidget: showLineWidget
});
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "saveFileIfNeeded",
/*#__PURE__*/
(0, _asyncToGenerator2.default)(
/*#__PURE__*/
_regenerator.default.mark(function _callee2() {
var cm, file, text, nextFile;
return _regenerator.default.wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
cm = _this.codemirror;
file = _this.state.file;
if (!(!cm || !file)) {
_context2.next = 4;
break;
}
return _context2.abrupt("return");
case 4:
text = cm.getValue(); // 現在のコード
if (!(file.text == text)) {
_context2.next = 7;
break;
}
return _context2.abrupt("return");
case 7:
// 変わっていない
nextFile = file.set({
text: text
});
_context2.next = 10;
return nextFile.babel();
case 10:
_context2.next = 12;
return _this.props.putFile(file, nextFile);
case 12:
case "end":
return _context2.stop();
}
}
}, _callee2, this);
})));
return _this;
}
(0, _createClass2.default)(SourceEditor, [{
key: "componentDidUpdate",
value: function componentDidUpdate(prevProps) {
if (prevProps.fileView !== this.props.fileView && this.state.file) {
this.setState({
snippets: this.props.getConfig('snippets')(this.state.file)
});
}
if (this.props.filePath && this.props.filePath !== prevProps.filePath) {
this.handleUpdateFile();
}
}
}, {
key: "componentDidMount",
value: function componentDidMount() {
if (this.props.filePath) {
this.handleUpdateFile();
}
}
}, {
key: "handleUpdateFile",
value: function handleUpdateFile() {
var _this$props = this.props,
filePath = _this$props.filePath,
findFile = _this$props.findFile;
var file = findFile(filePath);
if (file && file !== this.state.file) {
this.setFile(file);
}
}
}, {
key: "setFile",
value: function setFile(file) {
if (!file) return;
this.setState({
file: file,
showHint: !file.is('json'),
snippets: this.props.getConfig('snippets')(file)
});
}
}, {
key: "setValue",
value: function setValue(value) {
var _this$codemirror$getS2 = this.codemirror.getScrollInfo(),
left = _this$codemirror$getS2.left,
top = _this$codemirror$getS2.top;
this.codemirror.setValue(value);
this.codemirror.scrollTo(left, top);
}
}, {
key: "render",
value: function render() {
var _this2 = this;
var localization = this.props.localization;
var _this$state = this.state,
file = _this$state.file,
showHint = _this$state.showHint;
if (!file) {
return null;
} // const snippets = this.props.getConfig('snippets')(file);
var extraKeys = {
'Ctrl-Enter': function CtrlEnter() {
// Key Binding された操作の直後にカーソルが先頭に戻ってしまう(?)ため,
// それをやり過ごしてから実行する
window.setTimeout(_this2.runApp, 10);
},
'Ctrl-Alt-B': function CtrlAltB() {
// Key Binding された操作の直後にカーソルが先頭に戻ってしまう(?)ため,
// それをやり過ごしてから実行する
window.setTimeout(_this2.beautify, 10);
}
};
var foldOptions = {
widget: ' ... ',
minFoldSize: 1,
scanUp: false
};
if (file.is('javascript')) {
foldOptions.rangeFinder = _foldAsset.default;
}
return _react.default.createElement("div", {
className: cn.root
}, _react.default.createElement(_MenuBar.default, {
localization: localization,
getFiles: this.props.getFiles,
href: this.props.href,
handleUndo: this.handleUndo,
runApp: this.runApp,
hasHistory: this.state.hasHistory,
hasChanged: this.state.hasChanged,
filePath: this.props.filePath,
tabs: this.props.tabs,
showLineWidget: this.state.showLineWidget,
setShowLineWidget: this.setShowLineWidget,
label: this.props.label,
iconUrl: this.props.iconUrl,
filePathToBack: this.props.filePathToBack,
globalEvent: this.props.globalEvent,
isExpandingEditorCard: this.props.isExpandingEditorCard,
setExpandingEditorCard: this.props.setExpandingEditorCard
}), this.state.loading ? _react.default.createElement(_LinearProgress.default, {
color: "primary",
className: cn.progress
}) : null, _react.default.createElement("div", {
className: cn.editorContainer
}, _react.default.createElement(_Editor.default, {
file: file,
getFiles: this.props.getFiles,
getConfig: this.props.getConfig,
findFile: this.props.findFile,
loadConfig: this.props.loadConfig,
fileView: this.props.fileView,
showHint: showHint,
snippets: this.state.snippets,
codemirrorRef: this.handleCodemirror,
extraKeys: extraKeys,
foldOptions: foldOptions
})), _react.default.createElement(_ErrorPane.default, {
error: this.state.babelError,
localization: localization,
onRestore: this.handleRestore,
canRestore: this.state.hasHistory
}), this.codemirror && this.props.asset && _react.default.createElement(_AssetPane.default, {
label: this.props.label,
codemirror: this.codemirror,
files: this.props.files,
findFile: this.props.findFile,
putFile: this.props.putFile,
runApp: this.runApp,
localization: localization,
globalEvent: this.props.globalEvent,
asset: this.props.asset,
filePath: this.props.filePath,
filePathToBack: this.props.filePathToBack,
isExpandingEditorCard: this.props.isExpandingEditorCard,
setExpandingEditorCard: this.props.setExpandingEditorCard,
saveFileIfNeeded: this.saveFileIfNeeded
}), this.codemirror && _react.default.createElement(_LineWidget.default, {
show: this.state.showLineWidget,
codemirror: this.codemirror,
runApp: this.runApp,
localization: localization
}));
}
}]);
return SourceEditor;
}(_react.PureComponent), (0, _defineProperty2.default)(_class2, "propTypes", {
theme: _propTypes.default.object.isRequired,
fileView: _propTypes.default.object.isRequired,
filePath: _propTypes.default.string.isRequired,
files: _propTypes.default.array.isRequired,
getFiles: _propTypes.default.func.isRequired,
setLocation: _propTypes.default.func.isRequired,
href: _propTypes.default.string.isRequired,
getConfig: _propTypes.default.func.isRequired,
loadConfig: _propTypes.default.func.isRequired,
findFile: _propTypes.default.func.isRequired,
reboot: _propTypes.default.bool.isRequired,
localization: _propTypes.default.object.isRequired,
openFileDialog: _propTypes.default.func.isRequired,
putFile: _propTypes.default.func.isRequired,
tabs: _propTypes.default.array.isRequired,
label: _propTypes.default.string.isRequired,
iconUrl: _propTypes.default.string.isRequired,
filePathToBack: _propTypes.default.string.isRequired,
globalEvent: _propTypes.default.object.isRequired,
asset: _propTypes.default.object,
isExpandingEditorCard: _propTypes.default.bool.isRequired,
setExpandingEditorCard: _propTypes.default.func.isRequired
}), _temp)) || _class);
exports.default = SourceEditor;