@lvce-editor/explorer-view 1.2.0 → 1.4.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 +1112 -231
- package/package.json +1 -1
|
@@ -69,7 +69,7 @@ class JsonRpcError extends Error {
|
|
|
69
69
|
this.name = 'JsonRpcError';
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
|
-
const NewLine$
|
|
72
|
+
const NewLine$3 = '\n';
|
|
73
73
|
const DomException = 'DOMException';
|
|
74
74
|
const ReferenceError$1 = 'ReferenceError';
|
|
75
75
|
const SyntaxError$1 = 'SyntaxError';
|
|
@@ -114,23 +114,23 @@ const constructError = (message, type, name) => {
|
|
|
114
114
|
}
|
|
115
115
|
return new ErrorConstructor(message);
|
|
116
116
|
};
|
|
117
|
-
const getNewLineIndex$
|
|
118
|
-
return string.indexOf(NewLine$
|
|
117
|
+
const getNewLineIndex$2 = (string, startIndex = undefined) => {
|
|
118
|
+
return string.indexOf(NewLine$3, startIndex);
|
|
119
119
|
};
|
|
120
120
|
const getParentStack = error => {
|
|
121
121
|
let parentStack = error.stack || error.data || error.message || '';
|
|
122
122
|
if (parentStack.startsWith(' at')) {
|
|
123
|
-
parentStack = error.message + NewLine$
|
|
123
|
+
parentStack = error.message + NewLine$3 + parentStack;
|
|
124
124
|
}
|
|
125
125
|
return parentStack;
|
|
126
126
|
};
|
|
127
127
|
const joinLines$1 = lines => {
|
|
128
|
-
return lines.join(NewLine$
|
|
128
|
+
return lines.join(NewLine$3);
|
|
129
129
|
};
|
|
130
130
|
const MethodNotFound = -32601;
|
|
131
131
|
const Custom = -32001;
|
|
132
132
|
const splitLines$1 = lines => {
|
|
133
|
-
return lines.split(NewLine$
|
|
133
|
+
return lines.split(NewLine$3);
|
|
134
134
|
};
|
|
135
135
|
const restoreJsonRpcError = error => {
|
|
136
136
|
if (error && error instanceof Error) {
|
|
@@ -140,14 +140,14 @@ const restoreJsonRpcError = error => {
|
|
|
140
140
|
if (error && error.code && error.code === MethodNotFound) {
|
|
141
141
|
const restoredError = new JsonRpcError(error.message);
|
|
142
142
|
const parentStack = getParentStack(error);
|
|
143
|
-
restoredError.stack = parentStack + NewLine$
|
|
143
|
+
restoredError.stack = parentStack + NewLine$3 + currentStack;
|
|
144
144
|
return restoredError;
|
|
145
145
|
}
|
|
146
146
|
if (error && error.message) {
|
|
147
147
|
const restoredError = constructError(error.message, error.type, error.name);
|
|
148
148
|
if (error.data) {
|
|
149
149
|
if (error.data.stack && error.data.type && error.message) {
|
|
150
|
-
restoredError.stack = error.data.type + ': ' + error.message + NewLine$
|
|
150
|
+
restoredError.stack = error.data.type + ': ' + error.message + NewLine$3 + error.data.stack + NewLine$3 + currentStack;
|
|
151
151
|
} else if (error.data.stack) {
|
|
152
152
|
restoredError.stack = error.data.stack;
|
|
153
153
|
}
|
|
@@ -167,7 +167,7 @@ const restoreJsonRpcError = error => {
|
|
|
167
167
|
if (error.stack) {
|
|
168
168
|
const lowerStack = restoredError.stack || '';
|
|
169
169
|
// @ts-ignore
|
|
170
|
-
const indexNewLine = getNewLineIndex$
|
|
170
|
+
const indexNewLine = getNewLineIndex$2(lowerStack);
|
|
171
171
|
const parentStack = getParentStack(error);
|
|
172
172
|
// @ts-ignore
|
|
173
173
|
restoredError.stack = parentStack + lowerStack.slice(indexNewLine);
|
|
@@ -224,7 +224,7 @@ const getErrorProperty = (error, prettyError) => {
|
|
|
224
224
|
}
|
|
225
225
|
};
|
|
226
226
|
};
|
|
227
|
-
const create$1
|
|
227
|
+
const create$1 = (message, error) => {
|
|
228
228
|
return {
|
|
229
229
|
jsonrpc: Two,
|
|
230
230
|
id: message.id,
|
|
@@ -235,9 +235,9 @@ const getErrorResponse = (message, error, preparePrettyError, logError) => {
|
|
|
235
235
|
const prettyError = preparePrettyError(error);
|
|
236
236
|
logError(error, prettyError);
|
|
237
237
|
const errorProperty = getErrorProperty(error, prettyError);
|
|
238
|
-
return create$1
|
|
238
|
+
return create$1(message, errorProperty);
|
|
239
239
|
};
|
|
240
|
-
const create = (message, result) => {
|
|
240
|
+
const create$5 = (message, result) => {
|
|
241
241
|
return {
|
|
242
242
|
jsonrpc: Two,
|
|
243
243
|
id: message.id,
|
|
@@ -246,7 +246,7 @@ const create = (message, result) => {
|
|
|
246
246
|
};
|
|
247
247
|
const getSuccessResponse = (message, result) => {
|
|
248
248
|
const resultProperty = result ?? null;
|
|
249
|
-
return create(message, resultProperty);
|
|
249
|
+
return create$5(message, resultProperty);
|
|
250
250
|
};
|
|
251
251
|
const getResponse = async (message, ipc, execute, preparePrettyError, logError, requiresSocket) => {
|
|
252
252
|
try {
|
|
@@ -584,7 +584,7 @@ const getHelpfulChildProcessError = (stdout, stderr) => {
|
|
|
584
584
|
stack: rest
|
|
585
585
|
};
|
|
586
586
|
};
|
|
587
|
-
const normalizeLine = line => {
|
|
587
|
+
const normalizeLine$1 = line => {
|
|
588
588
|
if (line.startsWith('Error: ')) {
|
|
589
589
|
return line.slice('Error: '.length);
|
|
590
590
|
}
|
|
@@ -593,41 +593,41 @@ const normalizeLine = line => {
|
|
|
593
593
|
}
|
|
594
594
|
return line;
|
|
595
595
|
};
|
|
596
|
-
const getCombinedMessage = (error, message) => {
|
|
597
|
-
const stringifiedError = normalizeLine(`${error}`);
|
|
596
|
+
const getCombinedMessage$1 = (error, message) => {
|
|
597
|
+
const stringifiedError = normalizeLine$1(`${error}`);
|
|
598
598
|
if (message) {
|
|
599
599
|
return `${message}: ${stringifiedError}`;
|
|
600
600
|
}
|
|
601
601
|
return stringifiedError;
|
|
602
602
|
};
|
|
603
|
-
const NewLine = '\n';
|
|
604
|
-
const getNewLineIndex = (string, startIndex = undefined) => {
|
|
605
|
-
return string.indexOf(NewLine, startIndex);
|
|
603
|
+
const NewLine$2 = '\n';
|
|
604
|
+
const getNewLineIndex$1 = (string, startIndex = undefined) => {
|
|
605
|
+
return string.indexOf(NewLine$2, startIndex);
|
|
606
606
|
};
|
|
607
|
-
const mergeStacks = (parent, child) => {
|
|
607
|
+
const mergeStacks$1 = (parent, child) => {
|
|
608
608
|
if (!child) {
|
|
609
609
|
return parent;
|
|
610
610
|
}
|
|
611
|
-
const parentNewLineIndex = getNewLineIndex(parent);
|
|
612
|
-
const childNewLineIndex = getNewLineIndex(child);
|
|
611
|
+
const parentNewLineIndex = getNewLineIndex$1(parent);
|
|
612
|
+
const childNewLineIndex = getNewLineIndex$1(child);
|
|
613
613
|
if (childNewLineIndex === -1) {
|
|
614
614
|
return parent;
|
|
615
615
|
}
|
|
616
616
|
const parentFirstLine = parent.slice(0, parentNewLineIndex);
|
|
617
617
|
const childRest = child.slice(childNewLineIndex);
|
|
618
|
-
const childFirstLine = normalizeLine(child.slice(0, childNewLineIndex));
|
|
618
|
+
const childFirstLine = normalizeLine$1(child.slice(0, childNewLineIndex));
|
|
619
619
|
if (parentFirstLine.includes(childFirstLine)) {
|
|
620
620
|
return parentFirstLine + childRest;
|
|
621
621
|
}
|
|
622
622
|
return child;
|
|
623
623
|
};
|
|
624
|
-
class VError extends Error {
|
|
624
|
+
let VError$1 = class VError extends Error {
|
|
625
625
|
constructor(error, message) {
|
|
626
|
-
const combinedMessage = getCombinedMessage(error, message);
|
|
626
|
+
const combinedMessage = getCombinedMessage$1(error, message);
|
|
627
627
|
super(combinedMessage);
|
|
628
628
|
this.name = 'VError';
|
|
629
629
|
if (error instanceof Error) {
|
|
630
|
-
this.stack = mergeStacks(this.stack, error.stack);
|
|
630
|
+
this.stack = mergeStacks$1(this.stack, error.stack);
|
|
631
631
|
}
|
|
632
632
|
if (error.codeFrame) {
|
|
633
633
|
// @ts-ignore
|
|
@@ -638,8 +638,8 @@ class VError extends Error {
|
|
|
638
638
|
this.code = error.code;
|
|
639
639
|
}
|
|
640
640
|
}
|
|
641
|
-
}
|
|
642
|
-
class IpcError extends VError {
|
|
641
|
+
};
|
|
642
|
+
class IpcError extends VError$1 {
|
|
643
643
|
// @ts-ignore
|
|
644
644
|
constructor(betterMessage, stdout = '', stderr = '') {
|
|
645
645
|
if (stdout || stderr) {
|
|
@@ -784,7 +784,7 @@ const listen$1 = async () => {
|
|
|
784
784
|
const ipc = module.wrap(rawIpc);
|
|
785
785
|
return ipc;
|
|
786
786
|
};
|
|
787
|
-
const create
|
|
787
|
+
const create = async ({
|
|
788
788
|
commandMap
|
|
789
789
|
}) => {
|
|
790
790
|
// TODO create a commandMap per rpc instance
|
|
@@ -796,7 +796,7 @@ const create$1 = async ({
|
|
|
796
796
|
};
|
|
797
797
|
const WebWorkerRpcClient = {
|
|
798
798
|
__proto__: null,
|
|
799
|
-
create
|
|
799
|
+
create
|
|
800
800
|
};
|
|
801
801
|
|
|
802
802
|
const RE_CHARACTERS = /^[a-zA-Z.-]+$/;
|
|
@@ -943,7 +943,7 @@ const computeExplorerRenamedDirent = (dirents, index, newName) => {
|
|
|
943
943
|
};
|
|
944
944
|
};
|
|
945
945
|
|
|
946
|
-
const None$
|
|
946
|
+
const None$3 = 0;
|
|
947
947
|
const CreateFile = 1;
|
|
948
948
|
const CreateFolder = 2;
|
|
949
949
|
const Rename = 3;
|
|
@@ -955,60 +955,8 @@ const getRealPath = async path => {};
|
|
|
955
955
|
const stat = async dirent => {};
|
|
956
956
|
const createFile = async uri => {};
|
|
957
957
|
const mkdir = async uri => {};
|
|
958
|
-
const rename = async (oldUri, newUri) => {};
|
|
959
|
-
|
|
960
|
-
class AssertionError extends Error {
|
|
961
|
-
constructor(message) {
|
|
962
|
-
super(message);
|
|
963
|
-
this.name = 'AssertionError';
|
|
964
|
-
}
|
|
965
|
-
}
|
|
966
|
-
const getType = value => {
|
|
967
|
-
switch (typeof value) {
|
|
968
|
-
case 'number':
|
|
969
|
-
return 'number';
|
|
970
|
-
case 'function':
|
|
971
|
-
return 'function';
|
|
972
|
-
case 'string':
|
|
973
|
-
return 'string';
|
|
974
|
-
case 'object':
|
|
975
|
-
if (value === null) {
|
|
976
|
-
return 'null';
|
|
977
|
-
}
|
|
978
|
-
if (Array.isArray(value)) {
|
|
979
|
-
return 'array';
|
|
980
|
-
}
|
|
981
|
-
return 'object';
|
|
982
|
-
case 'boolean':
|
|
983
|
-
return 'boolean';
|
|
984
|
-
default:
|
|
985
|
-
return 'unknown';
|
|
986
|
-
}
|
|
987
|
-
};
|
|
988
|
-
const object = value => {
|
|
989
|
-
const type = getType(value);
|
|
990
|
-
if (type !== 'object') {
|
|
991
|
-
throw new AssertionError('expected value to be of type object');
|
|
992
|
-
}
|
|
993
|
-
};
|
|
994
|
-
const number = value => {
|
|
995
|
-
const type = getType(value);
|
|
996
|
-
if (type !== 'number') {
|
|
997
|
-
throw new AssertionError('expected value to be of type number');
|
|
998
|
-
}
|
|
999
|
-
};
|
|
1000
|
-
const array = value => {
|
|
1001
|
-
const type = getType(value);
|
|
1002
|
-
if (type !== 'array') {
|
|
1003
|
-
throw new AssertionError('expected value to be of type array');
|
|
1004
|
-
}
|
|
1005
|
-
};
|
|
1006
|
-
const string = value => {
|
|
1007
|
-
const type = getType(value);
|
|
1008
|
-
if (type !== 'string') {
|
|
1009
|
-
throw new AssertionError('expected value to be of type string');
|
|
1010
|
-
}
|
|
1011
|
-
};
|
|
958
|
+
const rename$1 = async (oldUri, newUri) => {};
|
|
959
|
+
const copy$1 = async (oldUri, newUri) => {};
|
|
1012
960
|
|
|
1013
961
|
const dirname = (pathSeparator, path) => {
|
|
1014
962
|
const index = path.lastIndexOf(pathSeparator);
|
|
@@ -1017,6 +965,12 @@ const dirname = (pathSeparator, path) => {
|
|
|
1017
965
|
}
|
|
1018
966
|
return path.slice(0, index);
|
|
1019
967
|
};
|
|
968
|
+
const join = (pathSeparator, ...parts) => {
|
|
969
|
+
return parts.join(pathSeparator);
|
|
970
|
+
};
|
|
971
|
+
const getBaseName = (pathSeparator, path) => {
|
|
972
|
+
return path.slice(path.lastIndexOf(pathSeparator) + 1);
|
|
973
|
+
};
|
|
1020
974
|
|
|
1021
975
|
const getParentFolder = (dirents, index, root) => {
|
|
1022
976
|
if (index < 0) {
|
|
@@ -1097,7 +1051,7 @@ const acceptCreate = async (state, newDirentType, createFn) => {
|
|
|
1097
1051
|
items: newDirents,
|
|
1098
1052
|
editingIndex: -1,
|
|
1099
1053
|
focusedIndex: insertIndex + 1,
|
|
1100
|
-
editingType: None$
|
|
1054
|
+
editingType: None$3,
|
|
1101
1055
|
maxLineY: newMaxlineY
|
|
1102
1056
|
};
|
|
1103
1057
|
};
|
|
@@ -1114,7 +1068,7 @@ const acceptRename = async state => {
|
|
|
1114
1068
|
const oldAbsolutePath = renamedDirent.path;
|
|
1115
1069
|
const oldParentPath = dirname(pathSeparator, oldAbsolutePath);
|
|
1116
1070
|
const newAbsolutePath = [oldParentPath, editingValue].join(pathSeparator);
|
|
1117
|
-
await rename(oldAbsolutePath, newAbsolutePath);
|
|
1071
|
+
await rename$1(oldAbsolutePath, newAbsolutePath);
|
|
1118
1072
|
} catch (error) {
|
|
1119
1073
|
// TODO
|
|
1120
1074
|
// await ErrorHandling.showErrorDialog(error)
|
|
@@ -1130,7 +1084,7 @@ const acceptRename = async state => {
|
|
|
1130
1084
|
...state,
|
|
1131
1085
|
editingIndex: -1,
|
|
1132
1086
|
editingValue: '',
|
|
1133
|
-
editingType: None$
|
|
1087
|
+
editingType: None$3,
|
|
1134
1088
|
editingIcon: '',
|
|
1135
1089
|
focusedIndex,
|
|
1136
1090
|
focused: true
|
|
@@ -1152,77 +1106,107 @@ const acceptEdit = state => {
|
|
|
1152
1106
|
}
|
|
1153
1107
|
};
|
|
1154
1108
|
|
|
1155
|
-
const
|
|
1109
|
+
const getFocusedDirent$1 = state => {
|
|
1110
|
+
const {
|
|
1111
|
+
focusedIndex,
|
|
1112
|
+
minLineY,
|
|
1113
|
+
items
|
|
1114
|
+
} = state;
|
|
1115
|
+
const dirent = items[focusedIndex + minLineY];
|
|
1116
|
+
return dirent;
|
|
1117
|
+
};
|
|
1156
1118
|
|
|
1157
|
-
const
|
|
1158
|
-
|
|
1119
|
+
const copyPath$1 = async state => {
|
|
1120
|
+
// await Command.execute(RendererWorkerCommandType.ClipBoardWriteText, /* text */ path)
|
|
1121
|
+
return state;
|
|
1159
1122
|
};
|
|
1160
1123
|
|
|
1161
|
-
const
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
type: rawDirent.type,
|
|
1172
|
-
path,
|
|
1173
|
-
// TODO storing absolute path might be too costly, could also store relative path here
|
|
1174
|
-
icon: getIcon()
|
|
1175
|
-
};
|
|
1176
|
-
};
|
|
1177
|
-
const result = [];
|
|
1178
|
-
let i = 0;
|
|
1179
|
-
for (const rawDirent of rawDirents) {
|
|
1180
|
-
if (excluded.includes(rawDirent.name)) {
|
|
1181
|
-
continue;
|
|
1182
|
-
}
|
|
1183
|
-
result.push(toDisplayDirent(rawDirent, i));
|
|
1184
|
-
i++;
|
|
1185
|
-
}
|
|
1186
|
-
return result;
|
|
1124
|
+
const state = {
|
|
1125
|
+
rpc: undefined
|
|
1126
|
+
};
|
|
1127
|
+
const invoke = (method, ...params) => {
|
|
1128
|
+
const rpc = state.rpc;
|
|
1129
|
+
// @ts-ignore
|
|
1130
|
+
return rpc.invoke(method, ...params);
|
|
1131
|
+
};
|
|
1132
|
+
const setRpc = rpc => {
|
|
1133
|
+
state.rpc = rpc;
|
|
1187
1134
|
};
|
|
1188
1135
|
|
|
1189
|
-
const
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1136
|
+
const writeText = async text => {
|
|
1137
|
+
await invoke('ClipBoard.writeText', /* text */text);
|
|
1138
|
+
};
|
|
1139
|
+
const readNativeFiles = async () => {
|
|
1140
|
+
return invoke('ClipBoard.readNativeFiles');
|
|
1141
|
+
};
|
|
1142
|
+
|
|
1143
|
+
const copyRelativePath$1 = async state => {
|
|
1144
|
+
const dirent = getFocusedDirent$1(state);
|
|
1145
|
+
const relativePath = dirent.path.slice(1);
|
|
1146
|
+
// TODO handle error
|
|
1147
|
+
await writeText(relativePath);
|
|
1148
|
+
return state;
|
|
1149
|
+
};
|
|
1150
|
+
|
|
1151
|
+
class AssertionError extends Error {
|
|
1152
|
+
constructor(message) {
|
|
1153
|
+
super(message);
|
|
1154
|
+
this.name = 'AssertionError';
|
|
1198
1155
|
}
|
|
1199
|
-
|
|
1200
|
-
|
|
1156
|
+
}
|
|
1157
|
+
const getType = value => {
|
|
1158
|
+
switch (typeof value) {
|
|
1159
|
+
case 'number':
|
|
1160
|
+
return 'number';
|
|
1161
|
+
case 'function':
|
|
1162
|
+
return 'function';
|
|
1163
|
+
case 'string':
|
|
1164
|
+
return 'string';
|
|
1165
|
+
case 'object':
|
|
1166
|
+
if (value === null) {
|
|
1167
|
+
return 'null';
|
|
1168
|
+
}
|
|
1169
|
+
if (Array.isArray(value)) {
|
|
1170
|
+
return 'array';
|
|
1171
|
+
}
|
|
1172
|
+
return 'object';
|
|
1173
|
+
case 'boolean':
|
|
1174
|
+
return 'boolean';
|
|
1175
|
+
default:
|
|
1176
|
+
return 'unknown';
|
|
1201
1177
|
}
|
|
1202
|
-
return index;
|
|
1203
1178
|
};
|
|
1204
|
-
const
|
|
1205
|
-
const
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
startIndex--;
|
|
1179
|
+
const object = value => {
|
|
1180
|
+
const type = getType(value);
|
|
1181
|
+
if (type !== 'object') {
|
|
1182
|
+
throw new AssertionError('expected value to be of type object');
|
|
1209
1183
|
}
|
|
1210
|
-
return startIndex;
|
|
1211
1184
|
};
|
|
1212
|
-
const
|
|
1213
|
-
const
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1185
|
+
const number = value => {
|
|
1186
|
+
const type = getType(value);
|
|
1187
|
+
if (type !== 'number') {
|
|
1188
|
+
throw new AssertionError('expected value to be of type number');
|
|
1189
|
+
}
|
|
1190
|
+
};
|
|
1191
|
+
const array = value => {
|
|
1192
|
+
const type = getType(value);
|
|
1193
|
+
if (type !== 'array') {
|
|
1194
|
+
throw new AssertionError('expected value to be of type array');
|
|
1217
1195
|
}
|
|
1218
|
-
return endIndex;
|
|
1219
1196
|
};
|
|
1197
|
+
const string = value => {
|
|
1198
|
+
const type = getType(value);
|
|
1199
|
+
if (type !== 'string') {
|
|
1200
|
+
throw new AssertionError('expected value to be of type string');
|
|
1201
|
+
}
|
|
1202
|
+
};
|
|
1203
|
+
|
|
1220
1204
|
const isSymbolicLink = dirent => {
|
|
1221
1205
|
return dirent.type === Symlink;
|
|
1222
1206
|
};
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1207
|
+
|
|
1208
|
+
const ENOENT = 'ENOENT';
|
|
1209
|
+
|
|
1226
1210
|
const getSymlinkType = type => {
|
|
1227
1211
|
switch (type) {
|
|
1228
1212
|
case File:
|
|
@@ -1233,6 +1217,7 @@ const getSymlinkType = type => {
|
|
|
1233
1217
|
return Symlink;
|
|
1234
1218
|
}
|
|
1235
1219
|
};
|
|
1220
|
+
|
|
1236
1221
|
// TODO maybe resolving of symbolic links should happen in shared process?
|
|
1237
1222
|
// so that there is less code and less work in the frontend
|
|
1238
1223
|
const resolveSymbolicLink = async (uri, rawDirent) => {
|
|
@@ -1270,6 +1255,42 @@ const resolveSymbolicLinks = async (uri, rawDirents) => {
|
|
|
1270
1255
|
const resolvedDirents = await Promise.all(promises);
|
|
1271
1256
|
return resolvedDirents;
|
|
1272
1257
|
};
|
|
1258
|
+
|
|
1259
|
+
const sortExplorerItems = rawDirents => {
|
|
1260
|
+
rawDirents.sort(compareDirent);
|
|
1261
|
+
};
|
|
1262
|
+
|
|
1263
|
+
const toDisplayDirents = (pathSeparator, rawDirents, parentDirent, excluded) => {
|
|
1264
|
+
sortExplorerItems(rawDirents);
|
|
1265
|
+
// TODO figure out whether this uses too much memory (name,path -> redundant, depth could be computed on demand)
|
|
1266
|
+
const toDisplayDirent = (rawDirent, index) => {
|
|
1267
|
+
const path = [parentDirent.path, rawDirent.name].join(pathSeparator);
|
|
1268
|
+
return {
|
|
1269
|
+
name: rawDirent.name,
|
|
1270
|
+
posInSet: index + 1,
|
|
1271
|
+
setSize: rawDirents.length,
|
|
1272
|
+
depth: parentDirent.depth + 1,
|
|
1273
|
+
type: rawDirent.type,
|
|
1274
|
+
path,
|
|
1275
|
+
// TODO storing absolute path might be too costly, could also store relative path here
|
|
1276
|
+
icon: getIcon()
|
|
1277
|
+
};
|
|
1278
|
+
};
|
|
1279
|
+
const result = [];
|
|
1280
|
+
let i = 0;
|
|
1281
|
+
for (const rawDirent of rawDirents) {
|
|
1282
|
+
if (excluded.includes(rawDirent.name)) {
|
|
1283
|
+
continue;
|
|
1284
|
+
}
|
|
1285
|
+
result.push(toDisplayDirent(rawDirent, i));
|
|
1286
|
+
i++;
|
|
1287
|
+
}
|
|
1288
|
+
return result;
|
|
1289
|
+
};
|
|
1290
|
+
|
|
1291
|
+
const hasSymbolicLinks = rawDirents => {
|
|
1292
|
+
return rawDirents.some(isSymbolicLink);
|
|
1293
|
+
};
|
|
1273
1294
|
const getChildDirentsRaw = async uri => {
|
|
1274
1295
|
const rawDirents = await readDirWithFileTypes();
|
|
1275
1296
|
array(rawDirents);
|
|
@@ -1347,12 +1368,91 @@ const expandAll = async state => {
|
|
|
1347
1368
|
};
|
|
1348
1369
|
};
|
|
1349
1370
|
|
|
1350
|
-
const
|
|
1351
|
-
const
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1371
|
+
const getParentEndIndex = (dirents, index) => {
|
|
1372
|
+
const dirent = dirents[index];
|
|
1373
|
+
let endIndex = index + 1;
|
|
1374
|
+
while (endIndex < dirents.length && dirents[endIndex].depth > dirent.depth) {
|
|
1375
|
+
endIndex++;
|
|
1376
|
+
}
|
|
1377
|
+
return endIndex;
|
|
1378
|
+
};
|
|
1379
|
+
|
|
1380
|
+
const makeExpanded = dirent => {
|
|
1381
|
+
if (dirent.type === Directory) {
|
|
1382
|
+
return {
|
|
1383
|
+
...dirent,
|
|
1384
|
+
type: DirectoryExpanded
|
|
1385
|
+
};
|
|
1386
|
+
}
|
|
1387
|
+
return dirent;
|
|
1388
|
+
};
|
|
1389
|
+
const expandRecursively = async state => {
|
|
1390
|
+
const {
|
|
1391
|
+
items,
|
|
1392
|
+
focusedIndex,
|
|
1393
|
+
pathSeparator,
|
|
1394
|
+
root,
|
|
1395
|
+
height,
|
|
1396
|
+
itemHeight,
|
|
1397
|
+
minLineY
|
|
1398
|
+
} = state;
|
|
1399
|
+
const dirent = focusedIndex < 0 ? {
|
|
1400
|
+
type: Directory,
|
|
1401
|
+
path: root,
|
|
1402
|
+
depth: 0
|
|
1403
|
+
} : items[focusedIndex];
|
|
1404
|
+
if (dirent.type !== Directory && dirent.type !== DirectoryExpanding && dirent.type !== DirectoryExpanded) {
|
|
1405
|
+
return state;
|
|
1406
|
+
}
|
|
1407
|
+
// TODO this is very inefficient
|
|
1408
|
+
const getChildDirentsRecursively = async dirent => {
|
|
1409
|
+
switch (dirent.type) {
|
|
1410
|
+
case File:
|
|
1411
|
+
return [dirent];
|
|
1412
|
+
case Directory:
|
|
1413
|
+
case DirectoryExpanding:
|
|
1414
|
+
case DirectoryExpanded:
|
|
1415
|
+
const childDirents = await getChildDirents(pathSeparator, dirent);
|
|
1416
|
+
const all = [makeExpanded(dirent)];
|
|
1417
|
+
for (const childDirent of childDirents) {
|
|
1418
|
+
const childAll = await getChildDirentsRecursively(childDirent);
|
|
1419
|
+
all.push(...childAll);
|
|
1420
|
+
}
|
|
1421
|
+
return all;
|
|
1422
|
+
default:
|
|
1423
|
+
return [];
|
|
1424
|
+
}
|
|
1425
|
+
};
|
|
1426
|
+
// TODO race condition: what if folder is being collapse while it is recursively expanding?
|
|
1427
|
+
// TODO race condition: what if folder is being deleted while it is recursively expanding?
|
|
1428
|
+
// TODO race condition: what if a new file/folder is created while the folder is recursively expanding?
|
|
1429
|
+
const childDirents = await getChildDirentsRecursively(dirent);
|
|
1430
|
+
const startIndex = focusedIndex;
|
|
1431
|
+
if (focusedIndex >= 0) {
|
|
1432
|
+
const endIndex = getParentEndIndex(items, focusedIndex);
|
|
1433
|
+
const newDirents = [...items.slice(0, startIndex), ...childDirents, ...items.slice(endIndex)];
|
|
1434
|
+
const maxLineY = getExplorerMaxLineY(minLineY, height, itemHeight, newDirents.length);
|
|
1435
|
+
return {
|
|
1436
|
+
...state,
|
|
1437
|
+
items: newDirents,
|
|
1438
|
+
maxLineY
|
|
1439
|
+
};
|
|
1440
|
+
}
|
|
1441
|
+
const newDirents = childDirents.slice(1);
|
|
1442
|
+
const maxLineY = getExplorerMaxLineY(minLineY, height, itemHeight, newDirents.length);
|
|
1443
|
+
return {
|
|
1444
|
+
...state,
|
|
1445
|
+
items: newDirents,
|
|
1446
|
+
maxLineY
|
|
1447
|
+
};
|
|
1448
|
+
};
|
|
1449
|
+
|
|
1450
|
+
const focusIndex = (state, index) => {
|
|
1451
|
+
const {
|
|
1452
|
+
minLineY,
|
|
1453
|
+
maxLineY
|
|
1454
|
+
} = state;
|
|
1455
|
+
if (index < minLineY) {
|
|
1356
1456
|
if (index < 0) {
|
|
1357
1457
|
return {
|
|
1358
1458
|
...state,
|
|
@@ -1442,7 +1542,7 @@ const focusPrevious = state => {
|
|
|
1442
1542
|
}
|
|
1443
1543
|
};
|
|
1444
1544
|
|
|
1445
|
-
const None = 'none';
|
|
1545
|
+
const None$2 = 'none';
|
|
1446
1546
|
const Tree = 'tree';
|
|
1447
1547
|
const TreeItem$1 = 'treeitem';
|
|
1448
1548
|
|
|
@@ -1501,6 +1601,39 @@ const UiStrings = {
|
|
|
1501
1601
|
OpenFolder: 'Open folder',
|
|
1502
1602
|
NoFolderOpen: 'No Folder Open'
|
|
1503
1603
|
};
|
|
1604
|
+
const newFile = () => {
|
|
1605
|
+
return i18nString(UiStrings.NewFile);
|
|
1606
|
+
};
|
|
1607
|
+
const newFolder = () => {
|
|
1608
|
+
return i18nString(UiStrings.NewFolder);
|
|
1609
|
+
};
|
|
1610
|
+
const openContainingFolder$1 = () => {
|
|
1611
|
+
return i18nString(UiStrings.OpenContainingFolder);
|
|
1612
|
+
};
|
|
1613
|
+
const openInIntegratedTerminal = () => {
|
|
1614
|
+
return i18nString(UiStrings.OpenInIntegratedTerminal);
|
|
1615
|
+
};
|
|
1616
|
+
const cut = () => {
|
|
1617
|
+
return i18nString(UiStrings.Cut);
|
|
1618
|
+
};
|
|
1619
|
+
const copy = () => {
|
|
1620
|
+
return i18nString(UiStrings.Copy);
|
|
1621
|
+
};
|
|
1622
|
+
const paste = () => {
|
|
1623
|
+
return i18nString(UiStrings.Paste);
|
|
1624
|
+
};
|
|
1625
|
+
const copyPath = () => {
|
|
1626
|
+
return i18nString(UiStrings.CopyPath);
|
|
1627
|
+
};
|
|
1628
|
+
const copyRelativePath = () => {
|
|
1629
|
+
return i18nString(UiStrings.CopyRelativePath);
|
|
1630
|
+
};
|
|
1631
|
+
const rename = () => {
|
|
1632
|
+
return i18nString(UiStrings.Rename);
|
|
1633
|
+
};
|
|
1634
|
+
const deleteItem = () => {
|
|
1635
|
+
return i18nString(UiStrings.Delete);
|
|
1636
|
+
};
|
|
1504
1637
|
const filesExplorer = () => {
|
|
1505
1638
|
return i18nString(UiStrings.FilesExplorer);
|
|
1506
1639
|
};
|
|
@@ -1523,7 +1656,7 @@ const getFileIconVirtualDom = icon => {
|
|
|
1523
1656
|
type: Img,
|
|
1524
1657
|
className: FileIcon,
|
|
1525
1658
|
src: icon,
|
|
1526
|
-
role: None,
|
|
1659
|
+
role: None$2,
|
|
1527
1660
|
childCount: 0
|
|
1528
1661
|
};
|
|
1529
1662
|
};
|
|
@@ -1805,6 +1938,121 @@ const getKeyBindings = () => {
|
|
|
1805
1938
|
}];
|
|
1806
1939
|
};
|
|
1807
1940
|
|
|
1941
|
+
const Separator = 1;
|
|
1942
|
+
const None$1 = 0;
|
|
1943
|
+
const RestoreFocus = 6;
|
|
1944
|
+
|
|
1945
|
+
const menuEntrySeparator = {
|
|
1946
|
+
id: 'separator',
|
|
1947
|
+
label: '',
|
|
1948
|
+
flags: Separator,
|
|
1949
|
+
command: ''
|
|
1950
|
+
};
|
|
1951
|
+
|
|
1952
|
+
const menuEntryNewFile = {
|
|
1953
|
+
id: 'newFile',
|
|
1954
|
+
label: newFile(),
|
|
1955
|
+
flags: None$1,
|
|
1956
|
+
command: 'Explorer.newFile'
|
|
1957
|
+
};
|
|
1958
|
+
const menuEntryNewFolder = {
|
|
1959
|
+
id: 'newFolder',
|
|
1960
|
+
label: newFolder(),
|
|
1961
|
+
flags: None$1,
|
|
1962
|
+
command: 'Explorer.newFolder'
|
|
1963
|
+
};
|
|
1964
|
+
const menuEntryOpenContainingFolder = {
|
|
1965
|
+
id: 'openContainingFolder',
|
|
1966
|
+
label: openContainingFolder$1(),
|
|
1967
|
+
flags: RestoreFocus,
|
|
1968
|
+
command: 'Explorer.openContainingFolder'
|
|
1969
|
+
};
|
|
1970
|
+
const menuEntryOpenInIntegratedTerminal = {
|
|
1971
|
+
id: 'openInIntegratedTerminal',
|
|
1972
|
+
label: openInIntegratedTerminal(),
|
|
1973
|
+
flags: None$1,
|
|
1974
|
+
command: /* TODO */-1
|
|
1975
|
+
};
|
|
1976
|
+
const menuEntryCut = {
|
|
1977
|
+
id: 'cut',
|
|
1978
|
+
label: cut(),
|
|
1979
|
+
flags: RestoreFocus,
|
|
1980
|
+
command: /* TODO */-1
|
|
1981
|
+
};
|
|
1982
|
+
const menuEntryCopy = {
|
|
1983
|
+
id: 'copy',
|
|
1984
|
+
label: copy(),
|
|
1985
|
+
flags: RestoreFocus,
|
|
1986
|
+
command: 'Explorer.handleCopy'
|
|
1987
|
+
};
|
|
1988
|
+
const menuEntryPaste = {
|
|
1989
|
+
id: 'paste',
|
|
1990
|
+
label: paste(),
|
|
1991
|
+
flags: None$1,
|
|
1992
|
+
command: 'Explorer.handlePaste'
|
|
1993
|
+
};
|
|
1994
|
+
const menuEntryCopyPath = {
|
|
1995
|
+
id: 'copyPath',
|
|
1996
|
+
label: copyPath(),
|
|
1997
|
+
flags: RestoreFocus,
|
|
1998
|
+
command: 'Explorer.copyPath'
|
|
1999
|
+
};
|
|
2000
|
+
const menuEntryCopyRelativePath = {
|
|
2001
|
+
id: 'copyRelativePath',
|
|
2002
|
+
label: copyRelativePath(),
|
|
2003
|
+
flags: RestoreFocus,
|
|
2004
|
+
command: 'Explorer.copyRelativePath'
|
|
2005
|
+
};
|
|
2006
|
+
const menuEntryRename = {
|
|
2007
|
+
id: 'rename',
|
|
2008
|
+
label: rename(),
|
|
2009
|
+
flags: None$1,
|
|
2010
|
+
command: 'Explorer.renameDirent'
|
|
2011
|
+
};
|
|
2012
|
+
const menuEntryDelete = {
|
|
2013
|
+
id: 'delete',
|
|
2014
|
+
label: deleteItem(),
|
|
2015
|
+
flags: None$1,
|
|
2016
|
+
command: 'Explorer.removeDirent'
|
|
2017
|
+
};
|
|
2018
|
+
const ALL_ENTRIES = [menuEntryNewFile, menuEntryNewFolder, menuEntryOpenContainingFolder, menuEntryOpenInIntegratedTerminal, menuEntrySeparator, menuEntryCut, menuEntryCopy, menuEntryPaste, menuEntrySeparator, menuEntryCopyPath, menuEntryCopyRelativePath, menuEntrySeparator, menuEntryRename, menuEntryDelete];
|
|
2019
|
+
|
|
2020
|
+
// TODO there are two possible ways of getting the focused dirent of explorer
|
|
2021
|
+
// 1. directly access state of explorer (bad because it directly accesses state of another component)
|
|
2022
|
+
// 2. expose getFocusedDirent method in explorer (bad because explorer code should not know about for menuEntriesExplorer, which needs that method)
|
|
2023
|
+
const getFocusedDirent = explorerState => {
|
|
2024
|
+
if (!explorerState || explorerState.focusedIndex < 0) {
|
|
2025
|
+
return undefined;
|
|
2026
|
+
}
|
|
2027
|
+
return explorerState.items[explorerState.focusedIndex];
|
|
2028
|
+
};
|
|
2029
|
+
const getMenuEntriesDirectory = () => {
|
|
2030
|
+
return ALL_ENTRIES;
|
|
2031
|
+
};
|
|
2032
|
+
const getMenuEntriesFile = () => {
|
|
2033
|
+
return [menuEntryOpenContainingFolder, menuEntryOpenInIntegratedTerminal, menuEntrySeparator, menuEntryCut, menuEntryCopy, menuEntryPaste, menuEntrySeparator, menuEntryCopyPath, menuEntryCopyRelativePath, menuEntrySeparator, menuEntryRename, menuEntryDelete];
|
|
2034
|
+
};
|
|
2035
|
+
const getMenuEntriesDefault = () => {
|
|
2036
|
+
return ALL_ENTRIES;
|
|
2037
|
+
};
|
|
2038
|
+
const getMenuEntriesRoot = () => {
|
|
2039
|
+
return [menuEntryNewFile, menuEntryNewFolder, menuEntryOpenContainingFolder, menuEntryOpenInIntegratedTerminal, menuEntrySeparator, menuEntryPaste, menuEntrySeparator, menuEntryCopyPath, menuEntryCopyRelativePath];
|
|
2040
|
+
};
|
|
2041
|
+
const getMenuEntries = state => {
|
|
2042
|
+
const focusedDirent = getFocusedDirent(state);
|
|
2043
|
+
if (!focusedDirent) {
|
|
2044
|
+
return getMenuEntriesRoot();
|
|
2045
|
+
}
|
|
2046
|
+
switch (focusedDirent.type) {
|
|
2047
|
+
case Directory:
|
|
2048
|
+
return getMenuEntriesDirectory();
|
|
2049
|
+
case File:
|
|
2050
|
+
return getMenuEntriesFile();
|
|
2051
|
+
default:
|
|
2052
|
+
return getMenuEntriesDefault();
|
|
2053
|
+
}
|
|
2054
|
+
};
|
|
2055
|
+
|
|
1808
2056
|
const getVisibleExplorerItems = (items, minLineY, maxLineY, focusedIndex, editingIndex, editingType, editingValue) => {
|
|
1809
2057
|
const visible = [];
|
|
1810
2058
|
for (let i = minLineY; i < Math.min(maxLineY, items.length); i++) {
|
|
@@ -1825,7 +2073,7 @@ const getVisibleExplorerItems = (items, minLineY, maxLineY, focusedIndex, editin
|
|
|
1825
2073
|
});
|
|
1826
2074
|
}
|
|
1827
2075
|
}
|
|
1828
|
-
if (editingType !== None$
|
|
2076
|
+
if (editingType !== None$3 && editingIndex === -1) {
|
|
1829
2077
|
visible.push({
|
|
1830
2078
|
depth: 3,
|
|
1831
2079
|
posInSet: 1,
|
|
@@ -1841,14 +2089,182 @@ const getVisibleExplorerItems = (items, minLineY, maxLineY, focusedIndex, editin
|
|
|
1841
2089
|
return visible;
|
|
1842
2090
|
};
|
|
1843
2091
|
|
|
1844
|
-
const
|
|
2092
|
+
const handleBlur = state => {
|
|
2093
|
+
// TODO when blur event occurs because of context menu, focused index should stay the same
|
|
2094
|
+
// but focus outline should be removed
|
|
2095
|
+
const {
|
|
2096
|
+
editingType
|
|
2097
|
+
} = state;
|
|
2098
|
+
if (editingType !== None$3) {
|
|
2099
|
+
return state;
|
|
2100
|
+
}
|
|
2101
|
+
return {
|
|
2102
|
+
...state,
|
|
2103
|
+
focused: false
|
|
2104
|
+
};
|
|
2105
|
+
};
|
|
2106
|
+
|
|
2107
|
+
// TODO viewlet should only have create and refresh functions
|
|
2108
|
+
// every thing else can be in a separate module <viewlet>.lazy.js
|
|
2109
|
+
// and <viewlet>.ipc.js
|
|
2110
|
+
|
|
2111
|
+
// viewlet: creating | refreshing | done | disposed
|
|
2112
|
+
// TODO recycle viewlets (maybe)
|
|
2113
|
+
|
|
2114
|
+
// TODO instead of root string, there should be a root dirent
|
|
2115
|
+
|
|
2116
|
+
// TODO rename dirents to items, then can use virtual list component directly
|
|
2117
|
+
|
|
2118
|
+
// TODO support multiselection and removing multiple dirents
|
|
2119
|
+
|
|
2120
|
+
// TODO use posInSet and setSize properties to compute more effectively
|
|
2121
|
+
|
|
2122
|
+
// TODO much shared logic with newFolder
|
|
2123
|
+
|
|
2124
|
+
const handleClickFile$1 = async (state, dirent, index, keepFocus = false) => {
|
|
2125
|
+
// await Command.execute(/* Main.openAbsolutePath */ 'Main.openUri', /* absolutePath */ dirent.path, /* focus */ !keepFocus)
|
|
2126
|
+
return {
|
|
2127
|
+
...state,
|
|
2128
|
+
focusedIndex: index,
|
|
2129
|
+
focused: keepFocus
|
|
2130
|
+
};
|
|
2131
|
+
};
|
|
2132
|
+
const handleClickDirectory$1 = async (state, dirent, index, keepFocus) => {
|
|
2133
|
+
dirent.type = DirectoryExpanding;
|
|
2134
|
+
// TODO handle error
|
|
2135
|
+
const dirents = await getChildDirents(state.pathSeparator, dirent);
|
|
2136
|
+
const state2 = {};
|
|
2137
|
+
if (!state2) {
|
|
2138
|
+
return state;
|
|
2139
|
+
}
|
|
2140
|
+
// TODO use Viewlet.getState here and check if it exists
|
|
2141
|
+
const newIndex = state2.items.indexOf(dirent);
|
|
2142
|
+
// TODO if viewlet is disposed or root has changed, return
|
|
2143
|
+
if (newIndex === -1) {
|
|
2144
|
+
return state;
|
|
2145
|
+
}
|
|
2146
|
+
const newDirents = [...state2.items];
|
|
2147
|
+
newDirents.splice(newIndex + 1, 0, ...dirents);
|
|
2148
|
+
dirent.type = DirectoryExpanded;
|
|
2149
|
+
dirent.icon = getIcon();
|
|
2150
|
+
const {
|
|
2151
|
+
height,
|
|
2152
|
+
itemHeight,
|
|
2153
|
+
minLineY
|
|
2154
|
+
} = state2;
|
|
2155
|
+
// TODO when focused index has changed while expanding, don't update it
|
|
2156
|
+
const maxLineY = getExplorerMaxLineY(minLineY, height, itemHeight, newDirents.length);
|
|
2157
|
+
return {
|
|
2158
|
+
...state,
|
|
2159
|
+
items: newDirents,
|
|
2160
|
+
focusedIndex: newIndex,
|
|
2161
|
+
focused: keepFocus,
|
|
2162
|
+
maxLineY
|
|
2163
|
+
};
|
|
2164
|
+
};
|
|
2165
|
+
const handleClickDirectoryExpanding = (state, dirent, index, keepFocus) => {
|
|
2166
|
+
dirent.type = Directory;
|
|
2167
|
+
dirent.icon = getIcon();
|
|
2168
|
+
return {
|
|
2169
|
+
...state,
|
|
2170
|
+
focusedIndex: index,
|
|
2171
|
+
focused: keepFocus
|
|
2172
|
+
};
|
|
2173
|
+
};
|
|
2174
|
+
const handleClickDirectoryExpanded$1 = (state, dirent, index, keepFocus) => {
|
|
1845
2175
|
const {
|
|
1846
|
-
focusedIndex,
|
|
1847
2176
|
minLineY,
|
|
2177
|
+
maxLineY,
|
|
2178
|
+
itemHeight
|
|
2179
|
+
} = state;
|
|
2180
|
+
dirent.type = Directory;
|
|
2181
|
+
dirent.icon = getIcon();
|
|
2182
|
+
const endIndex = getParentEndIndex(state.items, index);
|
|
2183
|
+
const removeCount = endIndex - index - 1;
|
|
2184
|
+
// TODO race conditions and side effects are everywhere
|
|
2185
|
+
const newDirents = [...state.items];
|
|
2186
|
+
newDirents.splice(index + 1, removeCount);
|
|
2187
|
+
const newTotal = newDirents.length;
|
|
2188
|
+
if (newTotal < maxLineY) {
|
|
2189
|
+
const visibleItems = Math.min(maxLineY - minLineY, newTotal);
|
|
2190
|
+
const newMaxLineY = Math.min(maxLineY, newTotal);
|
|
2191
|
+
const newMinLineY = newMaxLineY - visibleItems;
|
|
2192
|
+
const deltaY = newMinLineY * itemHeight;
|
|
2193
|
+
return {
|
|
2194
|
+
...state,
|
|
2195
|
+
items: newDirents,
|
|
2196
|
+
focusedIndex: index,
|
|
2197
|
+
focused: keepFocus,
|
|
2198
|
+
minLineY: newMinLineY,
|
|
2199
|
+
maxLineY: newMaxLineY,
|
|
2200
|
+
deltaY
|
|
2201
|
+
};
|
|
2202
|
+
}
|
|
2203
|
+
return {
|
|
2204
|
+
...state,
|
|
2205
|
+
items: newDirents,
|
|
2206
|
+
focusedIndex: index,
|
|
2207
|
+
focused: keepFocus
|
|
2208
|
+
};
|
|
2209
|
+
};
|
|
2210
|
+
const handleClickSymLink$1 = async (state, dirent, index) => {
|
|
2211
|
+
await getRealPath();
|
|
2212
|
+
const type = await stat();
|
|
2213
|
+
switch (type) {
|
|
2214
|
+
case File:
|
|
2215
|
+
return handleClickFile$1(state, dirent, index);
|
|
2216
|
+
default:
|
|
2217
|
+
throw new Error(`unsupported file type ${type}`);
|
|
2218
|
+
}
|
|
2219
|
+
};
|
|
2220
|
+
const getClickFn = direntType => {
|
|
2221
|
+
switch (direntType) {
|
|
2222
|
+
case File:
|
|
2223
|
+
case SymLinkFile:
|
|
2224
|
+
return handleClickFile$1;
|
|
2225
|
+
case Directory:
|
|
2226
|
+
case SymLinkFolder:
|
|
2227
|
+
return handleClickDirectory$1;
|
|
2228
|
+
case DirectoryExpanding:
|
|
2229
|
+
return handleClickDirectoryExpanding;
|
|
2230
|
+
case DirectoryExpanded:
|
|
2231
|
+
return handleClickDirectoryExpanded$1;
|
|
2232
|
+
case Symlink:
|
|
2233
|
+
return handleClickSymLink$1;
|
|
2234
|
+
case CharacterDevice:
|
|
2235
|
+
throw new Error('Cannot open character device files');
|
|
2236
|
+
case BlockDevice:
|
|
2237
|
+
throw new Error('Cannot open block device files');
|
|
2238
|
+
case Socket:
|
|
2239
|
+
throw new Error('Cannot open socket files');
|
|
2240
|
+
default:
|
|
2241
|
+
throw new Error(`unsupported dirent type ${direntType}`);
|
|
2242
|
+
}
|
|
2243
|
+
};
|
|
2244
|
+
|
|
2245
|
+
const getIndexFromPosition = (state, eventX, eventY) => {
|
|
2246
|
+
const {
|
|
2247
|
+
y,
|
|
2248
|
+
itemHeight,
|
|
1848
2249
|
items
|
|
1849
2250
|
} = state;
|
|
1850
|
-
const
|
|
1851
|
-
|
|
2251
|
+
const index = Math.floor((eventY - y) / itemHeight);
|
|
2252
|
+
if (index < 0) {
|
|
2253
|
+
return 0;
|
|
2254
|
+
}
|
|
2255
|
+
if (index >= items.length) {
|
|
2256
|
+
return -1;
|
|
2257
|
+
}
|
|
2258
|
+
return index;
|
|
2259
|
+
};
|
|
2260
|
+
|
|
2261
|
+
const getParentStartIndex = (dirents, index) => {
|
|
2262
|
+
const dirent = dirents[index];
|
|
2263
|
+
let startIndex = index - 1;
|
|
2264
|
+
while (startIndex >= 0 && dirents[startIndex].depth >= dirent.depth) {
|
|
2265
|
+
startIndex--;
|
|
2266
|
+
}
|
|
2267
|
+
return startIndex;
|
|
1852
2268
|
};
|
|
1853
2269
|
|
|
1854
2270
|
const Keyboard = -1;
|
|
@@ -1917,7 +2333,7 @@ const removeDirent = async state => {
|
|
|
1917
2333
|
if (state.focusedIndex < 0) {
|
|
1918
2334
|
return state;
|
|
1919
2335
|
}
|
|
1920
|
-
const dirent = getFocusedDirent(state);
|
|
2336
|
+
const dirent = getFocusedDirent$1(state);
|
|
1921
2337
|
const absolutePath = dirent.path;
|
|
1922
2338
|
try {
|
|
1923
2339
|
// TODO handle error
|
|
@@ -1992,7 +2408,7 @@ const cancelEdit = state => {
|
|
|
1992
2408
|
focused: true,
|
|
1993
2409
|
editingIndex: -1,
|
|
1994
2410
|
editingValue: '',
|
|
1995
|
-
editingType: None$
|
|
2411
|
+
editingType: None$3
|
|
1996
2412
|
};
|
|
1997
2413
|
};
|
|
1998
2414
|
|
|
@@ -2039,15 +2455,6 @@ const handleClickDirectory = async (state, dirent, index, keepFocus) => {
|
|
|
2039
2455
|
maxLineY
|
|
2040
2456
|
};
|
|
2041
2457
|
};
|
|
2042
|
-
const handleClickDirectoryExpanding = (state, dirent, index, keepFocus) => {
|
|
2043
|
-
dirent.type = Directory;
|
|
2044
|
-
dirent.icon = getIcon();
|
|
2045
|
-
return {
|
|
2046
|
-
...state,
|
|
2047
|
-
focusedIndex: index,
|
|
2048
|
-
focused: keepFocus
|
|
2049
|
-
};
|
|
2050
|
-
};
|
|
2051
2458
|
const handleClickDirectoryExpanded = (state, dirent, index, keepFocus) => {
|
|
2052
2459
|
const {
|
|
2053
2460
|
minLineY,
|
|
@@ -2084,30 +2491,6 @@ const handleClickDirectoryExpanded = (state, dirent, index, keepFocus) => {
|
|
|
2084
2491
|
focused: keepFocus
|
|
2085
2492
|
};
|
|
2086
2493
|
};
|
|
2087
|
-
const getClickFn = direntType => {
|
|
2088
|
-
switch (direntType) {
|
|
2089
|
-
case File:
|
|
2090
|
-
case SymLinkFile:
|
|
2091
|
-
return handleClickFile;
|
|
2092
|
-
case Directory:
|
|
2093
|
-
case SymLinkFolder:
|
|
2094
|
-
return handleClickDirectory;
|
|
2095
|
-
case DirectoryExpanding:
|
|
2096
|
-
return handleClickDirectoryExpanding;
|
|
2097
|
-
case DirectoryExpanded:
|
|
2098
|
-
return handleClickDirectoryExpanded;
|
|
2099
|
-
case Symlink:
|
|
2100
|
-
return handleClickSymLink;
|
|
2101
|
-
case CharacterDevice:
|
|
2102
|
-
throw new Error('Cannot open character device files');
|
|
2103
|
-
case BlockDevice:
|
|
2104
|
-
throw new Error('Cannot open block device files');
|
|
2105
|
-
case Socket:
|
|
2106
|
-
throw new Error('Cannot open socket files');
|
|
2107
|
-
default:
|
|
2108
|
-
throw new Error(`unsupported dirent type ${direntType}`);
|
|
2109
|
-
}
|
|
2110
|
-
};
|
|
2111
2494
|
const handleClick = (state, index, keepFocus = false) => {
|
|
2112
2495
|
const {
|
|
2113
2496
|
items,
|
|
@@ -2219,21 +2602,6 @@ const handleArrowLeft = state => {
|
|
|
2219
2602
|
// TODO what happens when mouse leave and anther mouse enter event occur?
|
|
2220
2603
|
// should update preview instead of closing and reopening
|
|
2221
2604
|
|
|
2222
|
-
const handleBlur = state => {
|
|
2223
|
-
// TODO when blur event occurs because of context menu, focused index should stay the same
|
|
2224
|
-
// but focus outline should be removed
|
|
2225
|
-
const {
|
|
2226
|
-
editingType
|
|
2227
|
-
} = state;
|
|
2228
|
-
if (editingType !== None$1) {
|
|
2229
|
-
return state;
|
|
2230
|
-
}
|
|
2231
|
-
return {
|
|
2232
|
-
...state,
|
|
2233
|
-
focused: false
|
|
2234
|
-
};
|
|
2235
|
-
};
|
|
2236
|
-
|
|
2237
2605
|
// TODO maybe just insert items into explorer and refresh whole explorer
|
|
2238
2606
|
|
|
2239
2607
|
const handleClickOpenFolder = async state => {
|
|
@@ -2241,18 +2609,6 @@ const handleClickOpenFolder = async state => {
|
|
|
2241
2609
|
return state;
|
|
2242
2610
|
};
|
|
2243
2611
|
|
|
2244
|
-
const state = {
|
|
2245
|
-
rpc: undefined
|
|
2246
|
-
};
|
|
2247
|
-
const invoke = (method, ...params) => {
|
|
2248
|
-
const rpc = state.rpc;
|
|
2249
|
-
// @ts-ignore
|
|
2250
|
-
return rpc.invoke(method, ...params);
|
|
2251
|
-
};
|
|
2252
|
-
const setRpc = rpc => {
|
|
2253
|
-
state.rpc = rpc;
|
|
2254
|
-
};
|
|
2255
|
-
|
|
2256
2612
|
const show = async (x, y, id, ...args) => {
|
|
2257
2613
|
return invoke('ContextMenu.show', x, y, id, ...args);
|
|
2258
2614
|
};
|
|
@@ -2294,18 +2650,321 @@ const handleContextMenu = (state, button, x, y) => {
|
|
|
2294
2650
|
}
|
|
2295
2651
|
};
|
|
2296
2652
|
|
|
2297
|
-
const
|
|
2298
|
-
|
|
2653
|
+
const getFilePathElectron = async file => {
|
|
2654
|
+
return invoke('GetFilePathElectron.getFilePathElectron', file);
|
|
2299
2655
|
};
|
|
2300
2656
|
|
|
2301
|
-
const
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2657
|
+
const mergeDirents$2 = (oldDirents, newDirents) => {
|
|
2658
|
+
return newDirents;
|
|
2659
|
+
};
|
|
2660
|
+
|
|
2661
|
+
// TODO copy files in parallel
|
|
2662
|
+
const copyFilesElectron = async (root, pathSeparator, files) => {
|
|
2663
|
+
for (const file of files) {
|
|
2664
|
+
await getFilePathElectron(file);
|
|
2665
|
+
// const from = file.path
|
|
2666
|
+
join(pathSeparator, root, file.name);
|
|
2667
|
+
await copy$1();
|
|
2668
|
+
}
|
|
2669
|
+
};
|
|
2670
|
+
const getMergedDirents$2 = async (root, pathSeparator, dirents) => {
|
|
2671
|
+
const childDirents = await getChildDirents(pathSeparator, {
|
|
2672
|
+
path: root,
|
|
2673
|
+
depth: 0
|
|
2674
|
+
});
|
|
2675
|
+
const mergedDirents = mergeDirents$2(dirents, childDirents);
|
|
2676
|
+
return mergedDirents;
|
|
2677
|
+
};
|
|
2678
|
+
const handleDrop$2 = async (state, files) => {
|
|
2679
|
+
const {
|
|
2680
|
+
root,
|
|
2681
|
+
pathSeparator,
|
|
2682
|
+
items
|
|
2683
|
+
} = state;
|
|
2684
|
+
await copyFilesElectron(root, pathSeparator, files);
|
|
2685
|
+
const mergedDirents = await getMergedDirents$2(root, pathSeparator, items);
|
|
2686
|
+
return {
|
|
2687
|
+
...state,
|
|
2688
|
+
items: mergedDirents,
|
|
2689
|
+
dropTargets: []
|
|
2690
|
+
};
|
|
2691
|
+
};
|
|
2692
|
+
|
|
2693
|
+
const uploadFileSystemHandles = async (root, pathSeparator, fileSystemHandles) => {
|
|
2694
|
+
// TODO send to renderer worker
|
|
2695
|
+
};
|
|
2696
|
+
|
|
2697
|
+
const mergeDirents$1 = (oldDirents, newDirents) => {
|
|
2698
|
+
return newDirents;
|
|
2699
|
+
};
|
|
2700
|
+
const getMergedDirents$1 = async (root, pathSeparator, dirents) => {
|
|
2701
|
+
const childDirents = await getChildDirents(pathSeparator, {
|
|
2702
|
+
path: root,
|
|
2703
|
+
depth: 0
|
|
2704
|
+
});
|
|
2705
|
+
const mergedDirents = mergeDirents$1(dirents, childDirents);
|
|
2706
|
+
return mergedDirents;
|
|
2707
|
+
};
|
|
2708
|
+
const handleDrop$1 = async (state, files) => {
|
|
2709
|
+
const {
|
|
2710
|
+
root,
|
|
2711
|
+
pathSeparator,
|
|
2712
|
+
items
|
|
2713
|
+
} = state;
|
|
2714
|
+
const handled = await uploadFileSystemHandles();
|
|
2715
|
+
if (handled) {
|
|
2716
|
+
return state;
|
|
2717
|
+
}
|
|
2718
|
+
const mergedDirents = await getMergedDirents$1(root, pathSeparator, items);
|
|
2719
|
+
return {
|
|
2720
|
+
...state,
|
|
2721
|
+
items: mergedDirents,
|
|
2722
|
+
dropTargets: []
|
|
2723
|
+
};
|
|
2724
|
+
};
|
|
2725
|
+
|
|
2726
|
+
const getModule = isElectron => {
|
|
2727
|
+
if (isElectron) {
|
|
2728
|
+
return handleDrop$2;
|
|
2729
|
+
}
|
|
2730
|
+
return handleDrop$1;
|
|
2731
|
+
};
|
|
2732
|
+
const handleDropRoot = async (state, files) => {
|
|
2733
|
+
const fn = await getModule(state.isElectron);
|
|
2734
|
+
return fn(state, files);
|
|
2735
|
+
};
|
|
2736
|
+
|
|
2737
|
+
const getEndIndex = (items, index, dirent) => {
|
|
2738
|
+
for (let i = index + 1; i < items.length; i++) {
|
|
2739
|
+
if (items[i].depth === dirent.depth) {
|
|
2740
|
+
return i;
|
|
2741
|
+
}
|
|
2742
|
+
}
|
|
2743
|
+
return items.length;
|
|
2744
|
+
};
|
|
2745
|
+
const getMergedDirents = (items, index, dirent, childDirents) => {
|
|
2746
|
+
const startIndex = index;
|
|
2747
|
+
const endIndex = getEndIndex(items, index, dirent);
|
|
2748
|
+
const mergedDirents = [...items.slice(0, startIndex), {
|
|
2749
|
+
...dirent,
|
|
2750
|
+
type: DirectoryExpanded
|
|
2751
|
+
}, ...childDirents, ...items.slice(endIndex)];
|
|
2752
|
+
return mergedDirents;
|
|
2753
|
+
};
|
|
2754
|
+
const handleDropIntoFolder = async (state, dirent, index, files) => {
|
|
2755
|
+
const {
|
|
2756
|
+
pathSeparator,
|
|
2757
|
+
items
|
|
2758
|
+
} = state;
|
|
2759
|
+
for (const file of files) {
|
|
2760
|
+
await copy$1();
|
|
2761
|
+
}
|
|
2762
|
+
const childDirents = await getChildDirents(pathSeparator, dirent);
|
|
2763
|
+
const mergedDirents = getMergedDirents(items, index, dirent, childDirents);
|
|
2764
|
+
// TODO update maxlineY
|
|
2765
|
+
return {
|
|
2766
|
+
...state,
|
|
2767
|
+
items: mergedDirents,
|
|
2768
|
+
dropTargets: []
|
|
2769
|
+
};
|
|
2770
|
+
};
|
|
2771
|
+
const handleDropIntoFile = (state, dirent, index, files) => {
|
|
2772
|
+
const {
|
|
2773
|
+
items
|
|
2774
|
+
} = state;
|
|
2775
|
+
const parentIndex = getParentStartIndex(items, index);
|
|
2776
|
+
if (parentIndex === -1) {
|
|
2777
|
+
return handleDropRoot(state, files);
|
|
2778
|
+
}
|
|
2779
|
+
// @ts-ignore
|
|
2780
|
+
return handleDropIndex(parentIndex);
|
|
2781
|
+
};
|
|
2782
|
+
const handleDropIndex = (state, index, files) => {
|
|
2783
|
+
const {
|
|
2784
|
+
items
|
|
2785
|
+
} = state;
|
|
2786
|
+
const dirent = items[index];
|
|
2787
|
+
// TODO if it is a file, drop into the folder of the file
|
|
2788
|
+
// TODO if it is a folder, drop into the folder
|
|
2789
|
+
// TODO if it is a symlink, read symlink and determine if file can be dropped
|
|
2790
|
+
switch (dirent.type) {
|
|
2791
|
+
case Directory:
|
|
2792
|
+
case DirectoryExpanded:
|
|
2793
|
+
return handleDropIntoFolder(state, dirent, index, files);
|
|
2794
|
+
case File:
|
|
2795
|
+
return handleDropIntoFile(state, dirent, index, files);
|
|
2796
|
+
default:
|
|
2797
|
+
return state;
|
|
2798
|
+
}
|
|
2799
|
+
};
|
|
2800
|
+
|
|
2801
|
+
const normalizeLine = line => {
|
|
2802
|
+
if (line.startsWith('Error: ')) {
|
|
2803
|
+
return line.slice('Error: '.length);
|
|
2804
|
+
}
|
|
2805
|
+
if (line.startsWith('VError: ')) {
|
|
2806
|
+
return line.slice('VError: '.length);
|
|
2807
|
+
}
|
|
2808
|
+
return line;
|
|
2809
|
+
};
|
|
2810
|
+
const getCombinedMessage = (error, message) => {
|
|
2811
|
+
const stringifiedError = normalizeLine(`${error}`);
|
|
2812
|
+
if (message) {
|
|
2813
|
+
return `${message}: ${stringifiedError}`;
|
|
2814
|
+
}
|
|
2815
|
+
return stringifiedError;
|
|
2816
|
+
};
|
|
2817
|
+
const NewLine = '\n';
|
|
2818
|
+
const getNewLineIndex = (string, startIndex = undefined) => {
|
|
2819
|
+
return string.indexOf(NewLine, startIndex);
|
|
2820
|
+
};
|
|
2821
|
+
const mergeStacks = (parent, child) => {
|
|
2822
|
+
if (!child) {
|
|
2823
|
+
return parent;
|
|
2824
|
+
}
|
|
2825
|
+
const parentNewLineIndex = getNewLineIndex(parent);
|
|
2826
|
+
const childNewLineIndex = getNewLineIndex(child);
|
|
2827
|
+
if (childNewLineIndex === -1) {
|
|
2828
|
+
return parent;
|
|
2829
|
+
}
|
|
2830
|
+
const parentFirstLine = parent.slice(0, parentNewLineIndex);
|
|
2831
|
+
const childRest = child.slice(childNewLineIndex);
|
|
2832
|
+
const childFirstLine = normalizeLine(child.slice(0, childNewLineIndex));
|
|
2833
|
+
if (parentFirstLine.includes(childFirstLine)) {
|
|
2834
|
+
return parentFirstLine + childRest;
|
|
2835
|
+
}
|
|
2836
|
+
return child;
|
|
2837
|
+
};
|
|
2838
|
+
class VError extends Error {
|
|
2839
|
+
constructor(error, message) {
|
|
2840
|
+
const combinedMessage = getCombinedMessage(error, message);
|
|
2841
|
+
super(combinedMessage);
|
|
2842
|
+
this.name = 'VError';
|
|
2843
|
+
if (error instanceof Error) {
|
|
2844
|
+
this.stack = mergeStacks(this.stack, error.stack);
|
|
2845
|
+
}
|
|
2846
|
+
if (error.codeFrame) {
|
|
2847
|
+
// @ts-ignore
|
|
2848
|
+
this.codeFrame = error.codeFrame;
|
|
2849
|
+
}
|
|
2850
|
+
if (error.code) {
|
|
2851
|
+
// @ts-ignore
|
|
2852
|
+
this.code = error.code;
|
|
2853
|
+
}
|
|
2854
|
+
}
|
|
2855
|
+
}
|
|
2856
|
+
|
|
2857
|
+
const handleDrop = async (state, x, y, files) => {
|
|
2858
|
+
try {
|
|
2859
|
+
const index = getIndexFromPosition(state, x, y);
|
|
2860
|
+
switch (index) {
|
|
2861
|
+
case -1:
|
|
2862
|
+
return await handleDropRoot(state, files);
|
|
2863
|
+
default:
|
|
2864
|
+
return await handleDropIndex(state, index, files);
|
|
2865
|
+
}
|
|
2866
|
+
} catch (error) {
|
|
2867
|
+
throw new VError(error, 'Failed to drop files');
|
|
2868
|
+
}
|
|
2869
|
+
};
|
|
2870
|
+
|
|
2871
|
+
const getTopLevelDirents = (root, pathSeparator, excluded) => {
|
|
2872
|
+
if (!root) {
|
|
2873
|
+
return [];
|
|
2874
|
+
}
|
|
2875
|
+
return getChildDirents(pathSeparator, {
|
|
2876
|
+
depth: 0,
|
|
2877
|
+
path: root,
|
|
2878
|
+
type: Directory
|
|
2879
|
+
}, excluded);
|
|
2880
|
+
};
|
|
2881
|
+
|
|
2882
|
+
const mergeDirents = (oldDirents, newDirents) => {
|
|
2883
|
+
const merged = [];
|
|
2884
|
+
for (const newDirent of newDirents) {
|
|
2885
|
+
merged.push(newDirent);
|
|
2886
|
+
}
|
|
2887
|
+
return merged;
|
|
2888
|
+
};
|
|
2889
|
+
|
|
2890
|
+
// TODO add lots of tests for this
|
|
2891
|
+
const updateRoot = async state1 => {
|
|
2892
|
+
if (state1.disposed) {
|
|
2893
|
+
return state1;
|
|
2894
|
+
}
|
|
2895
|
+
// const file = nativeFiles.files[0]
|
|
2896
|
+
// @ts-ignore
|
|
2897
|
+
const topLevelDirents = await getTopLevelDirents(state1.root, state1.pathSeparator);
|
|
2898
|
+
// const state2 = Viewlet.getState('Explorer')
|
|
2899
|
+
// // TODO what if root changes while reading directories?
|
|
2900
|
+
// if (state2.disposed || state2.root !== state1.root) {
|
|
2901
|
+
// return state2
|
|
2902
|
+
// }
|
|
2903
|
+
const newDirents = mergeDirents(state1.items, topLevelDirents);
|
|
2904
|
+
const state3 = {
|
|
2905
|
+
...state1,
|
|
2906
|
+
items: newDirents
|
|
2907
|
+
};
|
|
2908
|
+
return state3;
|
|
2909
|
+
};
|
|
2910
|
+
|
|
2911
|
+
const handlePasteCopy = async (state, nativeFiles) => {
|
|
2912
|
+
// TODO handle pasting files into nested folder
|
|
2913
|
+
// TODO handle pasting files into symlink
|
|
2914
|
+
// TODO handle pasting files into broken symlink
|
|
2915
|
+
// TODO handle pasting files into hardlink
|
|
2916
|
+
// TODO what if folder is big and it takes a long time
|
|
2917
|
+
for (const source of nativeFiles.files) {
|
|
2918
|
+
join(state.pathSeperator, state.root, getBaseName(state.pathSeparator, source));
|
|
2919
|
+
await copy$1();
|
|
2920
|
+
}
|
|
2921
|
+
// TODO only update folder at which level it changed
|
|
2922
|
+
return updateRoot(state);
|
|
2923
|
+
};
|
|
2924
|
+
|
|
2925
|
+
const handlePasteCut = async (state, nativeFiles) => {
|
|
2926
|
+
for (const source of nativeFiles.files) {
|
|
2927
|
+
`${state.root}${state.pathSeparator}${getBaseName(state.pathSeparator, source)}`;
|
|
2928
|
+
await rename$1();
|
|
2929
|
+
}
|
|
2930
|
+
return state;
|
|
2931
|
+
};
|
|
2932
|
+
|
|
2933
|
+
const handlePasteNone = (state, nativeFiles) => {
|
|
2934
|
+
console.info('[ViewletExplorer/handlePaste] no paths detected');
|
|
2306
2935
|
return state;
|
|
2307
2936
|
};
|
|
2308
2937
|
|
|
2938
|
+
const None = 'none';
|
|
2939
|
+
const Copy = 'copy';
|
|
2940
|
+
const Cut = 'cut';
|
|
2941
|
+
|
|
2942
|
+
const handlePaste = async state => {
|
|
2943
|
+
const nativeFiles = await readNativeFiles();
|
|
2944
|
+
// TODO detect cut/paste event, not sure if that is possible
|
|
2945
|
+
// TODO check that pasted folder is not a parent folder of opened folder
|
|
2946
|
+
// TODO support pasting multiple paths
|
|
2947
|
+
// TODO what happens when pasting multiple paths, but some of them error?
|
|
2948
|
+
// how many error messages should be shown? Should the operation be undone?
|
|
2949
|
+
// TODO what if it is a large folder and takes a long time to copy? Should show progress
|
|
2950
|
+
// TODO what if there is a permission error? Probably should show a modal to ask for permission
|
|
2951
|
+
// TODO if error is EEXISTS, just rename the copy (e.g. file-copy-1.txt, file-copy-2.txt)
|
|
2952
|
+
// TODO actual target should be selected folder
|
|
2953
|
+
// TODO but what if a file is currently selected? Then maybe the parent folder
|
|
2954
|
+
// TODO but will it work if the folder is a symlink?
|
|
2955
|
+
// TODO handle error gracefully when copy fails
|
|
2956
|
+
switch (nativeFiles.type) {
|
|
2957
|
+
case None:
|
|
2958
|
+
return handlePasteNone(state);
|
|
2959
|
+
case Copy:
|
|
2960
|
+
return handlePasteCopy(state, nativeFiles);
|
|
2961
|
+
case Cut:
|
|
2962
|
+
return handlePasteCut(state, nativeFiles);
|
|
2963
|
+
default:
|
|
2964
|
+
throw new Error(`unexpected native paste type: ${nativeFiles.type}`);
|
|
2965
|
+
}
|
|
2966
|
+
};
|
|
2967
|
+
|
|
2309
2968
|
const handlePointerDown = (state, button, x, y) => {
|
|
2310
2969
|
const index = getIndexFromPosition(state, x, y);
|
|
2311
2970
|
if (button === LeftClick && index === -1) {
|
|
@@ -2428,9 +3087,226 @@ const restoreState = savedState => {
|
|
|
2428
3087
|
};
|
|
2429
3088
|
};
|
|
2430
3089
|
|
|
2431
|
-
const
|
|
2432
|
-
|
|
2433
|
-
|
|
3090
|
+
const getIndex = (dirents, uri) => {
|
|
3091
|
+
for (let i = 0; i < dirents.length; i++) {
|
|
3092
|
+
const dirent = dirents[i];
|
|
3093
|
+
if (dirent.path === uri) {
|
|
3094
|
+
return i;
|
|
3095
|
+
}
|
|
3096
|
+
}
|
|
3097
|
+
return -1;
|
|
3098
|
+
};
|
|
3099
|
+
|
|
3100
|
+
const getPathParts = (root, uri, pathSeparator) => {
|
|
3101
|
+
const parts = [];
|
|
3102
|
+
let index = root.length - 1;
|
|
3103
|
+
let depth = 0;
|
|
3104
|
+
while ((index = uri.indexOf('/', index + 1)) !== -1) {
|
|
3105
|
+
const partUri = uri.slice(0, index);
|
|
3106
|
+
parts.push({
|
|
3107
|
+
path: partUri,
|
|
3108
|
+
depth: depth++,
|
|
3109
|
+
root,
|
|
3110
|
+
pathSeparator
|
|
3111
|
+
});
|
|
3112
|
+
}
|
|
3113
|
+
return parts;
|
|
3114
|
+
};
|
|
3115
|
+
|
|
3116
|
+
const isTopLevel = dirent => {
|
|
3117
|
+
return dirent.depth === 1;
|
|
3118
|
+
};
|
|
3119
|
+
const orderDirents = dirents => {
|
|
3120
|
+
if (dirents.length === 0) {
|
|
3121
|
+
return dirents;
|
|
3122
|
+
}
|
|
3123
|
+
// const parentMap = Object.create(null)
|
|
3124
|
+
// for(const dirent of dirents){
|
|
3125
|
+
// const parentPath = dirent.slice(0, dirent.lastIndexOf('/'))
|
|
3126
|
+
// parentMap[parentPath]||=[]
|
|
3127
|
+
// parentMap[parentPath].push(dirent)
|
|
3128
|
+
// }
|
|
3129
|
+
const withDeepChildren = parent => {
|
|
3130
|
+
const children = [];
|
|
3131
|
+
for (const dirent of dirents) {
|
|
3132
|
+
if (dirent.depth === parent.depth + 1 && dirent.path.startsWith(parent.path)) {
|
|
3133
|
+
children.push(dirent, ...withDeepChildren(dirent));
|
|
3134
|
+
}
|
|
3135
|
+
}
|
|
3136
|
+
return [parent, ...children];
|
|
3137
|
+
};
|
|
3138
|
+
const topLevelDirents = dirents.filter(isTopLevel);
|
|
3139
|
+
const ordered = topLevelDirents.flatMap(withDeepChildren);
|
|
3140
|
+
return ordered;
|
|
3141
|
+
};
|
|
3142
|
+
|
|
3143
|
+
const scrollInto = (index, minLineY, maxLineY) => {
|
|
3144
|
+
const diff = maxLineY - minLineY;
|
|
3145
|
+
const smallerHalf = Math.floor(diff / 2);
|
|
3146
|
+
const largerHalf = diff - smallerHalf;
|
|
3147
|
+
if (index < minLineY) {
|
|
3148
|
+
return {
|
|
3149
|
+
newMinLineY: index - smallerHalf,
|
|
3150
|
+
newMaxLineY: index + largerHalf
|
|
3151
|
+
};
|
|
3152
|
+
}
|
|
3153
|
+
if (index >= maxLineY) {
|
|
3154
|
+
return {
|
|
3155
|
+
newMinLineY: index - smallerHalf,
|
|
3156
|
+
newMaxLineY: index + largerHalf
|
|
3157
|
+
};
|
|
3158
|
+
}
|
|
3159
|
+
return {
|
|
3160
|
+
newMinLineY: minLineY,
|
|
3161
|
+
newMaxLineY: maxLineY
|
|
3162
|
+
};
|
|
3163
|
+
};
|
|
3164
|
+
|
|
3165
|
+
const getPathPartsToReveal = (root, pathParts, dirents) => {
|
|
3166
|
+
for (let i = 0; i < pathParts.length; i++) {
|
|
3167
|
+
const pathPart = pathParts[i];
|
|
3168
|
+
const index = getIndex(dirents, pathPart.uri);
|
|
3169
|
+
if (index === -1) {
|
|
3170
|
+
continue;
|
|
3171
|
+
}
|
|
3172
|
+
return pathParts.slice(i);
|
|
3173
|
+
}
|
|
3174
|
+
return pathParts;
|
|
3175
|
+
};
|
|
3176
|
+
const getPathPartChildren = pathPart => {
|
|
3177
|
+
const children = getChildDirents(pathPart.pathSeparator, pathPart);
|
|
3178
|
+
return children;
|
|
3179
|
+
};
|
|
3180
|
+
const mergeVisibleWithHiddenItems = (visibleItems, hiddenItems) => {
|
|
3181
|
+
const merged = [...hiddenItems];
|
|
3182
|
+
const seen = Object.create(null);
|
|
3183
|
+
const unique = [];
|
|
3184
|
+
for (const item of merged) {
|
|
3185
|
+
if (seen[item.path]) {
|
|
3186
|
+
continue;
|
|
3187
|
+
}
|
|
3188
|
+
seen[item.path] = true;
|
|
3189
|
+
unique.push(item);
|
|
3190
|
+
}
|
|
3191
|
+
|
|
3192
|
+
// depth one
|
|
3193
|
+
// let depth=1
|
|
3194
|
+
// while(true){
|
|
3195
|
+
// for(const item of unique){
|
|
3196
|
+
// if(item.depth===depth){
|
|
3197
|
+
// ordered.push(item)
|
|
3198
|
+
// }
|
|
3199
|
+
// }
|
|
3200
|
+
// break
|
|
3201
|
+
// }
|
|
3202
|
+
// const getChildren = (path) => {
|
|
3203
|
+
// const children = []
|
|
3204
|
+
// for (const item of unique) {
|
|
3205
|
+
// if (item.path.startsWith(path) && item.path !== path) {
|
|
3206
|
+
// ordered.push(item)
|
|
3207
|
+
// }
|
|
3208
|
+
// }
|
|
3209
|
+
// return children
|
|
3210
|
+
// }
|
|
3211
|
+
// for (const item of unique) {
|
|
3212
|
+
// for (const potentialChild of unique) {
|
|
3213
|
+
// if (
|
|
3214
|
+
// potentialChild.path.startsWith(item.path) &&
|
|
3215
|
+
// potentialChild.path !== item.path
|
|
3216
|
+
// ) {
|
|
3217
|
+
// ordered.push(potentialChild)
|
|
3218
|
+
// }
|
|
3219
|
+
// }
|
|
3220
|
+
// }
|
|
3221
|
+
// const refreshedRoots = Object.create(null)
|
|
3222
|
+
// for (const hiddenItem of hiddenItems) {
|
|
3223
|
+
// const parent = hiddenItem.path.slice(0, hiddenItem.path.lastIndexOf('/'))
|
|
3224
|
+
// refreshedRoots[parent] = true
|
|
3225
|
+
// }
|
|
3226
|
+
// const mergedDirents = []
|
|
3227
|
+
// for(const v)
|
|
3228
|
+
// for (const visibleItem of visibleItems) {
|
|
3229
|
+
// if (visibleItem.path === hiddenItemRoot) {
|
|
3230
|
+
// // TODO update aria posinset and aria setsize
|
|
3231
|
+
// mergedDirents.push(...hiddenItems)
|
|
3232
|
+
// } else {
|
|
3233
|
+
// for (const hiddenItem of hiddenItems) {
|
|
3234
|
+
// if (hiddenItem.path === visibleItem.path) {
|
|
3235
|
+
// continue
|
|
3236
|
+
// }
|
|
3237
|
+
// }
|
|
3238
|
+
// mergedDirents.push(visibleItem)
|
|
3239
|
+
// }
|
|
3240
|
+
// }
|
|
3241
|
+
|
|
3242
|
+
return unique;
|
|
3243
|
+
};
|
|
3244
|
+
|
|
3245
|
+
// TODO maybe just insert items into explorer and refresh whole explorer
|
|
3246
|
+
const revealItemHidden = async (state, uri) => {
|
|
3247
|
+
const {
|
|
3248
|
+
root,
|
|
3249
|
+
pathSeparator,
|
|
3250
|
+
items,
|
|
3251
|
+
minLineY,
|
|
3252
|
+
maxLineY
|
|
3253
|
+
} = state;
|
|
3254
|
+
const pathParts = getPathParts(root, uri, pathSeparator);
|
|
3255
|
+
if (pathParts.length === 0) {
|
|
3256
|
+
return state;
|
|
3257
|
+
}
|
|
3258
|
+
const pathPartsToReveal = getPathPartsToReveal(root, pathParts, items);
|
|
3259
|
+
const pathPartsChildren = await Promise.all(pathPartsToReveal.map(getPathPartChildren));
|
|
3260
|
+
const pathPartsChildrenFlat = pathPartsChildren.flat(1);
|
|
3261
|
+
const orderedPathParts = orderDirents(pathPartsChildrenFlat);
|
|
3262
|
+
const mergedDirents = mergeVisibleWithHiddenItems(items, orderedPathParts);
|
|
3263
|
+
const index = getIndex(mergedDirents, uri);
|
|
3264
|
+
if (index === -1) {
|
|
3265
|
+
throw new Error(`File not found in explorer ${uri}`);
|
|
3266
|
+
}
|
|
3267
|
+
const {
|
|
3268
|
+
newMinLineY,
|
|
3269
|
+
newMaxLineY
|
|
3270
|
+
} = scrollInto(index, minLineY, maxLineY);
|
|
3271
|
+
return {
|
|
3272
|
+
...state,
|
|
3273
|
+
items: mergedDirents,
|
|
3274
|
+
focused: true,
|
|
3275
|
+
focusedIndex: index,
|
|
3276
|
+
minLineY: newMinLineY,
|
|
3277
|
+
maxLineY: newMaxLineY
|
|
3278
|
+
};
|
|
3279
|
+
};
|
|
3280
|
+
|
|
3281
|
+
const revealItemVisible = (state, index) => {
|
|
3282
|
+
const {
|
|
3283
|
+
minLineY,
|
|
3284
|
+
maxLineY
|
|
3285
|
+
} = state;
|
|
3286
|
+
const {
|
|
3287
|
+
newMinLineY,
|
|
3288
|
+
newMaxLineY
|
|
3289
|
+
} = scrollInto(index, minLineY, maxLineY);
|
|
3290
|
+
return {
|
|
3291
|
+
...state,
|
|
3292
|
+
focused: true,
|
|
3293
|
+
focusedIndex: index,
|
|
3294
|
+
minLineY: newMinLineY,
|
|
3295
|
+
maxLineY: newMaxLineY
|
|
3296
|
+
};
|
|
3297
|
+
};
|
|
3298
|
+
|
|
3299
|
+
const revealItem = async (state, uri) => {
|
|
3300
|
+
object(state);
|
|
3301
|
+
string(uri);
|
|
3302
|
+
const {
|
|
3303
|
+
items
|
|
3304
|
+
} = state;
|
|
3305
|
+
const index = getIndex(items, uri);
|
|
3306
|
+
if (index === -1) {
|
|
3307
|
+
return revealItemHidden(state, uri);
|
|
3308
|
+
}
|
|
3309
|
+
return revealItemVisible(state, index);
|
|
2434
3310
|
};
|
|
2435
3311
|
|
|
2436
3312
|
const isExpandedDirectory = dirent => {
|
|
@@ -2460,15 +3336,17 @@ const saveState = state => {
|
|
|
2460
3336
|
const commandMap = {
|
|
2461
3337
|
'Explorer.acceptEdit': acceptEdit,
|
|
2462
3338
|
'Explorer.cancelEdit': cancelEdit,
|
|
2463
|
-
'Explorer.copyPath': copyPath,
|
|
2464
|
-
'Explorer.copyRelativePath': copyRelativePath,
|
|
3339
|
+
'Explorer.copyPath': copyPath$1,
|
|
3340
|
+
'Explorer.copyRelativePath': copyRelativePath$1,
|
|
2465
3341
|
'Explorer.expandAll': expandAll,
|
|
3342
|
+
'Explorer.expandRecursively': expandRecursively,
|
|
2466
3343
|
'Explorer.focusFirst': focusFirst,
|
|
2467
3344
|
'Explorer.focusIndex': focusIndex,
|
|
2468
3345
|
'Explorer.focusLast': focusLast,
|
|
2469
3346
|
'Explorer.focusNext': focusNext,
|
|
2470
3347
|
'Explorer.focusPrevious': focusPrevious,
|
|
2471
3348
|
'Explorer.getKeyBindings': getKeyBindings,
|
|
3349
|
+
'Explorer.getMenuEntries': getMenuEntries,
|
|
2472
3350
|
'Explorer.getVirtualDom': getExplorerVirtualDom,
|
|
2473
3351
|
'Explorer.getVisibleItems': getVisibleExplorerItems,
|
|
2474
3352
|
'Explorer.handleArrowLeft': handleArrowLeft,
|
|
@@ -2479,7 +3357,9 @@ const commandMap = {
|
|
|
2479
3357
|
'Explorer.handleClickCurrentButKeepFocus': handleClickCurrentButKeepFocus,
|
|
2480
3358
|
'Explorer.handleClickOpenFolder': handleClickOpenFolder,
|
|
2481
3359
|
'Explorer.handleContextMenu': handleContextMenu,
|
|
3360
|
+
'Explorer.handleDrop': handleDrop,
|
|
2482
3361
|
'Explorer.handleIconThemeChange': handleIconThemeChange,
|
|
3362
|
+
'Explorer.handlePaste': handlePaste,
|
|
2483
3363
|
'Explorer.handlePointerDown': handlePointerDown,
|
|
2484
3364
|
'Explorer.handleWheel': handleWheel,
|
|
2485
3365
|
'Explorer.loadContent': loadContent,
|
|
@@ -2487,6 +3367,7 @@ const commandMap = {
|
|
|
2487
3367
|
'Explorer.removeDirent': removeDirent,
|
|
2488
3368
|
'Explorer.renameDirent': renameDirent,
|
|
2489
3369
|
'Explorer.restoreState': restoreState,
|
|
3370
|
+
'Explorer.revealItem': revealItem,
|
|
2490
3371
|
'Explorer.saveState': saveState
|
|
2491
3372
|
};
|
|
2492
3373
|
|