@lvce-editor/explorer-view 1.3.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 +874 -219
- 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;
|
|
@@ -956,59 +956,7 @@ const stat = async dirent => {};
|
|
|
956
956
|
const createFile = async uri => {};
|
|
957
957
|
const mkdir = async uri => {};
|
|
958
958
|
const rename$1 = 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
|
-
};
|
|
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
|
};
|
|
@@ -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
|
|
@@ -1182,6 +1136,9 @@ const setRpc = rpc => {
|
|
|
1182
1136
|
const writeText = async text => {
|
|
1183
1137
|
await invoke('ClipBoard.writeText', /* text */text);
|
|
1184
1138
|
};
|
|
1139
|
+
const readNativeFiles = async () => {
|
|
1140
|
+
return invoke('ClipBoard.readNativeFiles');
|
|
1141
|
+
};
|
|
1185
1142
|
|
|
1186
1143
|
const copyRelativePath$1 = async state => {
|
|
1187
1144
|
const dirent = getFocusedDirent$1(state);
|
|
@@ -1191,6 +1148,59 @@ const copyRelativePath$1 = async state => {
|
|
|
1191
1148
|
return state;
|
|
1192
1149
|
};
|
|
1193
1150
|
|
|
1151
|
+
class AssertionError extends Error {
|
|
1152
|
+
constructor(message) {
|
|
1153
|
+
super(message);
|
|
1154
|
+
this.name = 'AssertionError';
|
|
1155
|
+
}
|
|
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';
|
|
1177
|
+
}
|
|
1178
|
+
};
|
|
1179
|
+
const object = value => {
|
|
1180
|
+
const type = getType(value);
|
|
1181
|
+
if (type !== 'object') {
|
|
1182
|
+
throw new AssertionError('expected value to be of type object');
|
|
1183
|
+
}
|
|
1184
|
+
};
|
|
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');
|
|
1195
|
+
}
|
|
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
|
+
|
|
1194
1204
|
const isSymbolicLink = dirent => {
|
|
1195
1205
|
return dirent.type === Symlink;
|
|
1196
1206
|
};
|
|
@@ -1532,7 +1542,7 @@ const focusPrevious = state => {
|
|
|
1532
1542
|
}
|
|
1533
1543
|
};
|
|
1534
1544
|
|
|
1535
|
-
const None$
|
|
1545
|
+
const None$2 = 'none';
|
|
1536
1546
|
const Tree = 'tree';
|
|
1537
1547
|
const TreeItem$1 = 'treeitem';
|
|
1538
1548
|
|
|
@@ -1646,7 +1656,7 @@ const getFileIconVirtualDom = icon => {
|
|
|
1646
1656
|
type: Img,
|
|
1647
1657
|
className: FileIcon,
|
|
1648
1658
|
src: icon,
|
|
1649
|
-
role: None$
|
|
1659
|
+
role: None$2,
|
|
1650
1660
|
childCount: 0
|
|
1651
1661
|
};
|
|
1652
1662
|
};
|
|
@@ -1929,7 +1939,7 @@ const getKeyBindings = () => {
|
|
|
1929
1939
|
};
|
|
1930
1940
|
|
|
1931
1941
|
const Separator = 1;
|
|
1932
|
-
const None = 0;
|
|
1942
|
+
const None$1 = 0;
|
|
1933
1943
|
const RestoreFocus = 6;
|
|
1934
1944
|
|
|
1935
1945
|
const menuEntrySeparator = {
|
|
@@ -1942,13 +1952,13 @@ const menuEntrySeparator = {
|
|
|
1942
1952
|
const menuEntryNewFile = {
|
|
1943
1953
|
id: 'newFile',
|
|
1944
1954
|
label: newFile(),
|
|
1945
|
-
flags: None,
|
|
1955
|
+
flags: None$1,
|
|
1946
1956
|
command: 'Explorer.newFile'
|
|
1947
1957
|
};
|
|
1948
1958
|
const menuEntryNewFolder = {
|
|
1949
1959
|
id: 'newFolder',
|
|
1950
1960
|
label: newFolder(),
|
|
1951
|
-
flags: None,
|
|
1961
|
+
flags: None$1,
|
|
1952
1962
|
command: 'Explorer.newFolder'
|
|
1953
1963
|
};
|
|
1954
1964
|
const menuEntryOpenContainingFolder = {
|
|
@@ -1960,7 +1970,7 @@ const menuEntryOpenContainingFolder = {
|
|
|
1960
1970
|
const menuEntryOpenInIntegratedTerminal = {
|
|
1961
1971
|
id: 'openInIntegratedTerminal',
|
|
1962
1972
|
label: openInIntegratedTerminal(),
|
|
1963
|
-
flags: None,
|
|
1973
|
+
flags: None$1,
|
|
1964
1974
|
command: /* TODO */-1
|
|
1965
1975
|
};
|
|
1966
1976
|
const menuEntryCut = {
|
|
@@ -1978,7 +1988,7 @@ const menuEntryCopy = {
|
|
|
1978
1988
|
const menuEntryPaste = {
|
|
1979
1989
|
id: 'paste',
|
|
1980
1990
|
label: paste(),
|
|
1981
|
-
flags: None,
|
|
1991
|
+
flags: None$1,
|
|
1982
1992
|
command: 'Explorer.handlePaste'
|
|
1983
1993
|
};
|
|
1984
1994
|
const menuEntryCopyPath = {
|
|
@@ -1996,13 +2006,13 @@ const menuEntryCopyRelativePath = {
|
|
|
1996
2006
|
const menuEntryRename = {
|
|
1997
2007
|
id: 'rename',
|
|
1998
2008
|
label: rename(),
|
|
1999
|
-
flags: None,
|
|
2009
|
+
flags: None$1,
|
|
2000
2010
|
command: 'Explorer.renameDirent'
|
|
2001
2011
|
};
|
|
2002
2012
|
const menuEntryDelete = {
|
|
2003
2013
|
id: 'delete',
|
|
2004
2014
|
label: deleteItem(),
|
|
2005
|
-
flags: None,
|
|
2015
|
+
flags: None$1,
|
|
2006
2016
|
command: 'Explorer.removeDirent'
|
|
2007
2017
|
};
|
|
2008
2018
|
const ALL_ENTRIES = [menuEntryNewFile, menuEntryNewFolder, menuEntryOpenContainingFolder, menuEntryOpenInIntegratedTerminal, menuEntrySeparator, menuEntryCut, menuEntryCopy, menuEntryPaste, menuEntrySeparator, menuEntryCopyPath, menuEntryCopyRelativePath, menuEntrySeparator, menuEntryRename, menuEntryDelete];
|
|
@@ -2063,7 +2073,7 @@ const getVisibleExplorerItems = (items, minLineY, maxLineY, focusedIndex, editin
|
|
|
2063
2073
|
});
|
|
2064
2074
|
}
|
|
2065
2075
|
}
|
|
2066
|
-
if (editingType !== None$
|
|
2076
|
+
if (editingType !== None$3 && editingIndex === -1) {
|
|
2067
2077
|
visible.push({
|
|
2068
2078
|
depth: 3,
|
|
2069
2079
|
posInSet: 1,
|
|
@@ -2079,36 +2089,19 @@ const getVisibleExplorerItems = (items, minLineY, maxLineY, focusedIndex, editin
|
|
|
2079
2089
|
return visible;
|
|
2080
2090
|
};
|
|
2081
2091
|
|
|
2082
|
-
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
|
|
2083
2095
|
const {
|
|
2084
|
-
|
|
2085
|
-
itemHeight,
|
|
2086
|
-
items
|
|
2096
|
+
editingType
|
|
2087
2097
|
} = state;
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
return 0;
|
|
2091
|
-
}
|
|
2092
|
-
if (index >= items.length) {
|
|
2093
|
-
return -1;
|
|
2094
|
-
}
|
|
2095
|
-
return index;
|
|
2096
|
-
};
|
|
2097
|
-
|
|
2098
|
-
const getParentStartIndex = (dirents, index) => {
|
|
2099
|
-
const dirent = dirents[index];
|
|
2100
|
-
let startIndex = index - 1;
|
|
2101
|
-
while (startIndex >= 0 && dirents[startIndex].depth >= dirent.depth) {
|
|
2102
|
-
startIndex--;
|
|
2098
|
+
if (editingType !== None$3) {
|
|
2099
|
+
return state;
|
|
2103
2100
|
}
|
|
2104
|
-
return
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
const LeftClick = 0;
|
|
2109
|
-
|
|
2110
|
-
const openFolder = async () => {
|
|
2111
|
-
// TODO
|
|
2101
|
+
return {
|
|
2102
|
+
...state,
|
|
2103
|
+
focused: false
|
|
2104
|
+
};
|
|
2112
2105
|
};
|
|
2113
2106
|
|
|
2114
2107
|
// TODO viewlet should only have create and refresh functions
|
|
@@ -2120,66 +2113,236 @@ const openFolder = async () => {
|
|
|
2120
2113
|
|
|
2121
2114
|
// TODO instead of root string, there should be a root dirent
|
|
2122
2115
|
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
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)
|
|
2131
2126
|
return {
|
|
2132
2127
|
...state,
|
|
2133
|
-
|
|
2128
|
+
focusedIndex: index,
|
|
2129
|
+
focused: keepFocus
|
|
2134
2130
|
};
|
|
2135
2131
|
};
|
|
2136
|
-
const
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
itemHeight,
|
|
2144
|
-
height,
|
|
2145
|
-
items
|
|
2146
|
-
} = state;
|
|
2147
|
-
if (deltaY < 0) {
|
|
2148
|
-
deltaY = 0;
|
|
2149
|
-
} else if (deltaY > items.length * itemHeight - height) {
|
|
2150
|
-
deltaY = Math.max(items.length * itemHeight - height, 0);
|
|
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;
|
|
2151
2139
|
}
|
|
2152
|
-
|
|
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) {
|
|
2153
2144
|
return state;
|
|
2154
2145
|
}
|
|
2155
|
-
const
|
|
2156
|
-
|
|
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
2157
|
return {
|
|
2158
2158
|
...state,
|
|
2159
|
-
|
|
2160
|
-
|
|
2159
|
+
items: newDirents,
|
|
2160
|
+
focusedIndex: newIndex,
|
|
2161
|
+
focused: keepFocus,
|
|
2161
2162
|
maxLineY
|
|
2162
2163
|
};
|
|
2163
2164
|
};
|
|
2164
|
-
const
|
|
2165
|
-
|
|
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
|
+
};
|
|
2166
2173
|
};
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
}
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2174
|
+
const handleClickDirectoryExpanded$1 = (state, dirent, index, keepFocus) => {
|
|
2175
|
+
const {
|
|
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,
|
|
2249
|
+
items
|
|
2250
|
+
} = state;
|
|
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;
|
|
2268
|
+
};
|
|
2269
|
+
|
|
2270
|
+
const Keyboard = -1;
|
|
2271
|
+
const LeftClick = 0;
|
|
2272
|
+
|
|
2273
|
+
const openFolder = async () => {
|
|
2274
|
+
// TODO
|
|
2275
|
+
};
|
|
2276
|
+
|
|
2277
|
+
// TODO viewlet should only have create and refresh functions
|
|
2278
|
+
// every thing else can be in a separate module <viewlet>.lazy.js
|
|
2279
|
+
// and <viewlet>.ipc.js
|
|
2280
|
+
|
|
2281
|
+
// viewlet: creating | refreshing | done | disposed
|
|
2282
|
+
// TODO recycle viewlets (maybe)
|
|
2283
|
+
|
|
2284
|
+
// TODO instead of root string, there should be a root dirent
|
|
2285
|
+
|
|
2286
|
+
const updateIcon = dirent => {
|
|
2287
|
+
return {
|
|
2288
|
+
...dirent,
|
|
2289
|
+
icon: getIcon()
|
|
2290
|
+
};
|
|
2291
|
+
};
|
|
2292
|
+
const updateIcons = state => {
|
|
2293
|
+
const newDirents = state.items.map(updateIcon);
|
|
2294
|
+
return {
|
|
2295
|
+
...state,
|
|
2296
|
+
items: newDirents
|
|
2297
|
+
};
|
|
2298
|
+
};
|
|
2299
|
+
const handleIconThemeChange = state => {
|
|
2300
|
+
return updateIcons(state);
|
|
2301
|
+
};
|
|
2302
|
+
|
|
2303
|
+
// TODO rename dirents to items, then can use virtual list component directly
|
|
2304
|
+
const setDeltaY = (state, deltaY) => {
|
|
2305
|
+
const {
|
|
2306
|
+
itemHeight,
|
|
2307
|
+
height,
|
|
2308
|
+
items
|
|
2309
|
+
} = state;
|
|
2310
|
+
if (deltaY < 0) {
|
|
2311
|
+
deltaY = 0;
|
|
2312
|
+
} else if (deltaY > items.length * itemHeight - height) {
|
|
2313
|
+
deltaY = Math.max(items.length * itemHeight - height, 0);
|
|
2314
|
+
}
|
|
2315
|
+
if (state.deltaY === deltaY) {
|
|
2316
|
+
return state;
|
|
2317
|
+
}
|
|
2318
|
+
const minLineY = Math.round(deltaY / itemHeight);
|
|
2319
|
+
const maxLineY = minLineY + Math.round(height / itemHeight);
|
|
2320
|
+
return {
|
|
2321
|
+
...state,
|
|
2322
|
+
deltaY,
|
|
2323
|
+
minLineY,
|
|
2324
|
+
maxLineY
|
|
2325
|
+
};
|
|
2326
|
+
};
|
|
2327
|
+
const handleWheel = (state, deltaMode, deltaY) => {
|
|
2328
|
+
return setDeltaY(state, state.deltaY + deltaY);
|
|
2329
|
+
};
|
|
2330
|
+
|
|
2331
|
+
// TODO support multiselection and removing multiple dirents
|
|
2332
|
+
const removeDirent = async state => {
|
|
2333
|
+
if (state.focusedIndex < 0) {
|
|
2334
|
+
return state;
|
|
2335
|
+
}
|
|
2336
|
+
const dirent = getFocusedDirent$1(state);
|
|
2337
|
+
const absolutePath = dirent.path;
|
|
2338
|
+
try {
|
|
2339
|
+
// TODO handle error
|
|
2340
|
+
await remove(absolutePath);
|
|
2341
|
+
} catch (error) {
|
|
2342
|
+
// TODO vscode shows error as alert (no stacktrace) and retry button
|
|
2343
|
+
// maybe should show alert as well, but where to put stacktrace?
|
|
2344
|
+
// on web should probably show notification (dialog)
|
|
2345
|
+
// ErrorHandling.handleError(error)
|
|
2183
2346
|
// await ErrorHandling.showErrorDialog(error)
|
|
2184
2347
|
return;
|
|
2185
2348
|
}
|
|
@@ -2245,7 +2408,7 @@ const cancelEdit = state => {
|
|
|
2245
2408
|
focused: true,
|
|
2246
2409
|
editingIndex: -1,
|
|
2247
2410
|
editingValue: '',
|
|
2248
|
-
editingType: None$
|
|
2411
|
+
editingType: None$3
|
|
2249
2412
|
};
|
|
2250
2413
|
};
|
|
2251
2414
|
|
|
@@ -2292,15 +2455,6 @@ const handleClickDirectory = async (state, dirent, index, keepFocus) => {
|
|
|
2292
2455
|
maxLineY
|
|
2293
2456
|
};
|
|
2294
2457
|
};
|
|
2295
|
-
const handleClickDirectoryExpanding = (state, dirent, index, keepFocus) => {
|
|
2296
|
-
dirent.type = Directory;
|
|
2297
|
-
dirent.icon = getIcon();
|
|
2298
|
-
return {
|
|
2299
|
-
...state,
|
|
2300
|
-
focusedIndex: index,
|
|
2301
|
-
focused: keepFocus
|
|
2302
|
-
};
|
|
2303
|
-
};
|
|
2304
2458
|
const handleClickDirectoryExpanded = (state, dirent, index, keepFocus) => {
|
|
2305
2459
|
const {
|
|
2306
2460
|
minLineY,
|
|
@@ -2337,30 +2491,6 @@ const handleClickDirectoryExpanded = (state, dirent, index, keepFocus) => {
|
|
|
2337
2491
|
focused: keepFocus
|
|
2338
2492
|
};
|
|
2339
2493
|
};
|
|
2340
|
-
const getClickFn = direntType => {
|
|
2341
|
-
switch (direntType) {
|
|
2342
|
-
case File:
|
|
2343
|
-
case SymLinkFile:
|
|
2344
|
-
return handleClickFile;
|
|
2345
|
-
case Directory:
|
|
2346
|
-
case SymLinkFolder:
|
|
2347
|
-
return handleClickDirectory;
|
|
2348
|
-
case DirectoryExpanding:
|
|
2349
|
-
return handleClickDirectoryExpanding;
|
|
2350
|
-
case DirectoryExpanded:
|
|
2351
|
-
return handleClickDirectoryExpanded;
|
|
2352
|
-
case Symlink:
|
|
2353
|
-
return handleClickSymLink;
|
|
2354
|
-
case CharacterDevice:
|
|
2355
|
-
throw new Error('Cannot open character device files');
|
|
2356
|
-
case BlockDevice:
|
|
2357
|
-
throw new Error('Cannot open block device files');
|
|
2358
|
-
case Socket:
|
|
2359
|
-
throw new Error('Cannot open socket files');
|
|
2360
|
-
default:
|
|
2361
|
-
throw new Error(`unsupported dirent type ${direntType}`);
|
|
2362
|
-
}
|
|
2363
|
-
};
|
|
2364
2494
|
const handleClick = (state, index, keepFocus = false) => {
|
|
2365
2495
|
const {
|
|
2366
2496
|
items,
|
|
@@ -2472,21 +2602,6 @@ const handleArrowLeft = state => {
|
|
|
2472
2602
|
// TODO what happens when mouse leave and anther mouse enter event occur?
|
|
2473
2603
|
// should update preview instead of closing and reopening
|
|
2474
2604
|
|
|
2475
|
-
const handleBlur = state => {
|
|
2476
|
-
// TODO when blur event occurs because of context menu, focused index should stay the same
|
|
2477
|
-
// but focus outline should be removed
|
|
2478
|
-
const {
|
|
2479
|
-
editingType
|
|
2480
|
-
} = state;
|
|
2481
|
-
if (editingType !== None$2) {
|
|
2482
|
-
return state;
|
|
2483
|
-
}
|
|
2484
|
-
return {
|
|
2485
|
-
...state,
|
|
2486
|
-
focused: false
|
|
2487
|
-
};
|
|
2488
|
-
};
|
|
2489
|
-
|
|
2490
2605
|
// TODO maybe just insert items into explorer and refresh whole explorer
|
|
2491
2606
|
|
|
2492
2607
|
const handleClickOpenFolder = async state => {
|
|
@@ -2535,6 +2650,321 @@ const handleContextMenu = (state, button, x, y) => {
|
|
|
2535
2650
|
}
|
|
2536
2651
|
};
|
|
2537
2652
|
|
|
2653
|
+
const getFilePathElectron = async file => {
|
|
2654
|
+
return invoke('GetFilePathElectron.getFilePathElectron', file);
|
|
2655
|
+
};
|
|
2656
|
+
|
|
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');
|
|
2935
|
+
return state;
|
|
2936
|
+
};
|
|
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
|
+
|
|
2538
2968
|
const handlePointerDown = (state, button, x, y) => {
|
|
2539
2969
|
const index = getIndexFromPosition(state, x, y);
|
|
2540
2970
|
if (button === LeftClick && index === -1) {
|
|
@@ -2657,6 +3087,228 @@ const restoreState = savedState => {
|
|
|
2657
3087
|
};
|
|
2658
3088
|
};
|
|
2659
3089
|
|
|
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);
|
|
3310
|
+
};
|
|
3311
|
+
|
|
2660
3312
|
const isExpandedDirectory = dirent => {
|
|
2661
3313
|
return dirent.type === DirectoryExpanded;
|
|
2662
3314
|
};
|
|
@@ -2705,7 +3357,9 @@ const commandMap = {
|
|
|
2705
3357
|
'Explorer.handleClickCurrentButKeepFocus': handleClickCurrentButKeepFocus,
|
|
2706
3358
|
'Explorer.handleClickOpenFolder': handleClickOpenFolder,
|
|
2707
3359
|
'Explorer.handleContextMenu': handleContextMenu,
|
|
3360
|
+
'Explorer.handleDrop': handleDrop,
|
|
2708
3361
|
'Explorer.handleIconThemeChange': handleIconThemeChange,
|
|
3362
|
+
'Explorer.handlePaste': handlePaste,
|
|
2709
3363
|
'Explorer.handlePointerDown': handlePointerDown,
|
|
2710
3364
|
'Explorer.handleWheel': handleWheel,
|
|
2711
3365
|
'Explorer.loadContent': loadContent,
|
|
@@ -2713,6 +3367,7 @@ const commandMap = {
|
|
|
2713
3367
|
'Explorer.removeDirent': removeDirent,
|
|
2714
3368
|
'Explorer.renameDirent': renameDirent,
|
|
2715
3369
|
'Explorer.restoreState': restoreState,
|
|
3370
|
+
'Explorer.revealItem': revealItem,
|
|
2716
3371
|
'Explorer.saveState': saveState
|
|
2717
3372
|
};
|
|
2718
3373
|
|