vs-editor-file-mgr.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  1. /**
  2. * 1.管理文件资源逻辑部分
  3. */
  4. const fe = Editor.require('packages://simple-code/tools/tools.js');
  5. const fs = require('fs');
  6. const config = Editor.require('packages://simple-code/config.js');
  7. const path = require("path");
  8. const tools = require('../../tools/tools');
  9. const { WatchMgr, WatchFile } = require('../../tools/watchFile');
  10. const prsPath = Editor.Project && Editor.Project.path ? Editor.Project.path : Editor.remote.projectPath;
  11. class FileMgr{
  12. constructor(parent){
  13. /** @type import('./vs-panel-base') */
  14. this.parent = parent;
  15. this.watchMgr = new WatchMgr()
  16. this.importPathBuffer = {};
  17. this.waitReadModels = {};
  18. }
  19. // 更新游戏项目文件列表缓存
  20. initFileListBuffer(callback) {
  21. if (this.parent.file_list_buffer && this.parent.file_list_buffer.length != 0) {
  22. if(callback) callback();
  23. return ;
  24. };
  25. Editor.assetdb.queryAssets('db://**/*', '', (err, results)=> {
  26. if(this.parent.file_list_buffer && this.parent.file_list_buffer.length >0) return;
  27. for (let i = 0; i < results.length; i++)
  28. {
  29. let result = results[i];
  30. let info = this.getUriInfo(result.url);
  31. if (info.extname != "" && this.parent.SEARCH_BOX_IGNORE[info.extname] == null)
  32. {
  33. let name = info.name;
  34. result.extname = info.extname
  35. let item_cfg = this.newFileInfo(result.extname, name, result.url, result.uuid,result.path)
  36. this.parent.file_list_buffer.push(item_cfg);
  37. this.parent.file_list_map[fe.normPath( result.path )] = item_cfg;
  38. this.parent.file_counts[result.extname] = (this.parent.file_counts[result.extname] || 0) + 1
  39. }
  40. }
  41. this.sortFileBuffer();
  42. if(callback && this.parent.file_list_buffer.length > 0)
  43. {
  44. let temp = callback;
  45. callback = null;
  46. // schId()
  47. // schId = null;
  48. temp();
  49. }
  50. });
  51. }
  52. // 排序:设置搜索优先级
  53. sortFileBuffer() {
  54. let getScore = (extname) => {
  55. return this.parent.SEARCH_SCORES[extname] || (this.parent.FILE_OPEN_TYPES[extname] && 80) || (this.parent.SEARCH_BOX_IGNORE[extname] && 1) || 2;
  56. }
  57. this.parent.file_list_buffer.sort((a, b) => getScore(b.extname) - getScore(a.extname));
  58. }
  59. newFileInfo(extname, name, url, uuid,fsPath) {
  60. let item_cfg = {
  61. extname: extname,//格式
  62. value: name == "" ? url : name,
  63. meta: url,
  64. url: url,
  65. score: 0,//搜索优先级
  66. fsPath:tools.normPath(fsPath),
  67. // matchMask: i,
  68. // exactMatch: 0,
  69. uuid: uuid,
  70. data:undefined,// 文件数据
  71. };
  72. return item_cfg;
  73. }
  74. getUriInfo(url) {
  75. let s_i = url.lastIndexOf('/');
  76. if(s_i == -1) s_i = url.lastIndexOf('\\');
  77. let name = ""
  78. if (s_i != -1) name = url.substr(s_i + 1)
  79. s_i = name.lastIndexOf('.');
  80. let extname = ""
  81. if (s_i != -1) {
  82. extname = name.substr(s_i).toLowerCase()
  83. }
  84. return { name, extname,url }
  85. }
  86. getFileUrlInfoByUuid(uuid) {
  87. let url = Editor.remote.assetdb.uuidToUrl(uuid);
  88. let fs_path = Editor.remote.assetdb.urlToFspath(url);
  89. if(url == null || fs_path == null) return;
  90. fs_path = fs_path.replace(/\\/g,'/');
  91. let name = url.substr(url.lastIndexOf('/') + 1);
  92. let file_type = name.substr(name.lastIndexOf('.') + 1)
  93. if (!fe.isFileExit(fs_path) || fs.statSync(fs_path).isDirectory() || this.parent.IGNORE_FILE.indexOf(file_type) != -1) {
  94. return
  95. }
  96. let text = fs.readFileSync(fs_path).toString();
  97. return { data: text, uuid: uuid, path: url, name: name, file_type: file_type ,fs_path:fs_path};
  98. }
  99. getFileUrlInfoByFsPath(fs_path)
  100. {
  101. fs_path = fs_path.replace(/\\/g,'/');
  102. let uuid = Editor.remote.assetdb.fspathToUuid(fs_path) || "outside";
  103. let url = uuid == "outside" ? fs_path.replace(/\\/g,'/') : Editor.remote.assetdb.uuidToUrl(uuid);
  104. let name = url.substr(url.lastIndexOf('/') + 1);
  105. let file_type = name.substr(name.lastIndexOf('.') + 1)
  106. if (!fe.isFileExit(fs_path) || fs.statSync(fs_path).isDirectory() || this.parent.IGNORE_FILE.indexOf(file_type) != -1) {
  107. return
  108. }
  109. let text = fs.readFileSync(fs_path).toString();
  110. return { data: text, uuid: uuid, path: url, name: name, file_type: file_type ,fs_path:fs_path};
  111. }
  112. getModelByFsPath(fsPath){
  113. return this.parent.monaco.editor.getModel(this.fsPathToModelUrl(fsPath))
  114. }
  115. getModelByUrl(url){
  116. return this.getModelByFsPath(Editor.remote.assetdb.urlToFspath(url))
  117. }
  118. fsPathToModelUrl(fsPath){
  119. let str_uri = Editor.isWin32 ? fsPath.replace(/ /g,'').replace(/\\/g,'/') : fsPath;
  120. return this.parent.monaco.Uri.parse(str_uri).toString();
  121. }
  122. // fsPathToUrl(fsPath){
  123. // fsPath = fsPath.replace(/\\/g,'/')
  124. // let ind = fsPath.indexOf(fe.normPath( prsPath)+"/assets");
  125. // let str_uri;
  126. // if(ind != -1){
  127. // ind = prsPath.length;
  128. // let _path = fsPath.substr(ind+1);
  129. // str_uri = 'db://' + _path ;
  130. // }
  131. // return str_uri;
  132. // }
  133. checkCurrFileChange(editInfo) {
  134. // 正在编辑的文件被删
  135. if (editInfo && editInfo.uuid) {
  136. let file_path = editInfo.uuid == "outside" ? editInfo.path : unescape(Editor.url(editInfo.path));
  137. let text = ""
  138. try {
  139. text = fs.readFileSync(file_path).toString();
  140. } catch (e) {
  141. Editor.info("正在编辑的文件被删除:", file_path)
  142. return;
  143. }
  144. if (text != '' && text != editInfo.data) {
  145. if (editInfo.data != editInfo.new_data)
  146. {
  147. if (confirm(editInfo.name + " 文件在外边被修改是否刷新?"))
  148. {
  149. editInfo.data = editInfo.new_data = text;
  150. editInfo.is_need_save = false;
  151. editInfo.vs_model.setValue(text);
  152. }
  153. this.parent.upTitle(editInfo.id);
  154. } else {
  155. // 编辑器内文件未修改
  156. editInfo.data = editInfo.new_data = text;
  157. if (this.parent.edit_id == editInfo.id) {
  158. editInfo.vs_model.setValue(text);
  159. }
  160. }
  161. } else {
  162. this.parent.upTitle(editInfo.id);
  163. }
  164. return text;
  165. }
  166. }
  167. // 检查当前文件在外边是否被改变
  168. checkAllCurrFileChange() {
  169. // 编辑信息
  170. this.parent.edit_list.forEach((editInfo) => {
  171. this.checkCurrFileChange(editInfo)
  172. })
  173. }
  174. /**
  175. *
  176. * @param {sting} pathName
  177. * @param {import('../../tools/watchFile').WatchEventCallback} eventCallback
  178. * @returns {WatchFile}
  179. */
  180. addWatchPath(pathName,eventCallback){
  181. return this.watchMgr.addWatchPath(pathName,eventCallback);
  182. }
  183. checkWatch(){
  184. this.watchMgr.checkAll();
  185. }
  186. // 加载import引用路径上的文件
  187. async loadNeedImportPathsAsync(needImportPaths,isTs)
  188. {
  189. let importCompletePath
  190. let loadFunc = async (tryPath,isCompareName,isFromSystemRead)=>
  191. {
  192. tryPath = tryPath.substr(0,7) == 'file://' ? tryPath.substr(7) : tryPath; // 去掉前缀
  193. let fileItem
  194. if(isCompareName)
  195. {
  196. // 2.cocos专用只对比文件名的方式加载
  197. let _tryPath = tryPath;
  198. let index = _tryPath.lastIndexOf('/');
  199. if(index != -1){
  200. _tryPath = _tryPath.substr(index+1);
  201. }
  202. for (const fsPath in this.parent.file_list_map)
  203. {
  204. let fileName = fsPath;
  205. let _fileItem = this.parent.file_list_map[fsPath];
  206. if(_fileItem.extname == '.ts' || _fileItem.extname == '.js' || _fileItem.extname == '.json')
  207. {
  208. index = fileName.lastIndexOf('/');
  209. if(index != -1){
  210. fileName = fileName.substr(index+1);
  211. }
  212. index = fileName.lastIndexOf('.');
  213. if(index != -1){
  214. fileName = fileName.substr(0,index);
  215. }
  216. if(_tryPath == fileName){
  217. fileItem = _fileItem;
  218. break;
  219. }
  220. }
  221. }
  222. }else{
  223. // 1.正常node路径加载
  224. fileItem = this.parent.file_list_map[tryPath];
  225. }
  226. if(!fileItem){
  227. // console.log("尝试import失败:",tryPath)
  228. if(isFromSystemRead && await fe.isFileExitAsync(tryPath))
  229. {
  230. // 3.最后尝试从系统api读取
  231. fileItem = {
  232. uuid : "outside",
  233. meta : tryPath,
  234. }
  235. }else{
  236. return 1
  237. }
  238. // console.warn("测试失败import:",importPath,tryPath)
  239. }
  240. let isOutside = fileItem.uuid == "outside";
  241. let filePath = fileItem.meta;
  242. let vs_model = isOutside ? this.getModelByFsPath(filePath) : this.getModelByUrl(filePath);
  243. importCompletePath = filePath;
  244. if(this.waitReadModels[filePath] || vs_model && vs_model.getValue() != ''){
  245. return 0; // 已经存在缓存,不再继续读取
  246. }
  247. this.waitReadModels[filePath] = true;
  248. // 性能优化: 关闭刷新代码缓存,等需要加载的文件都加载完后再刷新
  249. this.parent.setEnableUpdateTs(false)
  250. // 2.加载文件
  251. this.parent.loadVsModeAsyn(filePath, path.extname(filePath) , !isOutside,false,(model)=>{
  252. delete this.waitReadModels[filePath]; // 读取完成
  253. })
  254. // console.log("加载import:",filePath);
  255. return 0;
  256. }
  257. for (const importPath in needImportPaths)
  258. {
  259. // 告诉解析器已经处理此路径
  260. this.parent.tsWr.removeNeedImportPath(importPath)
  261. // console.log('尝试加载:',importPath,tryPaths.length)
  262. if(this.importPathBuffer[importPath]){
  263. continue ;// 已经尝试加载过
  264. }
  265. this.importPathBuffer[importPath] = true;
  266. let tryPaths = needImportPaths[importPath];
  267. let isImport = false;
  268. for (let i = 0; i < tryPaths.length; i++)
  269. {
  270. // 1.从缓存找出路径文件是否存在
  271. let tryPath = tryPaths[i];
  272. let retState = await loadFunc(tryPath);
  273. if(retState == 1){
  274. continue;
  275. }else if(retState == 0){
  276. isImport = true;
  277. break;
  278. }
  279. }
  280. if(isImport){
  281. continue; // 已经加载成功
  282. }
  283. // 2.正常路径方式找不到文件时切换为只对比文件名的方式加载
  284. if( tryPaths.length ){
  285. let retState = await loadFunc(tryPaths[0],true)
  286. if(retState == 0){
  287. isImport = true;
  288. continue;
  289. }
  290. }
  291. // 3.从系统加载
  292. for (let i = 1; i < tryPaths.length; i++)
  293. {
  294. // 1.从硬盘上找出路径文件是否存在
  295. let tryPath = tryPaths[i];
  296. let retState = await loadFunc(tryPath,false,true);
  297. if(retState == 1){
  298. continue;
  299. }else if(retState == 0){
  300. isImport = true;
  301. break;
  302. }
  303. }
  304. }
  305. // 被加载的路径
  306. return importCompletePath;
  307. }
  308. // 项目资源文件发生改变
  309. assetsChangedEvent(info)
  310. {
  311. this.checkAllCurrFileChange();
  312. let isOutside = info.uuid == 'outside';// 内部修改
  313. let url = isOutside ? info.url : Editor.remote.assetdb.uuidToUrl(info.uuid); // outside额外做处理
  314. if(!url) return;
  315. let edit_id = this.parent.getTabIdByPath(url);
  316. if(edit_id == null || !this.parent.edit_list[edit_id] || !this.parent.edit_list[edit_id].is_need_save)
  317. {
  318. // 刷新文件/代码提示,只有未被编辑情况下才刷新
  319. let urlI = this.getUriInfo(url);
  320. let model = isOutside ? this.getModelByFsPath(url) : this.getModelByUrl(url)
  321. if(model){
  322. this.parent.loadVsModel(url, urlI.extname, !isOutside);
  323. if(this.parent.file_list_map[model.fsPath]){
  324. this.parent.file_list_map[model.fsPath].data = model.getValue();
  325. }
  326. }
  327. }
  328. }
  329. // 项目资源创建
  330. assetsCreatedEvent(files)
  331. {
  332. files.forEach((v, i) => {
  333. let urlI = this.getUriInfo(v.url)
  334. if (urlI.extname != "" && this.parent.SEARCH_BOX_IGNORE[urlI.extname] == null)
  335. {
  336. let isOutside = v.uuid == 'outside';// 内部修改
  337. let fsPath = fe.normPath( isOutside ? v.url : Editor.remote.assetdb.urlToFspath(v.url) );
  338. let item = this.newFileInfo(urlI.extname, urlI.name, v.url, v.uuid,fsPath);
  339. this.parent.file_list_buffer.push(item);
  340. this.parent.file_list_map[fsPath] = item;
  341. let edit_id = this.parent.getTabIdByPath(fsPath);
  342. if(edit_id != null){
  343. this.parent.closeTab(edit_id); // 被删的文件重新添加
  344. }
  345. this.parent.loadAssetAndCompleter(item.meta, item.extname,!isOutside);
  346. }
  347. })
  348. // 刷新编译
  349. this.parent.setTimeoutById(()=>{
  350. this.importPathBuffer = {};
  351. this.parent.upCompCodeFile()
  352. },500,'loadNeedImportPaths');
  353. }
  354. // 项目文件被删除
  355. assetsDeletedEvent(files)
  356. {
  357. files.forEach((v) =>
  358. {
  359. let isOutside = v.uuid == 'outside';
  360. // 删除缓存
  361. let fsPath = fe.normPath(v.path);
  362. delete this.parent.file_list_map[fsPath];
  363. for (let i = this.parent.file_list_buffer.length-1; i >= 0 ; i--) {
  364. let item = this.parent.file_list_buffer[i];
  365. if ( !isOutside && item.uuid == v.uuid || isOutside && v.url == item.meta ) {
  366. this.parent.file_list_buffer.splice(i, 1);
  367. break;
  368. }
  369. }
  370. let is_remove = false
  371. // 刷新编辑信息
  372. let old_url = isOutside ? fsPath : Editor.remote.assetdb.fspathToUrl(v.path) ;
  373. let id = this.parent.getTabIdByPath(old_url);
  374. // 正在编辑的tab
  375. if(id != null)
  376. {
  377. // 正在编辑的文件被删
  378. let editInfo = this.parent.edit_list[id]
  379. if (editInfo && ( !isOutside && v.uuid == editInfo.uuid || isOutside && fsPath == editInfo.path)) {
  380. editInfo.uuid = "outside";
  381. editInfo.path = isOutside ? fsPath : fe.normPath(unescape(Editor.url(editInfo.path)));
  382. editInfo.can_remove_model = 1;
  383. if(editInfo.vs_model)
  384. {
  385. // 刷新 model 信息,不然函数转跳不正确
  386. let text = editInfo.vs_model.getValue();
  387. editInfo.vs_model.dispose()
  388. let model = this.parent.loadVsModel(editInfo.path,this.getUriInfo(editInfo.path).extname,false,false)
  389. if(model)
  390. {
  391. let is_show = this.parent.vs_editor.getModel() == null;
  392. model.setValue(text)
  393. editInfo.vs_model = model;
  394. if(is_show){
  395. this.parent.setTabPage(id);
  396. }
  397. editInfo.data = ' ';
  398. editInfo.is_need_save = true;
  399. this.parent.upTitle(id);
  400. }
  401. }
  402. this.checkCurrFileChange(editInfo);
  403. is_remove = true
  404. }
  405. }else{
  406. // 清缓存
  407. let vs_model = this.parent.monaco.editor.getModel(this.parent.monaco.Uri.parse(this.fsPathToModelUrl(fsPath)))
  408. if(vs_model) vs_model.dispose()
  409. }
  410. })
  411. // 刷新编译
  412. this.parent.setTimeoutById(()=>{
  413. this.importPathBuffer = {};
  414. this.parent.upCompCodeFile();
  415. },500,'loadNeedImportPaths');
  416. }
  417. assetsMovedEvent(files)
  418. {
  419. files.forEach((v, i) =>
  420. {
  421. let urlI = this.getUriInfo(v.url)
  422. v.extname = urlI.extname;
  423. v.srcPath = fe.normPath(v.srcPath);
  424. v.destPath = fe.normPath(v.destPath);
  425. // 更新文件缓存
  426. delete this.parent.file_list_map[v.srcPath];
  427. for (let i = 0; i < this.parent.file_list_buffer.length; i++) {
  428. let item = this.parent.file_list_buffer[i];
  429. if (item.uuid == v.uuid)
  430. {
  431. let s_i = this.parent.refresh_file_list.indexOf(item.meta)
  432. if(s_i != -1) this.parent.refresh_file_list[s_i] = v.url; // 需要手动编译的文件
  433. item.extname = urlI.extname
  434. item.value = urlI.name
  435. item.meta = v.url
  436. item.url = v.url
  437. item.fsPath = v.destPath;
  438. this.parent.file_list_map[item.fsPath] = item;
  439. break;
  440. }
  441. }
  442. this.onMoveFile(v);
  443. });
  444. }
  445. // 移动 ts/js代码文件
  446. onMoveFile(v)
  447. {
  448. // 刷新编辑信息
  449. let urlI = this.getUriInfo(v.url)
  450. let id = this.parent.getTabIdByPath(Editor.remote.assetdb.fspathToUrl(v.srcPath));
  451. let hasMoveCodeFile = false;
  452. // 正在编辑的tab
  453. if (id != null)
  454. {
  455. let editInfo = this.parent.edit_list[id]
  456. if (editInfo && editInfo.uuid == v.uuid) {
  457. editInfo.path = v.url;
  458. editInfo.name = urlI.name;
  459. if(editInfo.vs_model)
  460. {
  461. // 刷新 model 信息,不然函数转跳不正确
  462. let text = editInfo.vs_model.getValue();
  463. editInfo.vs_model.dispose()
  464. let model = this.parent.loadVsModel(editInfo.path,urlI.extname,true,false)
  465. if(model)
  466. {
  467. let is_show = this.parent.vs_editor.getModel() == null;
  468. model.setValue(text)
  469. editInfo.vs_model = model;
  470. if(is_show){
  471. this.parent.vs_editor.setModel(editInfo.vs_model);
  472. this.parent.setTabPage(id);
  473. }
  474. }
  475. }
  476. this.parent.upTitle(editInfo.id)
  477. hasMoveCodeFile = true;
  478. }
  479. }else{
  480. // 修改缓存
  481. let vs_model = this.parent.monaco.editor.getModel(this.fsPathToModelUrl(v.srcPath))
  482. if(vs_model) {
  483. let text = vs_model.getValue();
  484. vs_model.dispose()
  485. let model = this.parent.loadVsModel(v.url,urlI.extname,true,false)
  486. model.setValue(text);
  487. hasMoveCodeFile = true;
  488. }
  489. }
  490. if(hasMoveCodeFile){
  491. // 刷新编译
  492. this.parent.setTimeoutById(()=>{
  493. this.importPathBuffer = {};
  494. this.parent.upCompCodeFile();
  495. },500,'loadNeedImportPaths');
  496. }
  497. }
  498. }
  499. module.exports = FileMgr;