@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.
- package/dist/explorerViewWorkerMain.js +388 -351
- package/package.json +1 -1
|
@@ -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
|
|
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
|
|
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
|
|
1107
|
-
|
|
1108
|
-
return root;
|
|
1109
|
-
}
|
|
1110
|
-
return dirents[index].path;
|
|
1289
|
+
const sortExplorerItems = rawDirents => {
|
|
1290
|
+
return rawDirents.toSorted(compareDirent);
|
|
1111
1291
|
};
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
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
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
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
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
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
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
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
|
-
|
|
1188
|
-
|
|
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
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
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
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
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
|
|
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
|
-
|
|
1397
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
const
|
|
4849
|
-
|
|
4850
|
-
|
|
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
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
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
|
|
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);
|