feeles-ide
Version:
The hackable and serializable IDE to make learning material
453 lines (380 loc) • 14 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
var _toArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toArray"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _includes = _interopRequireDefault(require("lodash/includes"));
var _ = require("./");
/**
* ファイルの状態を In Memory & IndexedDB で保有するストア
* ファイルの探索を手助けする Indexer でもある
* Redux は使わず、 FileView インスタンスが setState の
* 参照をもち、外から状態を更新する
*/
var FileView =
/*#__PURE__*/
function () {
function FileView(files) {
(0, _classCallCheck2.default)(this, FileView);
this.files = files;
this._changed = true; // files 変更フラグ
}
(0, _createClass2.default)(FileView, [{
key: "install",
value: function install(component) {
this.component = component;
/*
互換性保持のため, つねに最新のステートを参照するAPIをのこす
いずれ this.state.fileView を直接 propergate させたい
*/
component.addFile = this.addFile.bind(this);
component.putFile = this.putFile.bind(this);
component.deleteFile = this.deleteFile.bind(this);
component.findFile = this.findFile.bind(this);
}
}, {
key: "uninstall",
value: function uninstall() {
this.component = null;
}
}, {
key: "setState",
value: function setState(_ref) {
var files = _ref.files;
this._changed = true; // Index するフラグ
if (!files) throw 'Cannot set other than files';
if (!this.component) throw 'Component is not been set';
var fileView = new FileView(files);
fileView.install(this.component);
return this.component.setStatePromise({
fileView: fileView
});
}
/**
* ファイルパスでファイルを取得
* @param {String} path 取得したいファイルのパス
* @return {SourceFile|BinalyFile|null} ファイルまたは null
*/
}, {
key: "getFileByFullPath",
value: function getFileByFullPath(path) {
this.updateIndex();
return this.pathToFileMap.get(path);
}
/**
* 任意の拡張子をもつすべてのファイルを取得
* @param {String} ext 取得したいファイルのパス
* @return {Array<SourceFile|BinalyFile>} ファイルの配列
*/
}, {
key: "getFilesByExtention",
value: function getFilesByExtention(ext) {
this.updateIndex();
return this.extToFilesMap.get(ext) || [];
}
/**
* Map を用いてファイル名をインデックス
* files が変わったら適宜呼び出す
*/
}, {
key: "updateIndex",
value: function updateIndex() {
var _this = this;
// files が変更されていればあらたにインデックス
if (!this._changed) return;
this._changed = false;
this.pathToFileMap = new Map();
this.extToFilesMap = new Map();
var append = function append(name, file) {
// name => File
_this.pathToFileMap.set(name, file); // extention => File
var _name$split = name.split('.'),
_name$split2 = (0, _toArray2.default)(_name$split),
extArray = _name$split2.slice(1);
if (!extArray.length) return;
var ext = extArray.join('.');
if (_this.extToFilesMap.has(ext)) {
var files = _this.extToFilesMap.get(ext);
_this.extToFilesMap.set(ext, files.concat(file));
} else {
_this.extToFilesMap.set(ext, [file]);
}
}; // i18n 以外
var i18n = [];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = this.files[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var file = _step.value;
if (!file.name.startsWith('i18n/')) {
append(file.name, file);
} else {
i18n.push(file);
}
} // i18n/{ll_CC} を追加
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return != null) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
var ll_CC = this.component.props.localization.ll_CC;
for (var _i = 0; _i < i18n.length; _i++) {
var _file = i18n[_i];
var _file$name$split = _file.name.split('/'),
_file$name$split2 = (0, _toArray2.default)(_file$name$split),
locale = _file$name$split2[1],
virtualPath = _file$name$split2.slice(2);
if (locale === ll_CC) {
append(virtualPath.join('/'), _file);
}
}
}
}, {
key: "forceUpdate",
value: function forceUpdate() {
var fileView = new FileView(this.files);
fileView.install(this.component);
return this.component.setStatePromise({
fileView: fileView
});
}
/**
* ファイルを検索して取得する (後方互換性)
* @param {String|Function} name ファイル名
* @param {Boolean} multiple 全件取得フラグ
*/
}, {
key: "findFile",
value: function findFile(name) {
var multiple = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
if (typeof name === 'string') {
name = name.replace(/^(\.\/|\/)*/, '');
}
var i18nName = "i18n/".concat(this.component.props.localization.ll_CC, "/").concat(name);
var pred = typeof name === 'function' ? name : function (file) {
return !file.options.isTrashed && ( // 言語設定による動的ファイルパス解決
file.name === i18nName || file.moduleName === i18nName || // 通常のファイルパス解決
file.name === name || file.moduleName === name);
};
return multiple ? this.files.filter(pred) : this.files.find(pred) || null;
}
/**
* ファイルをひとつ追加する
* @param {SourceFile|BinalyFile} file 追加するファイル
*/
}, {
key: "addFile",
value: function () {
var _addFile = (0, _asyncToGenerator2.default)(
/*#__PURE__*/
_regenerator.default.mark(function _callee(file) {
var remove, files;
return _regenerator.default.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
remove = this.inspection(file);
if (!(file === remove)) {
_context.next = 3;
break;
}
return _context.abrupt("return", file);
case 3:
files = this.files.concat(file).filter(function (item) {
return item !== remove;
});
_context.next = 6;
return this.setState({
files: files
});
case 6:
_context.next = 8;
return this.component.resetConfig(file.name);
case 8:
if (!this.component.state.project) {
_context.next = 11;
break;
}
_context.next = 11;
return (0, _.putFile)(this.component.state.project.id, file.serialize());
case 11:
return _context.abrupt("return", file);
case 12:
case "end":
return _context.stop();
}
}
}, _callee, this);
}));
function addFile(_x) {
return _addFile.apply(this, arguments);
}
return addFile;
}()
/**
* ファイルを置き換える
* @param {SourceFile|BinalyFile} prevFile 削除するファイル
* @param {SourceFile|BinalyFile} nextFile 追加するファイル
*/
}, {
key: "putFile",
value: function () {
var _putFile2 = (0, _asyncToGenerator2.default)(
/*#__PURE__*/
_regenerator.default.mark(function _callee2(prevFile, nextFile) {
var remove, files;
return _regenerator.default.wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
if (!(nextFile === undefined)) {
_context2.next = 2;
break;
}
return _context2.abrupt("return", this.addFile(prevFile));
case 2:
console.time('putFile 1');
remove = this.inspection(nextFile);
console.timeEnd('putFile 1');
if (!(remove === nextFile)) {
_context2.next = 7;
break;
}
return _context2.abrupt("return", prevFile);
case 7:
files = this.files.filter(function (item) {
return item !== remove && item.key !== prevFile.key;
}).concat(nextFile);
console.time('putFile 2');
_context2.next = 11;
return this.setState({
files: files
});
case 11:
console.timeEnd('putFile 2');
console.time('putFile 3');
this.component.resetConfig(prevFile.name);
console.timeEnd('putFile 3');
if (!this.component.state.project) {
_context2.next = 20;
break;
}
console.time('putFile 4');
_context2.next = 19;
return (0, _.putFile)(this.component.state.project.id, nextFile.serialize());
case 19:
console.timeEnd('putFile 4');
case 20:
return _context2.abrupt("return", nextFile);
case 21:
case "end":
return _context2.stop();
}
}
}, _callee2, this);
}));
function putFile(_x2, _x3) {
return _putFile2.apply(this, arguments);
}
return putFile;
}()
/**
* 任意個数のファイルを削除する
* @param {Array<SourceFile|BinalyFile>} targets 削除するファイル
*/
}, {
key: "deleteFile",
value: function () {
var _deleteFile2 = (0, _asyncToGenerator2.default)(
/*#__PURE__*/
_regenerator.default.mark(function _callee3() {
var _len,
targets,
_key,
keys,
files,
fileNames,
_args3 = arguments;
return _regenerator.default.wrap(function _callee3$(_context3) {
while (1) {
switch (_context3.prev = _context3.next) {
case 0:
for (_len = _args3.length, targets = new Array(_len), _key = 0; _key < _len; _key++) {
targets[_key] = _args3[_key];
}
keys = targets.map(function (item) {
return item.key;
});
files = this.files.filter(function (item) {
return !(0, _includes.default)(keys, item.key);
});
_context3.next = 5;
return this.setState({
files: files
});
case 5:
if (!this.component.state.project) {
_context3.next = 9;
break;
}
fileNames = targets.map(function (item) {
return item.name;
});
_context3.next = 9;
return _.deleteFile.apply(void 0, [this.component.state.project.id].concat((0, _toConsumableArray2.default)(fileNames)));
case 9:
case "end":
return _context3.stop();
}
}
}, _callee3, this);
}));
function deleteFile() {
return _deleteFile2.apply(this, arguments);
}
return deleteFile;
}()
/**
* ファイル名の衝突をしらべる. TODO: FileDialog で実現すべき
* https://trello.com/c/Y4CbIH81/244-conflict-%E3%81%AF%E3%83%80%E3%82%A4%E3%82%A2%E3%83%AD%E3%82%B0%E3%81%AE%E3%81%A8%E3%81%93%E3%82%8D%E3%81%A7%E5%88%A4%E5%AE%9A%E3%81%99%E3%82%8B-filestore-%E3%81%A7%E3%81%AF%E7%84%A1%E8%A6%96
* @param {Array<SourceFile|BinalyFile>} newFile 追加予定のファイル
*/
}, {
key: "inspection",
value: function inspection(newFile) {
var conflict = this.files.find(function (file) {
return !file.options.isTrashed && file.key !== newFile.key && file.name === newFile.name;
});
if (conflict) {
// TODO: FileDialog instead of.
// 一時的に "強制上書き" にする
console.log(newFile);
return conflict; // if (confirm(this.component.props.localization.common.conflict)) {
// return conflict;
// } else {
// return newFile;
// }
}
return null;
}
}]);
return FileView;
}();
exports.default = FileView;