@lvce-editor/explorer-view 2.35.0 → 2.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -850,6 +850,134 @@ const WebWorkerRpcClient = {
850
850
  create: create$2
851
851
  };
852
852
 
853
+ const rpcs = Object.create(null);
854
+ const set$5 = (id, rpc) => {
855
+ rpcs[id] = rpc;
856
+ };
857
+ const get$1 = id => {
858
+ return rpcs[id];
859
+ };
860
+ const RendererWorker$1 = 1;
861
+ const invoke$2 = (method, ...params) => {
862
+ const rpc = get$1(RendererWorker$1);
863
+ // @ts-ignore
864
+ return rpc.invoke(method, ...params);
865
+ };
866
+ const set$2 = rpc => {
867
+ set$5(RendererWorker$1, rpc);
868
+ };
869
+ const RendererWorker = {
870
+ __proto__: null,
871
+ invoke: invoke$2,
872
+ set: set$2
873
+ };
874
+
875
+ const {
876
+ invoke,
877
+ set: set$1
878
+ } = RendererWorker;
879
+
880
+ const remove = async dirent => {
881
+ return invoke('FileSystem.remove', dirent);
882
+ };
883
+ const readDirWithFileTypes = async uri => {
884
+ return invoke('FileSystem.readDirWithFileTypes', uri);
885
+ };
886
+ const getPathSeparator$1 = async root => {
887
+ return invoke('FileSystem.getPathSeparator', root);
888
+ };
889
+ const getRealPath = async path => {
890
+ return invoke('FileSystem.getRealPath', path);
891
+ };
892
+ const stat = async dirent => {
893
+ return invoke('FileSystem.stat', dirent);
894
+ };
895
+ const createFile = async uri => {
896
+ return invoke('FileSystem.writeFile', uri, '');
897
+ };
898
+ const writeFile = async (uri, content) => {
899
+ return invoke('FileSystem.writeFile', uri, content);
900
+ };
901
+ const mkdir = async uri => {
902
+ return invoke('FileSystem.mkdir', uri);
903
+ };
904
+ const rename$1 = async (oldUri, newUri) => {
905
+ return invoke('FileSystem.rename', oldUri, newUri);
906
+ };
907
+ const copy$1 = async (oldUri, newUri) => {
908
+ return invoke('FileSystem.copy', oldUri, newUri);
909
+ };
910
+
911
+ const createNestedPath = async (root, path, pathSeparator) => {
912
+ const parts = path.slice(root.length).split(pathSeparator);
913
+ let currentPath = '';
914
+ for (const part of parts) {
915
+ if (!part) continue;
916
+ currentPath = currentPath ? `${currentPath}${pathSeparator}${part}` : part;
917
+ try {
918
+ await mkdir(`${root}${currentPath}`);
919
+ } catch (error) {
920
+ // Ignore error if directory already exists
921
+ if (!(error instanceof Error && error.message.includes('already exists'))) {
922
+ throw error;
923
+ }
924
+ }
925
+ }
926
+ };
927
+
928
+ const dirname = (pathSeparator, path) => {
929
+ const index = path.lastIndexOf(pathSeparator);
930
+ if (index === -1) {
931
+ return path;
932
+ }
933
+ return path.slice(0, index);
934
+ };
935
+ const dirname2 = path => {
936
+ return dirname('/', path);
937
+ };
938
+ const join = (pathSeparator, ...parts) => {
939
+ return parts.join(pathSeparator);
940
+ };
941
+ const getBaseName = (pathSeparator, path) => {
942
+ return path.slice(path.lastIndexOf(pathSeparator) + 1);
943
+ };
944
+ const join2 = (path, childPath) => {
945
+ if (path.endsWith('/')) {
946
+ return `${path}${childPath}`;
947
+ }
948
+ return `${path}/${childPath}`;
949
+ };
950
+
951
+ const createNewDirentsAccept = async (newFileName, pathSeparator, absolutePath, root, createFn) => {
952
+ try {
953
+ // Create parent directories if they don't exist
954
+ if (newFileName.includes(pathSeparator)) {
955
+ const parentPath = dirname(pathSeparator, absolutePath);
956
+ await createNestedPath(root, parentPath, pathSeparator);
957
+ }
958
+ await createFn(absolutePath);
959
+ return true;
960
+ } catch (error) {
961
+ console.error(new VError(error, `Failed to create file`));
962
+ return false;
963
+ }
964
+ };
965
+
966
+ const createTree = (items, root) => {
967
+ const tree = Object.create(null);
968
+ const rootLength = root.length;
969
+ for (const item of items) {
970
+ const relativePath = item.path.slice(rootLength);
971
+ const dirname = dirname2(relativePath);
972
+ tree[dirname] ||= [];
973
+ tree[dirname].push({
974
+ name: item.name,
975
+ type: item.type
976
+ });
977
+ }
978
+ return tree;
979
+ };
980
+
853
981
  const None$5 = 0;
854
982
  const CreateFile = 1;
855
983
  const CreateFolder = 2;
@@ -901,7 +1029,7 @@ const openInIntegratedTerminal = () => {
901
1029
  const cut = () => {
902
1030
  return i18nString(Cut$1);
903
1031
  };
904
- const copy$1 = () => {
1032
+ const copy = () => {
905
1033
  return i18nString(Copy$1);
906
1034
  };
907
1035
  const paste = () => {
@@ -913,7 +1041,7 @@ const copyPath$1 = () => {
913
1041
  const copyRelativePath$1 = () => {
914
1042
  return i18nString(CopyRelativePath);
915
1043
  };
916
- const rename$1 = () => {
1044
+ const rename = () => {
917
1045
  return i18nString(Rename);
918
1046
  };
919
1047
  const deleteItem = () => {
@@ -982,33 +1110,6 @@ const getPaths = items => {
982
1110
  return items.map(getPath);
983
1111
  };
984
1112
 
985
- const rpcs = Object.create(null);
986
- const set$5 = (id, rpc) => {
987
- rpcs[id] = rpc;
988
- };
989
- const get$1 = id => {
990
- return rpcs[id];
991
- };
992
- const RendererWorker$1 = 1;
993
- const invoke$2 = (method, ...params) => {
994
- const rpc = get$1(RendererWorker$1);
995
- // @ts-ignore
996
- return rpc.invoke(method, ...params);
997
- };
998
- const set$2 = rpc => {
999
- set$5(RendererWorker$1, rpc);
1000
- };
1001
- const RendererWorker = {
1002
- __proto__: null,
1003
- invoke: invoke$2,
1004
- set: set$2
1005
- };
1006
-
1007
- const {
1008
- invoke,
1009
- set: set$1
1010
- } = RendererWorker;
1011
-
1012
1113
  const DELTA_EDITING = 100;
1013
1114
 
1014
1115
  const BlockDevice = 1;
@@ -1075,6 +1176,88 @@ const getFileIcons = async (dirents, fileIconCache) => {
1075
1176
  };
1076
1177
  };
1077
1178
 
1179
+ const getParentFolder = (dirents, index, root) => {
1180
+ if (index < 0) {
1181
+ return root;
1182
+ }
1183
+ return dirents[index].path;
1184
+ };
1185
+
1186
+ const getPathParts = (root, uri, pathSeparator) => {
1187
+ const parts = [];
1188
+ let index = root.length - 1;
1189
+ let depth = 0;
1190
+ while ((index = uri.indexOf('/', index + 1)) !== -1) {
1191
+ const partUri = uri.slice(0, index);
1192
+ parts.push({
1193
+ path: partUri,
1194
+ depth: depth++,
1195
+ root,
1196
+ pathSeparator
1197
+ });
1198
+ }
1199
+ return parts;
1200
+ };
1201
+
1202
+ const isSymbolicLink = dirent => {
1203
+ return dirent.type === Symlink;
1204
+ };
1205
+
1206
+ const hasSymbolicLinks = rawDirents => {
1207
+ return rawDirents.some(isSymbolicLink);
1208
+ };
1209
+
1210
+ const ENOENT = 'ENOENT';
1211
+
1212
+ const getSymlinkType = type => {
1213
+ switch (type) {
1214
+ case File:
1215
+ return SymLinkFile;
1216
+ case Directory:
1217
+ return SymLinkFolder;
1218
+ default:
1219
+ return Symlink;
1220
+ }
1221
+ };
1222
+
1223
+ // TODO maybe resolving of symbolic links should happen in shared process?
1224
+ // so that there is less code and less work in the frontend
1225
+ const resolveSymbolicLink = async (uri, rawDirent) => {
1226
+ try {
1227
+ // TODO support windows paths
1228
+ const absolutePath = uri + '/' + rawDirent.name;
1229
+ const type = await stat(absolutePath);
1230
+ const symLinkType = getSymlinkType(type);
1231
+ return {
1232
+ name: rawDirent.name,
1233
+ type: symLinkType
1234
+ };
1235
+ } catch (error) {
1236
+ // @ts-ignore
1237
+ if (error && error.code === ENOENT) {
1238
+ return {
1239
+ name: rawDirent.name,
1240
+ type: SymLinkFile
1241
+ };
1242
+ }
1243
+ console.error(`Failed to resolve symbolic link for ${rawDirent.name}: ${error}`);
1244
+ return rawDirent;
1245
+ }
1246
+ };
1247
+ const resolveSymbolicLinks = async (uri, rawDirents) => {
1248
+ const promises = [];
1249
+ for (const rawDirent of rawDirents) {
1250
+ if (isSymbolicLink(rawDirent)) {
1251
+ const resolvedDirent = resolveSymbolicLink(uri, rawDirent);
1252
+ promises.push(resolvedDirent);
1253
+ } else {
1254
+ promises.push(rawDirent);
1255
+ }
1256
+ }
1257
+ const resolvedDirents = await Promise.all(promises);
1258
+ return resolvedDirents;
1259
+ };
1260
+
1078
1261
  const RE_CHARACTERS = /^[a-zA-Z.-]+$/;
1079
1262
  const compareStringNumeric = (a, b) => {
1080
1263
  if (RE_CHARACTERS.test(a) && RE_CHARACTERS.test(b)) {
@@ -1103,99 +1286,151 @@ const compareDirent = (direntA, direntB) => {
1103
1286
  return compareDirentType(direntA, direntB) || compareDirentName(direntA, direntB);
1104
1287
  };
1105
1288
 
1106
- const getParentFolder = (dirents, index, root) => {
1107
- if (index < 0) {
1108
- return root;
1109
- }
1110
- return dirents[index].path;
1289
+ const sortExplorerItems = rawDirents => {
1290
+ return rawDirents.toSorted(compareDirent);
1111
1291
  };
1112
- const getNewDirentsAccept = async (state, newDirentType, createFn) => {
1113
- const {
1114
- focusedIndex,
1115
- editingValue
1116
- } = state;
1117
- const newFileName = editingValue;
1118
- const parentFolder = getParentFolder(state.items, focusedIndex, state.root);
1119
- const absolutePath = [parentFolder, newFileName].join(state.pathSeparator);
1120
- // TODO better handle error
1121
- try {
1122
- await createFn(absolutePath);
1123
- } catch (error) {
1124
- console.error(new VError(error, `Failed to create file`));
1125
- // TODO display error
1126
- return {
1127
- dirents: state.items,
1128
- newFocusedIndex: state.focusedIndex
1129
- };
1130
- }
1131
- const parentDirent = focusedIndex >= 0 ? state.items[focusedIndex] : {
1132
- depth: 0};
1133
- const depth = parentDirent.depth + 1;
1134
- const newDirent = {
1135
- path: absolutePath,
1136
- // @ts-ignore
1137
- posInSet: -1,
1138
- setSize: 1,
1139
- depth,
1140
- name: newFileName,
1141
- type: newDirentType,
1292
+
1293
+ // TODO figure out whether this uses too much memory (name,path -> redundant, depth could be computed on demand)
1294
+ const toDisplayDirent = (parentDirent, rawDirent, index, length) => {
1295
+ const path = join2(parentDirent.path, rawDirent.name);
1296
+ return {
1297
+ name: rawDirent.name,
1298
+ posInSet: index + 1,
1299
+ setSize: length,
1300
+ depth: parentDirent.depth + 1,
1301
+ type: rawDirent.type,
1302
+ path,
1303
+ // TODO storing absolute path might be too costly, could also store relative path here
1142
1304
  icon: '',
1143
1305
  selected: false
1144
1306
  };
1145
- // @ts-ignore
1146
- newDirent.icon = '';
1147
- let insertIndex = state.focusedIndex;
1148
- let deltaPosInSet = 0;
1149
- let posInSet = 1;
1150
- let setSize = 1;
1151
- let i = Math.max(state.focusedIndex, -1) + 1;
1152
- const {
1153
- items
1154
- } = state;
1155
- // TODO update posinset and setsize of all affected dirents
1156
- for (; i < items.length; i++) {
1157
- const dirent = items[i];
1158
- if (dirent.depth !== depth) {
1159
- break;
1160
- }
1161
- const compareResult = compareDirent(dirent, newDirent);
1162
- if (compareResult === 1) {
1163
- insertIndex = i - 1;
1164
- deltaPosInSet = 1 - 1;
1165
- break;
1166
- } else {
1167
- // @ts-ignore
1168
- posInSet = dirent.posInSet + 1;
1169
- // @ts-ignore
1170
- setSize = dirent.setSize + 1;
1171
- // @ts-ignore
1172
- insertIndex = i;
1307
+ };
1308
+
1309
+ const toDisplayDirents = (pathSeparator, rawDirents, parentDirent, excluded) => {
1310
+ rawDirents = sortExplorerItems(rawDirents);
1311
+ const result = [];
1312
+ const visibleItems = rawDirents.filter(item => {
1313
+ if (excluded.includes(item.name)) {
1314
+ return false;
1173
1315
  }
1174
- // @ts-ignore
1175
- dirent.setSize++;
1176
- // @ts-ignore
1177
- dirent.posInSet += deltaPosInSet;
1316
+ return true;
1317
+ });
1318
+ const count = visibleItems.length;
1319
+ for (let i = 0; i < visibleItems.length; i++) {
1320
+ const rawDirent = visibleItems[i];
1321
+ result.push(toDisplayDirent(parentDirent, rawDirent, i, count));
1178
1322
  }
1179
- // @ts-ignore
1180
- newDirent.setSize = setSize;
1181
- // @ts-ignore
1182
- newDirent.posInSet = posInSet;
1183
- // @ts-ignore
1184
- items.splice(insertIndex + 1, 0, newDirent);
1185
- const newDirents = [...items].filter(item => item.type !== EditingFile && item.type !== EditingFolder);
1323
+ return result;
1324
+ };
1325
+
1326
+ const getChildDirentsRaw = async uri => {
1327
+ const rawDirents = await readDirWithFileTypes(uri);
1328
+ array(rawDirents);
1329
+ if (hasSymbolicLinks(rawDirents)) {
1330
+ return resolveSymbolicLinks(uri, rawDirents);
1331
+ }
1332
+ return rawDirents;
1333
+ };
1334
+ const getChildDirents = async (pathSeparator, parentDirent, excluded = []) => {
1335
+ string(pathSeparator);
1336
+ object(parentDirent);
1337
+ // TODO use event/actor based code instead, this is impossible to cancel right now
1338
+ // also cancel updating when opening new folder
1339
+ // const dispose = state => state.pendingRequests.forEach(cancelRequest)
1340
+ // TODO should use FileSystem directly in this case because it is globally available anyway
1341
+ // and more typesafe than Command.execute
1342
+ // and more performant
1343
+ const uri = parentDirent.path;
1344
+ const rawDirents = await getChildDirentsRaw(uri);
1345
+ const displayDirents = toDisplayDirents(pathSeparator, rawDirents, parentDirent, excluded);
1346
+ return displayDirents;
1347
+ };
1348
+
1349
+ const isTopLevel = dirent => {
1350
+ return dirent.depth === 1;
1351
+ };
1352
+
1353
+ const orderDirents = dirents => {
1354
+ if (dirents.length === 0) {
1355
+ return dirents;
1356
+ }
1357
+ const withDeepChildren = (parent, processed) => {
1358
+ if (processed.has(parent.path)) {
1359
+ return [];
1360
+ }
1361
+ processed.add(parent.path);
1362
+ const children = [];
1363
+ for (const dirent of dirents) {
1364
+ if (dirent.depth === parent.depth + 1 && dirent.path.startsWith(parent.path)) {
1365
+ children.push(...withDeepChildren(dirent, processed));
1366
+ }
1367
+ }
1368
+ return [parent, ...children];
1369
+ };
1370
+ const topLevelDirents = dirents.filter(isTopLevel);
1371
+ const processed = new Set();
1372
+ const ordered = topLevelDirents.flatMap(dirent => withDeepChildren(dirent, processed));
1373
+ return ordered;
1374
+ };
1375
+
1376
+ const getPathPartChildren = async pathPart => {
1377
+ const children = await getChildDirents(pathPart.pathSeparator, pathPart);
1378
+ return children;
1379
+ };
1380
+ const getPathPartsChildren = async pathparts => {
1381
+ const pathPartsChildren = await Promise.all(pathparts.map(getPathPartChildren));
1382
+ const pathPartsChildrenFlat = pathPartsChildren.flat();
1383
+ const orderedPathParts = orderDirents(pathPartsChildrenFlat);
1384
+ return orderedPathParts;
1385
+ };
1386
+
1387
+ const mergeTrees = (a, b) => {
1186
1388
  return {
1187
- dirents: newDirents,
1188
- newFocusedIndex: insertIndex + 1
1389
+ ...a,
1390
+ ...b
1189
1391
  };
1190
1392
  };
1191
1393
 
1394
+ const treeToArray = (map, root) => {
1395
+ const items = [];
1396
+ const processChildren = (path, depth) => {
1397
+ const children = map[path];
1398
+ if (!children) {
1399
+ return;
1400
+ }
1401
+ const count = children.length;
1402
+ for (let i = 0; i < count; i++) {
1403
+ const child = children[i];
1404
+ const childPath = join2(path, child.name);
1405
+ const absolutePath = `${root}${childPath}`;
1406
+ items.push({
1407
+ depth,
1408
+ posInSet: i + 1,
1409
+ setSize: count,
1410
+ icon: '',
1411
+ path: absolutePath,
1412
+ selected: false,
1413
+ name: child.name,
1414
+ type: child.type
1415
+ });
1416
+ processChildren(childPath, depth + 1);
1417
+ }
1418
+ };
1419
+ processChildren('', 0);
1420
+ return items;
1421
+ };
1422
+
1192
1423
  const acceptCreate = async (state, newDirentType, createFn) => {
1193
1424
  const {
1194
1425
  editingValue,
1195
1426
  minLineY,
1196
1427
  height,
1197
1428
  itemHeight,
1198
- fileIconCache
1429
+ fileIconCache,
1430
+ pathSeparator,
1431
+ root,
1432
+ focusedIndex,
1433
+ items
1199
1434
  } = state;
1200
1435
  const newFileName = editingValue;
1201
1436
  if (!newFileName) {
@@ -1208,10 +1443,20 @@ const acceptCreate = async (state, newDirentType, createFn) => {
1208
1443
  editingErrorMessage
1209
1444
  };
1210
1445
  }
1211
- const {
1212
- dirents,
1213
- newFocusedIndex
1214
- } = await getNewDirentsAccept(state, newDirentType, createFn);
1446
+ const parentFolder = getParentFolder(items, focusedIndex, root);
1447
+ const absolutePath = join2(parentFolder, newFileName);
1448
+ const successful = await createNewDirentsAccept(newFileName, pathSeparator, absolutePath, root, createFn);
1449
+ if (!successful) {
1450
+ return state;
1451
+ }
1452
+ const pathPaths = getPathParts(root, absolutePath, pathSeparator);
1453
+ const children = await getPathPartsChildren(pathPaths);
1454
+ const tree = createTree(items, root);
1455
+ const childTree = createTree(children, root);
1456
+ const merged = mergeTrees(tree, childTree);
1457
+ const newItems = treeToArray(merged, root);
1458
+ const dirents = newItems;
1459
+ const newFocusedIndex = newItems.findIndex(dirent => dirent.path === absolutePath);
1215
1460
  const maxLineY = getExplorerMaxLineY(minLineY, height, itemHeight, dirents.length);
1216
1461
  const visible = dirents.slice(minLineY, maxLineY);
1217
1462
  const {
@@ -1231,37 +1476,6 @@ const acceptCreate = async (state, newDirentType, createFn) => {
1231
1476
  };
1232
1477
  };
1233
1478
 
1234
- const remove = async dirent => {
1235
- return invoke('FileSystem.remove', dirent);
1236
- };
1237
- const readDirWithFileTypes = async uri => {
1238
- return invoke('FileSystem.readDirWithFileTypes', uri);
1239
- };
1240
- const getPathSeparator$1 = async root => {
1241
- return invoke('FileSystem.getPathSeparator', root);
1242
- };
1243
- const getRealPath = async path => {
1244
- return invoke('FileSystem.getRealPath', path);
1245
- };
1246
- const stat = async dirent => {
1247
- return invoke('FileSystem.stat', dirent);
1248
- };
1249
- const createFile = async uri => {
1250
- return invoke('FileSystem.writeFile', uri, '');
1251
- };
1252
- const writeFile = async (uri, content) => {
1253
- return invoke('FileSystem.writeFile', uri, content);
1254
- };
1255
- const mkdir = async uri => {
1256
- return invoke('FileSystem.mkdir', uri);
1257
- };
1258
- const rename = async (oldUri, newUri) => {
1259
- return invoke('FileSystem.rename', oldUri, newUri);
1260
- };
1261
- const copy = async (oldUri, newUri) => {
1262
- return invoke('FileSystem.copy', oldUri, newUri);
1263
- };
1264
-
1265
1479
  const acceptCreateFile = async state => {
1266
1480
  return acceptCreate(state, File, createFile);
1267
1481
  };
@@ -1365,42 +1579,28 @@ const computeExplorerRenamedDirent = (dirents, index, newName) => {
1365
1579
  };
1366
1580
  };
1367
1581
 
1368
- const dirname = (pathSeparator, path) => {
1369
- const index = path.lastIndexOf(pathSeparator);
1370
- if (index === -1) {
1371
- return path;
1372
- }
1373
- return path.slice(0, index);
1374
- };
1375
- const join = (pathSeparator, ...parts) => {
1376
- return parts.join(pathSeparator);
1377
- };
1378
- const getBaseName = (pathSeparator, path) => {
1379
- return path.slice(path.lastIndexOf(pathSeparator) + 1);
1380
- };
1381
- const join2 = (path, childPath) => {
1382
- if (path.endsWith('/')) {
1383
- return `${path}${childPath}`;
1582
+ const createNewDirentsRename = async (renamedDirent, editingValue, pathSeparator) => {
1583
+ try {
1584
+ // TODO this does not work with rename of nested file
1585
+ const oldAbsolutePath = renamedDirent.path;
1586
+ const oldParentPath = dirname2(oldAbsolutePath);
1587
+ const newAbsolutePath = join2(oldParentPath, editingValue);
1588
+ await rename$1(oldAbsolutePath, newAbsolutePath);
1589
+ } catch (error) {
1590
+ console.error(new VError(error, `Failed to rename file`));
1591
+ return false;
1384
1592
  }
1385
- return `${path}/${childPath}`;
1593
+ return true;
1386
1594
  };
1387
1595
 
1388
1596
  const acceptRename = async state => {
1389
1597
  const {
1390
1598
  editingIndex,
1391
1599
  editingValue,
1392
- items,
1393
- pathSeparator
1394
- } = state;
1600
+ items} = state;
1395
1601
  const renamedDirent = items[editingIndex];
1396
- try {
1397
- // TODO this does not work with rename of nested file
1398
- const oldAbsolutePath = renamedDirent.path;
1399
- const oldParentPath = dirname(pathSeparator, oldAbsolutePath);
1400
- const newAbsolutePath = join2(oldParentPath, editingValue);
1401
- await rename(oldAbsolutePath, newAbsolutePath);
1402
- } catch (error) {
1403
- console.error(new VError(error, `Failed to rename file`));
1602
+ const successful = await createNewDirentsRename(renamedDirent, editingValue);
1603
+ if (!successful) {
1404
1604
  return state;
1405
1605
  }
1406
1606
  const {
@@ -1514,10 +1714,6 @@ const cancelTypeAhead = state => {
1514
1714
  };
1515
1715
  };
1516
1716
 
1517
- const isTopLevel$1 = dirent => {
1518
- return dirent.depth === 1;
1519
- };
1520
-
1521
1717
  const toCollapsedDirent = dirent => {
1522
1718
  if (dirent.type === DirectoryExpanded) {
1523
1719
  return {
@@ -1532,7 +1728,7 @@ const collapseAll = state => {
1532
1728
  const {
1533
1729
  items
1534
1730
  } = state;
1535
- const newDirents = items.filter(isTopLevel$1).map(toCollapsedDirent);
1731
+ const newDirents = items.filter(isTopLevel).map(toCollapsedDirent);
1536
1732
  return {
1537
1733
  ...state,
1538
1734
  items: newDirents
@@ -1795,119 +1991,6 @@ const diff2 = uid => {
1795
1991
  return result;
1796
1992
  };
1797
1993
 
1798
- const isSymbolicLink = dirent => {
1799
- return dirent.type === Symlink;
1800
- };
1801
-
1802
- const ENOENT = 'ENOENT';
1803
-
1804
- const getSymlinkType = type => {
1805
- switch (type) {
1806
- case File:
1807
- return SymLinkFile;
1808
- case Directory:
1809
- return SymLinkFolder;
1810
- default:
1811
- return Symlink;
1812
- }
1813
- };
1814
-
1815
- // TODO maybe resolving of symbolic links should happen in shared process?
1816
- // so that there is less code and less work in the frontend
1817
- const resolveSymbolicLink = async (uri, rawDirent) => {
1818
- try {
1819
- // TODO support windows paths
1820
- const absolutePath = uri + '/' + rawDirent.name;
1821
- const type = await stat(absolutePath);
1822
- const symLinkType = getSymlinkType(type);
1823
- return {
1824
- name: rawDirent.name,
1825
- type: symLinkType
1826
- };
1827
- } catch (error) {
1828
- // @ts-ignore
1829
- if (error && error.code === ENOENT) {
1830
- return {
1831
- name: rawDirent.name,
1832
- type: SymLinkFile
1833
- };
1834
- }
1835
- console.error(`Failed to resolve symbolic link for ${rawDirent.name}: ${error}`);
1836
- return rawDirent;
1837
- }
1838
- };
1839
- const resolveSymbolicLinks = async (uri, rawDirents) => {
1840
- const promises = [];
1841
- for (const rawDirent of rawDirents) {
1842
- if (isSymbolicLink(rawDirent)) {
1843
- const resolvedDirent = resolveSymbolicLink(uri, rawDirent);
1844
- promises.push(resolvedDirent);
1845
- } else {
1846
- promises.push(rawDirent);
1847
- }
1848
- }
1849
- const resolvedDirents = await Promise.all(promises);
1850
- return resolvedDirents;
1851
- };
1852
-
1853
- const sortExplorerItems = rawDirents => {
1854
- return rawDirents.toSorted(compareDirent);
1855
- };
1856
-
1857
- const toDisplayDirents = (pathSeparator, rawDirents, parentDirent, excluded) => {
1858
- rawDirents = sortExplorerItems(rawDirents);
1859
- // TODO figure out whether this uses too much memory (name,path -> redundant, depth could be computed on demand)
1860
- const toDisplayDirent = (rawDirent, index) => {
1861
- const path = [parentDirent.path, rawDirent.name].join(pathSeparator);
1862
- return {
1863
- name: rawDirent.name,
1864
- posInSet: index + 1,
1865
- setSize: rawDirents.length,
1866
- depth: parentDirent.depth + 1,
1867
- type: rawDirent.type,
1868
- path,
1869
- // TODO storing absolute path might be too costly, could also store relative path here
1870
- icon: ''
1871
- };
1872
- };
1873
- const result = [];
1874
- let i = 0;
1875
- for (const rawDirent of rawDirents) {
1876
- if (excluded.includes(rawDirent.name)) {
1877
- continue;
1878
- }
1879
- result.push(toDisplayDirent(rawDirent, i));
1880
- i++;
1881
- }
1882
- return result;
1883
- };
1884
-
1885
- const hasSymbolicLinks = rawDirents => {
1886
- return rawDirents.some(isSymbolicLink);
1887
- };
1888
- const getChildDirentsRaw = async uri => {
1889
- const rawDirents = await readDirWithFileTypes(uri);
1890
- array(rawDirents);
1891
- if (hasSymbolicLinks(rawDirents)) {
1892
- return resolveSymbolicLinks(uri, rawDirents);
1893
- }
1894
- return rawDirents;
1895
- };
1896
- const getChildDirents = async (pathSeparator, parentDirent, excluded = []) => {
1897
- string(pathSeparator);
1898
- object(parentDirent);
1899
- // TODO use event/actor based code instead, this is impossible to cancel right now
1900
- // also cancel updating when opening new folder
1901
- // const dispose = state => state.pendingRequests.forEach(cancelRequest)
1902
- // TODO should use FileSystem directly in this case because it is globally available anyway
1903
- // and more typesafe than Command.execute
1904
- // and more performant
1905
- const uri = parentDirent.path;
1906
- const rawDirents = await getChildDirentsRaw(uri);
1907
- const displayDirents = toDisplayDirents(pathSeparator, rawDirents, parentDirent, excluded);
1908
- return displayDirents;
1909
- };
1910
-
1911
1994
  const expandAll = async state => {
1912
1995
  const {
1913
1996
  items,
@@ -2348,7 +2431,7 @@ const menuEntryCut = {
2348
2431
  };
2349
2432
  const menuEntryCopy = {
2350
2433
  id: 'copy',
2351
- label: copy$1(),
2434
+ label: copy(),
2352
2435
  flags: RestoreFocus,
2353
2436
  command: 'Explorer.handleCopy'
2354
2437
  };
@@ -2372,7 +2455,7 @@ const menuEntryCopyRelativePath = {
2372
2455
  };
2373
2456
  const menuEntryRename = {
2374
2457
  id: 'rename',
2375
- label: rename$1(),
2458
+ label: rename(),
2376
2459
  flags: None$4,
2377
2460
  command: 'Explorer.renameDirent'
2378
2461
  };
@@ -3116,7 +3199,7 @@ const applyOperation = operation => {
3116
3199
  return mkdir(operation.path);
3117
3200
  }
3118
3201
  if (operation.type === 'copy') {
3119
- return copy(operation.from || '', operation.path);
3202
+ return copy$1(operation.from || '', operation.path);
3120
3203
  }
3121
3204
  return writeFile(operation.path, operation.text);
3122
3205
  };
@@ -3330,7 +3413,7 @@ const handleDropIntoFolder = async (state, dirent, index, fileHandles, files, pa
3330
3413
  const baseName = file.name;
3331
3414
  const to = dirent.path + pathSeparator + baseName;
3332
3415
  // @ts-ignore
3333
- await copy(file, to);
3416
+ await copy$1(file, to);
3334
3417
  }
3335
3418
  const childDirents = await getChildDirents(pathSeparator, dirent);
3336
3419
  const mergedDirents = getMergedDirents(items, index, dirent, childDirents);
@@ -3584,7 +3667,7 @@ const handlePasteCopy = async (state, nativeFiles) => {
3584
3667
  for (const source of nativeFiles.files) {
3585
3668
  // @ts-ignore
3586
3669
  const target = join(state.pathSeperator, state.root, getBaseName(state.pathSeparator, source));
3587
- await copy(source, target);
3670
+ await copy$1(source, target);
3588
3671
  }
3589
3672
  // TODO only update folder at which level it changed
3590
3673
  return updateRoot(state);
@@ -3593,7 +3676,7 @@ const handlePasteCopy = async (state, nativeFiles) => {
3593
3676
  const handlePasteCut = async (state, nativeFiles) => {
3594
3677
  for (const source of nativeFiles.files) {
3595
3678
  const target = `${state.root}${state.pathSeparator}${getBaseName(state.pathSeparator, source)}`;
3596
- await rename(source, target);
3679
+ await rename$1(source, target);
3597
3680
  }
3598
3681
  return state;
3599
3682
  };
@@ -4814,22 +4897,6 @@ const getIndex = (dirents, uri) => {
4814
4897
  return -1;
4815
4898
  };
4816
4899
 
4817
- const getPathParts = (root, uri, pathSeparator) => {
4818
- const parts = [];
4819
- let index = root.length - 1;
4820
- let depth = 0;
4821
- while ((index = uri.indexOf('/', index + 1)) !== -1) {
4822
- const partUri = uri.slice(0, index);
4823
- parts.push({
4824
- path: partUri,
4825
- depth: depth++,
4826
- root,
4827
- pathSeparator
4828
- });
4829
- }
4830
- return parts;
4831
- };
4832
-
4833
4900
  const getPathPartsToReveal = (root, pathParts, dirents) => {
4834
4901
  for (let i = 0; i < pathParts.length; i++) {
4835
4902
  const pathPart = pathParts[i];
@@ -4842,30 +4909,18 @@ const getPathPartsToReveal = (root, pathParts, dirents) => {
4842
4909
  return pathParts;
4843
4910
  };
4844
4911
 
4845
- const isTopLevel = dirent => {
4846
- return dirent.depth === 1;
4847
- };
4848
- const orderDirents = dirents => {
4849
- if (dirents.length === 0) {
4850
- return dirents;
4851
- }
4852
- const withDeepChildren = (parent, processed) => {
4853
- if (processed.has(parent.path)) {
4854
- return [];
4855
- }
4856
- processed.add(parent.path);
4857
- const children = [];
4858
- for (const dirent of dirents) {
4859
- if (dirent.depth === parent.depth + 1 && dirent.path.startsWith(parent.path)) {
4860
- children.push(...withDeepChildren(dirent, processed));
4861
- }
4912
+ const mergeVisibleWithHiddenItems = (visibleItems, hiddenItems) => {
4913
+ const merged = [...visibleItems, ...hiddenItems];
4914
+ const seen = Object.create(null);
4915
+ const unique = [];
4916
+ for (const item of merged) {
4917
+ if (seen[item.path]) {
4918
+ continue;
4862
4919
  }
4863
- return [parent, ...children];
4864
- };
4865
- const topLevelDirents = dirents.filter(isTopLevel);
4866
- const processed = new Set();
4867
- const ordered = topLevelDirents.flatMap(dirent => withDeepChildren(dirent, processed));
4868
- return ordered;
4920
+ seen[item.path] = true;
4921
+ unique.push(item);
4922
+ }
4923
+ return unique;
4869
4924
  };
4870
4925
 
4871
4926
  const scrollInto = (index, minLineY, maxLineY) => {
@@ -4890,24 +4945,6 @@ const scrollInto = (index, minLineY, maxLineY) => {
4890
4945
  };
4891
4946
  };
4892
4947
 
4893
- const getPathPartChildren = async pathPart => {
4894
- const children = await getChildDirents(pathPart.pathSeparator, pathPart);
4895
- return children;
4896
- };
4897
- const mergeVisibleWithHiddenItems = (visibleItems, hiddenItems) => {
4898
- const merged = [...visibleItems, ...hiddenItems];
4899
- const seen = Object.create(null);
4900
- const unique = [];
4901
- for (const item of merged) {
4902
- if (seen[item.path]) {
4903
- continue;
4904
- }
4905
- seen[item.path] = true;
4906
- unique.push(item);
4907
- }
4908
- return unique;
4909
- };
4910
-
4911
4948
  // TODO maybe just insert items into explorer and refresh whole explorer
4912
4949
  const revealItemHidden = async (state, uri) => {
4913
4950
  const {
@@ -4922,7 +4959,7 @@ const revealItemHidden = async (state, uri) => {
4922
4959
  return state;
4923
4960
  }
4924
4961
  const pathPartsToReveal = getPathPartsToReveal(root, pathParts, items);
4925
- const pathPartsChildren = await Promise.all(pathPartsToReveal.map(getPathPartChildren));
4962
+ const pathPartsChildren = await getPathPartsChildren(pathPartsToReveal);
4926
4963
  const pathPartsChildrenFlat = pathPartsChildren.flat();
4927
4964
  const orderedPathParts = orderDirents(pathPartsChildrenFlat);
4928
4965
  const mergedDirents = mergeVisibleWithHiddenItems(items, orderedPathParts);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/explorer-view",
3
- "version": "2.35.0",
3
+ "version": "2.37.0",
4
4
  "description": "Explorer Worker",
5
5
  "repository": {
6
6
  "type": "git",