@lvce-editor/explorer-view 1.1.0 → 1.3.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 +763 -137
- package/package.json +1 -1
|
@@ -6,17 +6,17 @@ const create$4 = (method, params) => {
|
|
|
6
6
|
params
|
|
7
7
|
};
|
|
8
8
|
};
|
|
9
|
-
const state = {
|
|
9
|
+
const state$1 = {
|
|
10
10
|
callbacks: Object.create(null)
|
|
11
11
|
};
|
|
12
12
|
const set = (id, fn) => {
|
|
13
|
-
state.callbacks[id] = fn;
|
|
13
|
+
state$1.callbacks[id] = fn;
|
|
14
14
|
};
|
|
15
15
|
const get = id => {
|
|
16
|
-
return state.callbacks[id];
|
|
16
|
+
return state$1.callbacks[id];
|
|
17
17
|
};
|
|
18
18
|
const remove$1 = id => {
|
|
19
|
-
delete state.callbacks[id];
|
|
19
|
+
delete state$1.callbacks[id];
|
|
20
20
|
};
|
|
21
21
|
let id = 0;
|
|
22
22
|
const create$3 = () => {
|
|
@@ -339,7 +339,7 @@ const send = (transport, method, ...params) => {
|
|
|
339
339
|
const message = create$4(method, params);
|
|
340
340
|
transport.send(message);
|
|
341
341
|
};
|
|
342
|
-
const invoke = (ipc, method, ...params) => {
|
|
342
|
+
const invoke$1 = (ipc, method, ...params) => {
|
|
343
343
|
return invokeHelper(ipc, method, params, false);
|
|
344
344
|
};
|
|
345
345
|
const invokeAndTransfer = (ipc, method, ...params) => {
|
|
@@ -753,7 +753,7 @@ const createRpc = ipc => {
|
|
|
753
753
|
send(ipc, method, ...params);
|
|
754
754
|
},
|
|
755
755
|
invoke(method, ...params) {
|
|
756
|
-
return invoke(ipc, method, ...params);
|
|
756
|
+
return invoke$1(ipc, method, ...params);
|
|
757
757
|
},
|
|
758
758
|
invokeAndTransfer(method, ...params) {
|
|
759
759
|
return invokeAndTransfer(ipc, method, ...params);
|
|
@@ -799,6 +799,16 @@ const WebWorkerRpcClient = {
|
|
|
799
799
|
create: create$1
|
|
800
800
|
};
|
|
801
801
|
|
|
802
|
+
const RE_CHARACTERS = /^[a-zA-Z.-]+$/;
|
|
803
|
+
const compareStringNumeric = (a, b) => {
|
|
804
|
+
if (RE_CHARACTERS.test(a) && RE_CHARACTERS.test(b)) {
|
|
805
|
+
return a < b ? -1 : 1;
|
|
806
|
+
}
|
|
807
|
+
return a.localeCompare(b, 'en', {
|
|
808
|
+
numeric: true
|
|
809
|
+
});
|
|
810
|
+
};
|
|
811
|
+
|
|
802
812
|
const BlockDevice = 1;
|
|
803
813
|
const CharacterDevice = 2;
|
|
804
814
|
const Directory = 3;
|
|
@@ -811,6 +821,142 @@ const SymLinkFile = 10;
|
|
|
811
821
|
const SymLinkFolder = 11;
|
|
812
822
|
const Unknown = 12;
|
|
813
823
|
|
|
824
|
+
const priorityMapFoldersFirst = {
|
|
825
|
+
[Directory]: 1,
|
|
826
|
+
[SymLinkFolder]: 1,
|
|
827
|
+
[File]: 0,
|
|
828
|
+
[SymLinkFile]: 0,
|
|
829
|
+
[Unknown]: 0,
|
|
830
|
+
[Socket]: 0
|
|
831
|
+
};
|
|
832
|
+
const compareDirentType = (direntA, direntB) => {
|
|
833
|
+
return priorityMapFoldersFirst[direntB.type] - priorityMapFoldersFirst[direntA.type];
|
|
834
|
+
};
|
|
835
|
+
const compareDirentName = (direntA, direntB) => {
|
|
836
|
+
return compareStringNumeric(direntA.name, direntB.name);
|
|
837
|
+
};
|
|
838
|
+
const compareDirent = (direntA, direntB) => {
|
|
839
|
+
return compareDirentType(direntA, direntB) || compareDirentName(direntA, direntB);
|
|
840
|
+
};
|
|
841
|
+
|
|
842
|
+
const getFileIcon = ({
|
|
843
|
+
name
|
|
844
|
+
}) => {
|
|
845
|
+
return '';
|
|
846
|
+
};
|
|
847
|
+
const getIcon = dirent => {
|
|
848
|
+
return '';
|
|
849
|
+
};
|
|
850
|
+
|
|
851
|
+
// TODO use posInSet and setSize properties to compute more effectively
|
|
852
|
+
const computeExplorerRenamedDirent = (dirents, index, newName) => {
|
|
853
|
+
let startIndex = index;
|
|
854
|
+
let innerEndIndex = index + 1;
|
|
855
|
+
let insertIndex = -1;
|
|
856
|
+
let posInSet = -1;
|
|
857
|
+
const oldDirent = dirents[index];
|
|
858
|
+
const newDirent = {
|
|
859
|
+
...oldDirent,
|
|
860
|
+
name: newName,
|
|
861
|
+
path: oldDirent.path.slice(0, -oldDirent.name.length) + newName,
|
|
862
|
+
icon: getFileIcon({
|
|
863
|
+
name: newName
|
|
864
|
+
})
|
|
865
|
+
};
|
|
866
|
+
const depth = newDirent.depth;
|
|
867
|
+
// TODO
|
|
868
|
+
for (; startIndex >= 0; startIndex--) {
|
|
869
|
+
const dirent = dirents[startIndex];
|
|
870
|
+
if (dirent.depth > depth) {
|
|
871
|
+
continue;
|
|
872
|
+
}
|
|
873
|
+
if (dirent.depth < depth) {
|
|
874
|
+
break;
|
|
875
|
+
}
|
|
876
|
+
if (compareDirent(dirent, newDirent) === 1) {
|
|
877
|
+
insertIndex = startIndex;
|
|
878
|
+
posInSet = dirent.posInSet;
|
|
879
|
+
// dirent.posInSet++
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
startIndex++;
|
|
883
|
+
for (; innerEndIndex < dirents.length; innerEndIndex++) {
|
|
884
|
+
const dirent = dirents[innerEndIndex];
|
|
885
|
+
if (dirent.depth <= depth) {
|
|
886
|
+
break;
|
|
887
|
+
}
|
|
888
|
+
dirent.path = newDirent.path + dirent.path.slice(oldDirent.path.length);
|
|
889
|
+
}
|
|
890
|
+
innerEndIndex--;
|
|
891
|
+
let endIndex = innerEndIndex + 1;
|
|
892
|
+
for (; endIndex < dirents.length; endIndex++) {
|
|
893
|
+
const dirent = dirents[endIndex];
|
|
894
|
+
if (dirent.depth > depth) {
|
|
895
|
+
continue;
|
|
896
|
+
}
|
|
897
|
+
if (dirent.depth < depth) {
|
|
898
|
+
break;
|
|
899
|
+
}
|
|
900
|
+
if (insertIndex === -1 && compareDirent(dirent, newDirent === -1)) {
|
|
901
|
+
for (; endIndex < dirents.length; endIndex++) {
|
|
902
|
+
}
|
|
903
|
+
insertIndex = endIndex;
|
|
904
|
+
posInSet = dirent.posInSet + 1;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
endIndex--;
|
|
908
|
+
for (let j = startIndex; j < index; j++) {
|
|
909
|
+
const dirent = dirents[j];
|
|
910
|
+
if (dirent.depth === depth) {
|
|
911
|
+
dirent.posInSet++;
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
for (let j = index; j < endIndex; j++) {
|
|
915
|
+
const dirent = dirents[j];
|
|
916
|
+
if (dirent.depth === depth) {
|
|
917
|
+
dirent.posInSet--;
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
// for (let j = startIndex; j < index; j++) {
|
|
922
|
+
// const dirent = dirents[j]
|
|
923
|
+
// dirent.posInSet++
|
|
924
|
+
// }
|
|
925
|
+
|
|
926
|
+
if (insertIndex === -1) {
|
|
927
|
+
insertIndex = index;
|
|
928
|
+
return {
|
|
929
|
+
focusedIndex: index,
|
|
930
|
+
newDirents: [...dirents.slice(0, index), newDirent, ...dirents.slice(index + 1)]
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
newDirent.posInSet = posInSet;
|
|
934
|
+
const newDirents = [...dirents];
|
|
935
|
+
if (index < insertIndex) {
|
|
936
|
+
insertIndex--;
|
|
937
|
+
}
|
|
938
|
+
newDirents.splice(index, 1);
|
|
939
|
+
newDirents.splice(insertIndex, 0, newDirent);
|
|
940
|
+
return {
|
|
941
|
+
newDirents,
|
|
942
|
+
focusedIndex: insertIndex
|
|
943
|
+
};
|
|
944
|
+
};
|
|
945
|
+
|
|
946
|
+
const None$2 = 0;
|
|
947
|
+
const CreateFile = 1;
|
|
948
|
+
const CreateFolder = 2;
|
|
949
|
+
const Rename = 3;
|
|
950
|
+
|
|
951
|
+
const remove = async diren => {};
|
|
952
|
+
const readDirWithFileTypes = async uri => {};
|
|
953
|
+
const getPathSeparator$1 = async root => {};
|
|
954
|
+
const getRealPath = async path => {};
|
|
955
|
+
const stat = async dirent => {};
|
|
956
|
+
const createFile = async uri => {};
|
|
957
|
+
const mkdir = async uri => {};
|
|
958
|
+
const rename$1 = async (oldUri, newUri) => {};
|
|
959
|
+
|
|
814
960
|
class AssertionError extends Error {
|
|
815
961
|
constructor(message) {
|
|
816
962
|
super(message);
|
|
@@ -845,6 +991,12 @@ const object = value => {
|
|
|
845
991
|
throw new AssertionError('expected value to be of type object');
|
|
846
992
|
}
|
|
847
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
|
+
};
|
|
848
1000
|
const array = value => {
|
|
849
1001
|
const type = getType(value);
|
|
850
1002
|
if (type !== 'array') {
|
|
@@ -858,119 +1010,193 @@ const string = value => {
|
|
|
858
1010
|
}
|
|
859
1011
|
};
|
|
860
1012
|
|
|
861
|
-
const
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
const getPathSeparator$1 = async root => {};
|
|
866
|
-
const getRealPath = async path => {};
|
|
867
|
-
const stat = async dirent => {};
|
|
868
|
-
|
|
869
|
-
const getFileIcon = ({
|
|
870
|
-
name
|
|
871
|
-
}) => {
|
|
872
|
-
return '';
|
|
873
|
-
};
|
|
874
|
-
const getIcon = dirent => {
|
|
875
|
-
return '';
|
|
876
|
-
};
|
|
877
|
-
|
|
878
|
-
const RE_CHARACTERS = /^[a-zA-Z.-]+$/;
|
|
879
|
-
const compareStringNumeric = (a, b) => {
|
|
880
|
-
if (RE_CHARACTERS.test(a) && RE_CHARACTERS.test(b)) {
|
|
881
|
-
return a < b ? -1 : 1;
|
|
1013
|
+
const dirname = (pathSeparator, path) => {
|
|
1014
|
+
const index = path.lastIndexOf(pathSeparator);
|
|
1015
|
+
if (index === -1) {
|
|
1016
|
+
return path;
|
|
882
1017
|
}
|
|
883
|
-
return
|
|
884
|
-
numeric: true
|
|
885
|
-
});
|
|
1018
|
+
return path.slice(0, index);
|
|
886
1019
|
};
|
|
887
1020
|
|
|
888
|
-
const
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
[
|
|
893
|
-
[Unknown]: 0,
|
|
894
|
-
[Socket]: 0
|
|
895
|
-
};
|
|
896
|
-
const compareDirentType = (direntA, direntB) => {
|
|
897
|
-
return priorityMapFoldersFirst[direntB.type] - priorityMapFoldersFirst[direntA.type];
|
|
898
|
-
};
|
|
899
|
-
const compareDirentName = (direntA, direntB) => {
|
|
900
|
-
return compareStringNumeric(direntA.name, direntB.name);
|
|
901
|
-
};
|
|
902
|
-
const compareDirent = (direntA, direntB) => {
|
|
903
|
-
return compareDirentType(direntA, direntB) || compareDirentName(direntA, direntB);
|
|
904
|
-
};
|
|
905
|
-
const sortExplorerItems = rawDirents => {
|
|
906
|
-
rawDirents.sort(compareDirent);
|
|
1021
|
+
const getParentFolder = (dirents, index, root) => {
|
|
1022
|
+
if (index < 0) {
|
|
1023
|
+
return root;
|
|
1024
|
+
}
|
|
1025
|
+
return dirents[index].path;
|
|
907
1026
|
};
|
|
908
|
-
|
|
909
|
-
const
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
1027
|
+
const acceptCreate = async (state, newDirentType, createFn) => {
|
|
1028
|
+
const {
|
|
1029
|
+
focusedIndex,
|
|
1030
|
+
editingValue
|
|
1031
|
+
} = state;
|
|
1032
|
+
const newFileName = editingValue;
|
|
1033
|
+
if (!newFileName) {
|
|
1034
|
+
// TODO show error message that file name must not be empty
|
|
1035
|
+
// below input box
|
|
1036
|
+
// await ErrorHandling.showErrorDialog(new Error('file name must not be empty'))
|
|
1037
|
+
return state;
|
|
1038
|
+
}
|
|
1039
|
+
const parentFolder = getParentFolder(state.items, focusedIndex, state.root);
|
|
1040
|
+
const absolutePath = [parentFolder, newFileName].join(state.pathSeparator);
|
|
1041
|
+
// TODO better handle error
|
|
1042
|
+
try {
|
|
1043
|
+
await createFn(absolutePath);
|
|
1044
|
+
} catch (error) {
|
|
1045
|
+
// TODO display error
|
|
1046
|
+
return state;
|
|
1047
|
+
}
|
|
1048
|
+
const parentDirent = focusedIndex >= 0 ? state.items[focusedIndex] : {
|
|
1049
|
+
depth: 0,
|
|
1050
|
+
path: state.root
|
|
924
1051
|
};
|
|
925
|
-
const
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
1052
|
+
const depth = parentDirent.depth + 1;
|
|
1053
|
+
const newDirent = {
|
|
1054
|
+
path: absolutePath,
|
|
1055
|
+
posInSet: -1,
|
|
1056
|
+
setSize: 1,
|
|
1057
|
+
depth,
|
|
1058
|
+
name: newFileName,
|
|
1059
|
+
type: newDirentType,
|
|
1060
|
+
icon: ''
|
|
1061
|
+
};
|
|
1062
|
+
newDirent.icon = getIcon();
|
|
1063
|
+
let insertIndex = state.focusedIndex;
|
|
1064
|
+
let deltaPosInSet = 0;
|
|
1065
|
+
let posInSet = 1;
|
|
1066
|
+
let setSize = 1;
|
|
1067
|
+
let i = Math.max(state.focusedIndex, -1) + 1;
|
|
1068
|
+
const {
|
|
1069
|
+
items
|
|
1070
|
+
} = state;
|
|
1071
|
+
// TODO update posinset and setsize of all affected dirents
|
|
1072
|
+
for (; i < items.length; i++) {
|
|
1073
|
+
const dirent = items[i];
|
|
1074
|
+
if (dirent.depth !== depth) {
|
|
1075
|
+
break;
|
|
930
1076
|
}
|
|
931
|
-
|
|
932
|
-
|
|
1077
|
+
const compareResult = compareDirent(dirent, newDirent);
|
|
1078
|
+
if (compareResult === 1) {
|
|
1079
|
+
insertIndex = i - 1;
|
|
1080
|
+
deltaPosInSet = 1 - 1;
|
|
1081
|
+
break;
|
|
1082
|
+
} else {
|
|
1083
|
+
posInSet = dirent.posInSet + 1;
|
|
1084
|
+
setSize = dirent.setSize + 1;
|
|
1085
|
+
insertIndex = i;
|
|
1086
|
+
}
|
|
1087
|
+
dirent.setSize++;
|
|
1088
|
+
dirent.posInSet += deltaPosInSet;
|
|
933
1089
|
}
|
|
934
|
-
|
|
1090
|
+
newDirent.setSize = setSize;
|
|
1091
|
+
newDirent.posInSet = posInSet;
|
|
1092
|
+
items.splice(insertIndex + 1, 0, newDirent);
|
|
1093
|
+
const newDirents = [...items];
|
|
1094
|
+
const newMaxlineY = Math.max(state.maxLineY, newDirents.length);
|
|
1095
|
+
return {
|
|
1096
|
+
...state,
|
|
1097
|
+
items: newDirents,
|
|
1098
|
+
editingIndex: -1,
|
|
1099
|
+
focusedIndex: insertIndex + 1,
|
|
1100
|
+
editingType: None$2,
|
|
1101
|
+
maxLineY: newMaxlineY
|
|
1102
|
+
};
|
|
935
1103
|
};
|
|
936
|
-
|
|
937
|
-
const getIndexFromPosition = (state, eventX, eventY) => {
|
|
1104
|
+
const acceptRename = async state => {
|
|
938
1105
|
const {
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
items
|
|
1106
|
+
editingIndex,
|
|
1107
|
+
editingValue,
|
|
1108
|
+
items,
|
|
1109
|
+
pathSeparator
|
|
942
1110
|
} = state;
|
|
943
|
-
const
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
1111
|
+
const renamedDirent = items[editingIndex];
|
|
1112
|
+
try {
|
|
1113
|
+
// TODO this does not work with rename of nested file
|
|
1114
|
+
const oldAbsolutePath = renamedDirent.path;
|
|
1115
|
+
const oldParentPath = dirname(pathSeparator, oldAbsolutePath);
|
|
1116
|
+
const newAbsolutePath = [oldParentPath, editingValue].join(pathSeparator);
|
|
1117
|
+
await rename$1(oldAbsolutePath, newAbsolutePath);
|
|
1118
|
+
} catch (error) {
|
|
1119
|
+
// TODO
|
|
1120
|
+
// await ErrorHandling.showErrorDialog(error)
|
|
1121
|
+
return state;
|
|
949
1122
|
}
|
|
950
|
-
|
|
1123
|
+
const {
|
|
1124
|
+
newDirents,
|
|
1125
|
+
focusedIndex
|
|
1126
|
+
} = computeExplorerRenamedDirent(items, editingIndex, editingValue);
|
|
1127
|
+
// TODO move focused index
|
|
1128
|
+
state.items = newDirents;
|
|
1129
|
+
return {
|
|
1130
|
+
...state,
|
|
1131
|
+
editingIndex: -1,
|
|
1132
|
+
editingValue: '',
|
|
1133
|
+
editingType: None$2,
|
|
1134
|
+
editingIcon: '',
|
|
1135
|
+
focusedIndex,
|
|
1136
|
+
focused: true
|
|
1137
|
+
};
|
|
951
1138
|
};
|
|
952
|
-
const
|
|
953
|
-
const
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
1139
|
+
const acceptEdit = state => {
|
|
1140
|
+
const {
|
|
1141
|
+
editingType
|
|
1142
|
+
} = state;
|
|
1143
|
+
switch (editingType) {
|
|
1144
|
+
case CreateFile:
|
|
1145
|
+
return acceptCreate(state, File, createFile);
|
|
1146
|
+
case CreateFolder:
|
|
1147
|
+
return acceptCreate(state, Directory, mkdir);
|
|
1148
|
+
case Rename:
|
|
1149
|
+
return acceptRename(state);
|
|
1150
|
+
default:
|
|
1151
|
+
return state;
|
|
957
1152
|
}
|
|
958
|
-
return startIndex;
|
|
959
1153
|
};
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
1154
|
+
|
|
1155
|
+
const getFocusedDirent$1 = state => {
|
|
1156
|
+
const {
|
|
1157
|
+
focusedIndex,
|
|
1158
|
+
minLineY,
|
|
1159
|
+
items
|
|
1160
|
+
} = state;
|
|
1161
|
+
const dirent = items[focusedIndex + minLineY];
|
|
1162
|
+
return dirent;
|
|
967
1163
|
};
|
|
1164
|
+
|
|
1165
|
+
const copyPath$1 = async state => {
|
|
1166
|
+
// await Command.execute(RendererWorkerCommandType.ClipBoardWriteText, /* text */ path)
|
|
1167
|
+
return state;
|
|
1168
|
+
};
|
|
1169
|
+
|
|
1170
|
+
const state = {
|
|
1171
|
+
rpc: undefined
|
|
1172
|
+
};
|
|
1173
|
+
const invoke = (method, ...params) => {
|
|
1174
|
+
const rpc = state.rpc;
|
|
1175
|
+
// @ts-ignore
|
|
1176
|
+
return rpc.invoke(method, ...params);
|
|
1177
|
+
};
|
|
1178
|
+
const setRpc = rpc => {
|
|
1179
|
+
state.rpc = rpc;
|
|
1180
|
+
};
|
|
1181
|
+
|
|
1182
|
+
const writeText = async text => {
|
|
1183
|
+
await invoke('ClipBoard.writeText', /* text */text);
|
|
1184
|
+
};
|
|
1185
|
+
|
|
1186
|
+
const copyRelativePath$1 = async state => {
|
|
1187
|
+
const dirent = getFocusedDirent$1(state);
|
|
1188
|
+
const relativePath = dirent.path.slice(1);
|
|
1189
|
+
// TODO handle error
|
|
1190
|
+
await writeText(relativePath);
|
|
1191
|
+
return state;
|
|
1192
|
+
};
|
|
1193
|
+
|
|
968
1194
|
const isSymbolicLink = dirent => {
|
|
969
1195
|
return dirent.type === Symlink;
|
|
970
1196
|
};
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
1197
|
+
|
|
1198
|
+
const ENOENT = 'ENOENT';
|
|
1199
|
+
|
|
974
1200
|
const getSymlinkType = type => {
|
|
975
1201
|
switch (type) {
|
|
976
1202
|
case File:
|
|
@@ -981,6 +1207,7 @@ const getSymlinkType = type => {
|
|
|
981
1207
|
return Symlink;
|
|
982
1208
|
}
|
|
983
1209
|
};
|
|
1210
|
+
|
|
984
1211
|
// TODO maybe resolving of symbolic links should happen in shared process?
|
|
985
1212
|
// so that there is less code and less work in the frontend
|
|
986
1213
|
const resolveSymbolicLink = async (uri, rawDirent) => {
|
|
@@ -1018,6 +1245,42 @@ const resolveSymbolicLinks = async (uri, rawDirents) => {
|
|
|
1018
1245
|
const resolvedDirents = await Promise.all(promises);
|
|
1019
1246
|
return resolvedDirents;
|
|
1020
1247
|
};
|
|
1248
|
+
|
|
1249
|
+
const sortExplorerItems = rawDirents => {
|
|
1250
|
+
rawDirents.sort(compareDirent);
|
|
1251
|
+
};
|
|
1252
|
+
|
|
1253
|
+
const toDisplayDirents = (pathSeparator, rawDirents, parentDirent, excluded) => {
|
|
1254
|
+
sortExplorerItems(rawDirents);
|
|
1255
|
+
// TODO figure out whether this uses too much memory (name,path -> redundant, depth could be computed on demand)
|
|
1256
|
+
const toDisplayDirent = (rawDirent, index) => {
|
|
1257
|
+
const path = [parentDirent.path, rawDirent.name].join(pathSeparator);
|
|
1258
|
+
return {
|
|
1259
|
+
name: rawDirent.name,
|
|
1260
|
+
posInSet: index + 1,
|
|
1261
|
+
setSize: rawDirents.length,
|
|
1262
|
+
depth: parentDirent.depth + 1,
|
|
1263
|
+
type: rawDirent.type,
|
|
1264
|
+
path,
|
|
1265
|
+
// TODO storing absolute path might be too costly, could also store relative path here
|
|
1266
|
+
icon: getIcon()
|
|
1267
|
+
};
|
|
1268
|
+
};
|
|
1269
|
+
const result = [];
|
|
1270
|
+
let i = 0;
|
|
1271
|
+
for (const rawDirent of rawDirents) {
|
|
1272
|
+
if (excluded.includes(rawDirent.name)) {
|
|
1273
|
+
continue;
|
|
1274
|
+
}
|
|
1275
|
+
result.push(toDisplayDirent(rawDirent, i));
|
|
1276
|
+
i++;
|
|
1277
|
+
}
|
|
1278
|
+
return result;
|
|
1279
|
+
};
|
|
1280
|
+
|
|
1281
|
+
const hasSymbolicLinks = rawDirents => {
|
|
1282
|
+
return rawDirents.some(isSymbolicLink);
|
|
1283
|
+
};
|
|
1021
1284
|
const getChildDirentsRaw = async uri => {
|
|
1022
1285
|
const rawDirents = await readDirWithFileTypes();
|
|
1023
1286
|
array(rawDirents);
|
|
@@ -1095,6 +1358,85 @@ const expandAll = async state => {
|
|
|
1095
1358
|
};
|
|
1096
1359
|
};
|
|
1097
1360
|
|
|
1361
|
+
const getParentEndIndex = (dirents, index) => {
|
|
1362
|
+
const dirent = dirents[index];
|
|
1363
|
+
let endIndex = index + 1;
|
|
1364
|
+
while (endIndex < dirents.length && dirents[endIndex].depth > dirent.depth) {
|
|
1365
|
+
endIndex++;
|
|
1366
|
+
}
|
|
1367
|
+
return endIndex;
|
|
1368
|
+
};
|
|
1369
|
+
|
|
1370
|
+
const makeExpanded = dirent => {
|
|
1371
|
+
if (dirent.type === Directory) {
|
|
1372
|
+
return {
|
|
1373
|
+
...dirent,
|
|
1374
|
+
type: DirectoryExpanded
|
|
1375
|
+
};
|
|
1376
|
+
}
|
|
1377
|
+
return dirent;
|
|
1378
|
+
};
|
|
1379
|
+
const expandRecursively = async state => {
|
|
1380
|
+
const {
|
|
1381
|
+
items,
|
|
1382
|
+
focusedIndex,
|
|
1383
|
+
pathSeparator,
|
|
1384
|
+
root,
|
|
1385
|
+
height,
|
|
1386
|
+
itemHeight,
|
|
1387
|
+
minLineY
|
|
1388
|
+
} = state;
|
|
1389
|
+
const dirent = focusedIndex < 0 ? {
|
|
1390
|
+
type: Directory,
|
|
1391
|
+
path: root,
|
|
1392
|
+
depth: 0
|
|
1393
|
+
} : items[focusedIndex];
|
|
1394
|
+
if (dirent.type !== Directory && dirent.type !== DirectoryExpanding && dirent.type !== DirectoryExpanded) {
|
|
1395
|
+
return state;
|
|
1396
|
+
}
|
|
1397
|
+
// TODO this is very inefficient
|
|
1398
|
+
const getChildDirentsRecursively = async dirent => {
|
|
1399
|
+
switch (dirent.type) {
|
|
1400
|
+
case File:
|
|
1401
|
+
return [dirent];
|
|
1402
|
+
case Directory:
|
|
1403
|
+
case DirectoryExpanding:
|
|
1404
|
+
case DirectoryExpanded:
|
|
1405
|
+
const childDirents = await getChildDirents(pathSeparator, dirent);
|
|
1406
|
+
const all = [makeExpanded(dirent)];
|
|
1407
|
+
for (const childDirent of childDirents) {
|
|
1408
|
+
const childAll = await getChildDirentsRecursively(childDirent);
|
|
1409
|
+
all.push(...childAll);
|
|
1410
|
+
}
|
|
1411
|
+
return all;
|
|
1412
|
+
default:
|
|
1413
|
+
return [];
|
|
1414
|
+
}
|
|
1415
|
+
};
|
|
1416
|
+
// TODO race condition: what if folder is being collapse while it is recursively expanding?
|
|
1417
|
+
// TODO race condition: what if folder is being deleted while it is recursively expanding?
|
|
1418
|
+
// TODO race condition: what if a new file/folder is created while the folder is recursively expanding?
|
|
1419
|
+
const childDirents = await getChildDirentsRecursively(dirent);
|
|
1420
|
+
const startIndex = focusedIndex;
|
|
1421
|
+
if (focusedIndex >= 0) {
|
|
1422
|
+
const endIndex = getParentEndIndex(items, focusedIndex);
|
|
1423
|
+
const newDirents = [...items.slice(0, startIndex), ...childDirents, ...items.slice(endIndex)];
|
|
1424
|
+
const maxLineY = getExplorerMaxLineY(minLineY, height, itemHeight, newDirents.length);
|
|
1425
|
+
return {
|
|
1426
|
+
...state,
|
|
1427
|
+
items: newDirents,
|
|
1428
|
+
maxLineY
|
|
1429
|
+
};
|
|
1430
|
+
}
|
|
1431
|
+
const newDirents = childDirents.slice(1);
|
|
1432
|
+
const maxLineY = getExplorerMaxLineY(minLineY, height, itemHeight, newDirents.length);
|
|
1433
|
+
return {
|
|
1434
|
+
...state,
|
|
1435
|
+
items: newDirents,
|
|
1436
|
+
maxLineY
|
|
1437
|
+
};
|
|
1438
|
+
};
|
|
1439
|
+
|
|
1098
1440
|
const focusIndex = (state, index) => {
|
|
1099
1441
|
const {
|
|
1100
1442
|
minLineY,
|
|
@@ -1196,7 +1538,7 @@ const TreeItem$1 = 'treeitem';
|
|
|
1196
1538
|
|
|
1197
1539
|
const Button$1 = 'Button';
|
|
1198
1540
|
const ButtonPrimary = 'ButtonPrimary';
|
|
1199
|
-
const Explorer = 'Explorer';
|
|
1541
|
+
const Explorer$1 = 'Explorer';
|
|
1200
1542
|
const FileIcon = 'FileIcon';
|
|
1201
1543
|
const InputBox = 'InputBox';
|
|
1202
1544
|
const Label = 'Label';
|
|
@@ -1249,6 +1591,39 @@ const UiStrings = {
|
|
|
1249
1591
|
OpenFolder: 'Open folder',
|
|
1250
1592
|
NoFolderOpen: 'No Folder Open'
|
|
1251
1593
|
};
|
|
1594
|
+
const newFile = () => {
|
|
1595
|
+
return i18nString(UiStrings.NewFile);
|
|
1596
|
+
};
|
|
1597
|
+
const newFolder = () => {
|
|
1598
|
+
return i18nString(UiStrings.NewFolder);
|
|
1599
|
+
};
|
|
1600
|
+
const openContainingFolder$1 = () => {
|
|
1601
|
+
return i18nString(UiStrings.OpenContainingFolder);
|
|
1602
|
+
};
|
|
1603
|
+
const openInIntegratedTerminal = () => {
|
|
1604
|
+
return i18nString(UiStrings.OpenInIntegratedTerminal);
|
|
1605
|
+
};
|
|
1606
|
+
const cut = () => {
|
|
1607
|
+
return i18nString(UiStrings.Cut);
|
|
1608
|
+
};
|
|
1609
|
+
const copy = () => {
|
|
1610
|
+
return i18nString(UiStrings.Copy);
|
|
1611
|
+
};
|
|
1612
|
+
const paste = () => {
|
|
1613
|
+
return i18nString(UiStrings.Paste);
|
|
1614
|
+
};
|
|
1615
|
+
const copyPath = () => {
|
|
1616
|
+
return i18nString(UiStrings.CopyPath);
|
|
1617
|
+
};
|
|
1618
|
+
const copyRelativePath = () => {
|
|
1619
|
+
return i18nString(UiStrings.CopyRelativePath);
|
|
1620
|
+
};
|
|
1621
|
+
const rename = () => {
|
|
1622
|
+
return i18nString(UiStrings.Rename);
|
|
1623
|
+
};
|
|
1624
|
+
const deleteItem = () => {
|
|
1625
|
+
return i18nString(UiStrings.Delete);
|
|
1626
|
+
};
|
|
1252
1627
|
const filesExplorer = () => {
|
|
1253
1628
|
return i18nString(UiStrings.FilesExplorer);
|
|
1254
1629
|
};
|
|
@@ -1413,7 +1788,7 @@ const mergeClassNames = (...classNames) => {
|
|
|
1413
1788
|
const getExplorerWelcomeVirtualDom = isWide => {
|
|
1414
1789
|
return [{
|
|
1415
1790
|
type: Div,
|
|
1416
|
-
className: mergeClassNames(Viewlet, Explorer),
|
|
1791
|
+
className: mergeClassNames(Viewlet, Explorer$1),
|
|
1417
1792
|
tabIndex: 0,
|
|
1418
1793
|
childCount: 1
|
|
1419
1794
|
}, {
|
|
@@ -1439,7 +1814,7 @@ const getExplorerVirtualDom = (visibleItems, focusedIndex, root, isWide) => {
|
|
|
1439
1814
|
const dom = [];
|
|
1440
1815
|
dom.push({
|
|
1441
1816
|
type: Div,
|
|
1442
|
-
className: mergeClassNames(Viewlet, Explorer),
|
|
1817
|
+
className: mergeClassNames(Viewlet, Explorer$1),
|
|
1443
1818
|
tabIndex: 0,
|
|
1444
1819
|
role: Tree,
|
|
1445
1820
|
ariaLabel: filesExplorer(),
|
|
@@ -1553,8 +1928,120 @@ const getKeyBindings = () => {
|
|
|
1553
1928
|
}];
|
|
1554
1929
|
};
|
|
1555
1930
|
|
|
1931
|
+
const Separator = 1;
|
|
1556
1932
|
const None = 0;
|
|
1557
|
-
const
|
|
1933
|
+
const RestoreFocus = 6;
|
|
1934
|
+
|
|
1935
|
+
const menuEntrySeparator = {
|
|
1936
|
+
id: 'separator',
|
|
1937
|
+
label: '',
|
|
1938
|
+
flags: Separator,
|
|
1939
|
+
command: ''
|
|
1940
|
+
};
|
|
1941
|
+
|
|
1942
|
+
const menuEntryNewFile = {
|
|
1943
|
+
id: 'newFile',
|
|
1944
|
+
label: newFile(),
|
|
1945
|
+
flags: None,
|
|
1946
|
+
command: 'Explorer.newFile'
|
|
1947
|
+
};
|
|
1948
|
+
const menuEntryNewFolder = {
|
|
1949
|
+
id: 'newFolder',
|
|
1950
|
+
label: newFolder(),
|
|
1951
|
+
flags: None,
|
|
1952
|
+
command: 'Explorer.newFolder'
|
|
1953
|
+
};
|
|
1954
|
+
const menuEntryOpenContainingFolder = {
|
|
1955
|
+
id: 'openContainingFolder',
|
|
1956
|
+
label: openContainingFolder$1(),
|
|
1957
|
+
flags: RestoreFocus,
|
|
1958
|
+
command: 'Explorer.openContainingFolder'
|
|
1959
|
+
};
|
|
1960
|
+
const menuEntryOpenInIntegratedTerminal = {
|
|
1961
|
+
id: 'openInIntegratedTerminal',
|
|
1962
|
+
label: openInIntegratedTerminal(),
|
|
1963
|
+
flags: None,
|
|
1964
|
+
command: /* TODO */-1
|
|
1965
|
+
};
|
|
1966
|
+
const menuEntryCut = {
|
|
1967
|
+
id: 'cut',
|
|
1968
|
+
label: cut(),
|
|
1969
|
+
flags: RestoreFocus,
|
|
1970
|
+
command: /* TODO */-1
|
|
1971
|
+
};
|
|
1972
|
+
const menuEntryCopy = {
|
|
1973
|
+
id: 'copy',
|
|
1974
|
+
label: copy(),
|
|
1975
|
+
flags: RestoreFocus,
|
|
1976
|
+
command: 'Explorer.handleCopy'
|
|
1977
|
+
};
|
|
1978
|
+
const menuEntryPaste = {
|
|
1979
|
+
id: 'paste',
|
|
1980
|
+
label: paste(),
|
|
1981
|
+
flags: None,
|
|
1982
|
+
command: 'Explorer.handlePaste'
|
|
1983
|
+
};
|
|
1984
|
+
const menuEntryCopyPath = {
|
|
1985
|
+
id: 'copyPath',
|
|
1986
|
+
label: copyPath(),
|
|
1987
|
+
flags: RestoreFocus,
|
|
1988
|
+
command: 'Explorer.copyPath'
|
|
1989
|
+
};
|
|
1990
|
+
const menuEntryCopyRelativePath = {
|
|
1991
|
+
id: 'copyRelativePath',
|
|
1992
|
+
label: copyRelativePath(),
|
|
1993
|
+
flags: RestoreFocus,
|
|
1994
|
+
command: 'Explorer.copyRelativePath'
|
|
1995
|
+
};
|
|
1996
|
+
const menuEntryRename = {
|
|
1997
|
+
id: 'rename',
|
|
1998
|
+
label: rename(),
|
|
1999
|
+
flags: None,
|
|
2000
|
+
command: 'Explorer.renameDirent'
|
|
2001
|
+
};
|
|
2002
|
+
const menuEntryDelete = {
|
|
2003
|
+
id: 'delete',
|
|
2004
|
+
label: deleteItem(),
|
|
2005
|
+
flags: None,
|
|
2006
|
+
command: 'Explorer.removeDirent'
|
|
2007
|
+
};
|
|
2008
|
+
const ALL_ENTRIES = [menuEntryNewFile, menuEntryNewFolder, menuEntryOpenContainingFolder, menuEntryOpenInIntegratedTerminal, menuEntrySeparator, menuEntryCut, menuEntryCopy, menuEntryPaste, menuEntrySeparator, menuEntryCopyPath, menuEntryCopyRelativePath, menuEntrySeparator, menuEntryRename, menuEntryDelete];
|
|
2009
|
+
|
|
2010
|
+
// TODO there are two possible ways of getting the focused dirent of explorer
|
|
2011
|
+
// 1. directly access state of explorer (bad because it directly accesses state of another component)
|
|
2012
|
+
// 2. expose getFocusedDirent method in explorer (bad because explorer code should not know about for menuEntriesExplorer, which needs that method)
|
|
2013
|
+
const getFocusedDirent = explorerState => {
|
|
2014
|
+
if (!explorerState || explorerState.focusedIndex < 0) {
|
|
2015
|
+
return undefined;
|
|
2016
|
+
}
|
|
2017
|
+
return explorerState.items[explorerState.focusedIndex];
|
|
2018
|
+
};
|
|
2019
|
+
const getMenuEntriesDirectory = () => {
|
|
2020
|
+
return ALL_ENTRIES;
|
|
2021
|
+
};
|
|
2022
|
+
const getMenuEntriesFile = () => {
|
|
2023
|
+
return [menuEntryOpenContainingFolder, menuEntryOpenInIntegratedTerminal, menuEntrySeparator, menuEntryCut, menuEntryCopy, menuEntryPaste, menuEntrySeparator, menuEntryCopyPath, menuEntryCopyRelativePath, menuEntrySeparator, menuEntryRename, menuEntryDelete];
|
|
2024
|
+
};
|
|
2025
|
+
const getMenuEntriesDefault = () => {
|
|
2026
|
+
return ALL_ENTRIES;
|
|
2027
|
+
};
|
|
2028
|
+
const getMenuEntriesRoot = () => {
|
|
2029
|
+
return [menuEntryNewFile, menuEntryNewFolder, menuEntryOpenContainingFolder, menuEntryOpenInIntegratedTerminal, menuEntrySeparator, menuEntryPaste, menuEntrySeparator, menuEntryCopyPath, menuEntryCopyRelativePath];
|
|
2030
|
+
};
|
|
2031
|
+
const getMenuEntries = state => {
|
|
2032
|
+
const focusedDirent = getFocusedDirent(state);
|
|
2033
|
+
if (!focusedDirent) {
|
|
2034
|
+
return getMenuEntriesRoot();
|
|
2035
|
+
}
|
|
2036
|
+
switch (focusedDirent.type) {
|
|
2037
|
+
case Directory:
|
|
2038
|
+
return getMenuEntriesDirectory();
|
|
2039
|
+
case File:
|
|
2040
|
+
return getMenuEntriesFile();
|
|
2041
|
+
default:
|
|
2042
|
+
return getMenuEntriesDefault();
|
|
2043
|
+
}
|
|
2044
|
+
};
|
|
1558
2045
|
|
|
1559
2046
|
const getVisibleExplorerItems = (items, minLineY, maxLineY, focusedIndex, editingIndex, editingType, editingValue) => {
|
|
1560
2047
|
const visible = [];
|
|
@@ -1576,7 +2063,7 @@ const getVisibleExplorerItems = (items, minLineY, maxLineY, focusedIndex, editin
|
|
|
1576
2063
|
});
|
|
1577
2064
|
}
|
|
1578
2065
|
}
|
|
1579
|
-
if (editingType !== None && editingIndex === -1) {
|
|
2066
|
+
if (editingType !== None$2 && editingIndex === -1) {
|
|
1580
2067
|
visible.push({
|
|
1581
2068
|
depth: 3,
|
|
1582
2069
|
posInSet: 1,
|
|
@@ -1592,6 +2079,32 @@ const getVisibleExplorerItems = (items, minLineY, maxLineY, focusedIndex, editin
|
|
|
1592
2079
|
return visible;
|
|
1593
2080
|
};
|
|
1594
2081
|
|
|
2082
|
+
const getIndexFromPosition = (state, eventX, eventY) => {
|
|
2083
|
+
const {
|
|
2084
|
+
y,
|
|
2085
|
+
itemHeight,
|
|
2086
|
+
items
|
|
2087
|
+
} = state;
|
|
2088
|
+
const index = Math.floor((eventY - y) / itemHeight);
|
|
2089
|
+
if (index < 0) {
|
|
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--;
|
|
2103
|
+
}
|
|
2104
|
+
return startIndex;
|
|
2105
|
+
};
|
|
2106
|
+
|
|
2107
|
+
const Keyboard = -1;
|
|
1595
2108
|
const LeftClick = 0;
|
|
1596
2109
|
|
|
1597
2110
|
const openFolder = async () => {
|
|
@@ -1651,22 +2164,13 @@ const setDeltaY = (state, deltaY) => {
|
|
|
1651
2164
|
const handleWheel = (state, deltaMode, deltaY) => {
|
|
1652
2165
|
return setDeltaY(state, state.deltaY + deltaY);
|
|
1653
2166
|
};
|
|
1654
|
-
const getFocusedDirent = state => {
|
|
1655
|
-
const {
|
|
1656
|
-
focusedIndex,
|
|
1657
|
-
minLineY,
|
|
1658
|
-
items
|
|
1659
|
-
} = state;
|
|
1660
|
-
const dirent = items[focusedIndex + minLineY];
|
|
1661
|
-
return dirent;
|
|
1662
|
-
};
|
|
1663
2167
|
|
|
1664
2168
|
// TODO support multiselection and removing multiple dirents
|
|
1665
2169
|
const removeDirent = async state => {
|
|
1666
2170
|
if (state.focusedIndex < 0) {
|
|
1667
2171
|
return state;
|
|
1668
2172
|
}
|
|
1669
|
-
const dirent = getFocusedDirent(state);
|
|
2173
|
+
const dirent = getFocusedDirent$1(state);
|
|
1670
2174
|
const absolutePath = dirent.path;
|
|
1671
2175
|
try {
|
|
1672
2176
|
// TODO handle error
|
|
@@ -1741,22 +2245,9 @@ const cancelEdit = state => {
|
|
|
1741
2245
|
focused: true,
|
|
1742
2246
|
editingIndex: -1,
|
|
1743
2247
|
editingValue: '',
|
|
1744
|
-
editingType: None
|
|
2248
|
+
editingType: None$2
|
|
1745
2249
|
};
|
|
1746
2250
|
};
|
|
1747
|
-
const copyRelativePath = async state => {
|
|
1748
|
-
const dirent = getFocusedDirent(state);
|
|
1749
|
-
// @ts-ignore
|
|
1750
|
-
dirent.path.slice(1);
|
|
1751
|
-
// TODO handle error
|
|
1752
|
-
|
|
1753
|
-
// await Command.execute(RendererWorkerCommandType.ClipBoardWriteText, /* text */ relativePath)
|
|
1754
|
-
return state;
|
|
1755
|
-
};
|
|
1756
|
-
const copyPath = async state => {
|
|
1757
|
-
// await Command.execute(RendererWorkerCommandType.ClipBoardWriteText, /* text */ path)
|
|
1758
|
-
return state;
|
|
1759
|
-
};
|
|
1760
2251
|
|
|
1761
2252
|
// TODO much shared logic with newFolder
|
|
1762
2253
|
|
|
@@ -1987,7 +2478,7 @@ const handleBlur = state => {
|
|
|
1987
2478
|
const {
|
|
1988
2479
|
editingType
|
|
1989
2480
|
} = state;
|
|
1990
|
-
if (editingType !== None) {
|
|
2481
|
+
if (editingType !== None$2) {
|
|
1991
2482
|
return state;
|
|
1992
2483
|
}
|
|
1993
2484
|
return {
|
|
@@ -2003,6 +2494,59 @@ const handleClickOpenFolder = async state => {
|
|
|
2003
2494
|
return state;
|
|
2004
2495
|
};
|
|
2005
2496
|
|
|
2497
|
+
const show = async (x, y, id, ...args) => {
|
|
2498
|
+
return invoke('ContextMenu.show', x, y, id, ...args);
|
|
2499
|
+
};
|
|
2500
|
+
|
|
2501
|
+
const Explorer = 4;
|
|
2502
|
+
|
|
2503
|
+
const handleContextMenuKeyboard = async state => {
|
|
2504
|
+
const {
|
|
2505
|
+
focusedIndex,
|
|
2506
|
+
x,
|
|
2507
|
+
y,
|
|
2508
|
+
minLineY,
|
|
2509
|
+
itemHeight
|
|
2510
|
+
} = state;
|
|
2511
|
+
const menuX = x;
|
|
2512
|
+
const menuY = y + (focusedIndex - minLineY + 1) * itemHeight;
|
|
2513
|
+
await show(menuX, menuY, Explorer);
|
|
2514
|
+
return state;
|
|
2515
|
+
};
|
|
2516
|
+
|
|
2517
|
+
const handleContextMenuMouseAt = async (state, x, y) => {
|
|
2518
|
+
number(x);
|
|
2519
|
+
number(y);
|
|
2520
|
+
const focusedIndex = getIndexFromPosition(state, x, y);
|
|
2521
|
+
await show(x, y, Explorer);
|
|
2522
|
+
return {
|
|
2523
|
+
...state,
|
|
2524
|
+
focusedIndex,
|
|
2525
|
+
focused: false
|
|
2526
|
+
};
|
|
2527
|
+
};
|
|
2528
|
+
|
|
2529
|
+
const handleContextMenu = (state, button, x, y) => {
|
|
2530
|
+
switch (button) {
|
|
2531
|
+
case Keyboard:
|
|
2532
|
+
return handleContextMenuKeyboard(state);
|
|
2533
|
+
default:
|
|
2534
|
+
return handleContextMenuMouseAt(state, x, y);
|
|
2535
|
+
}
|
|
2536
|
+
};
|
|
2537
|
+
|
|
2538
|
+
const handlePointerDown = (state, button, x, y) => {
|
|
2539
|
+
const index = getIndexFromPosition(state, x, y);
|
|
2540
|
+
if (button === LeftClick && index === -1) {
|
|
2541
|
+
return {
|
|
2542
|
+
...state,
|
|
2543
|
+
focused: true,
|
|
2544
|
+
focusedIndex: -1
|
|
2545
|
+
};
|
|
2546
|
+
}
|
|
2547
|
+
return state;
|
|
2548
|
+
};
|
|
2549
|
+
|
|
2006
2550
|
// TODO viewlet should only have create and refresh functions
|
|
2007
2551
|
// every thing else can be in a separate module <viewlet>.lazy.js
|
|
2008
2552
|
// and <viewlet>.ipc.js
|
|
@@ -2030,11 +2574,11 @@ const getExcluded = () => {
|
|
|
2030
2574
|
}
|
|
2031
2575
|
return excluded;
|
|
2032
2576
|
};
|
|
2033
|
-
const getSavedRoot = (savedState, workspacePath) => {
|
|
2577
|
+
const getSavedRoot$1 = (savedState, workspacePath) => {
|
|
2034
2578
|
return workspacePath;
|
|
2035
2579
|
};
|
|
2036
2580
|
const loadContent = async (state, savedState) => {
|
|
2037
|
-
const root = getSavedRoot(savedState, '');
|
|
2581
|
+
const root = getSavedRoot$1(savedState, '');
|
|
2038
2582
|
// TODO path separator could be restored from saved state
|
|
2039
2583
|
const pathSeparator = await getPathSeparator(); // TODO only load path separator once
|
|
2040
2584
|
const excluded = getExcluded();
|
|
@@ -2064,17 +2608,93 @@ const loadContent = async (state, savedState) => {
|
|
|
2064
2608
|
};
|
|
2065
2609
|
};
|
|
2066
2610
|
|
|
2611
|
+
const getContaingingFolder = (root, dirents, focusedIndex, pathSeparator) => {
|
|
2612
|
+
if (focusedIndex < 0) {
|
|
2613
|
+
return root;
|
|
2614
|
+
}
|
|
2615
|
+
const dirent = dirents[focusedIndex];
|
|
2616
|
+
const direntPath = dirent.path;
|
|
2617
|
+
const direntParentPath = direntPath.slice(0, -(dirent.name.length + 1));
|
|
2618
|
+
const path = `${direntParentPath}`;
|
|
2619
|
+
return path;
|
|
2620
|
+
};
|
|
2621
|
+
const openContainingFolder = async state => {
|
|
2622
|
+
const {
|
|
2623
|
+
focusedIndex,
|
|
2624
|
+
root,
|
|
2625
|
+
items,
|
|
2626
|
+
pathSeparator
|
|
2627
|
+
} = state;
|
|
2628
|
+
const path = getContaingingFolder(root, items, focusedIndex);
|
|
2629
|
+
await invoke('OpenNativeFolder.openNativeFolder', /* path */path);
|
|
2630
|
+
return state;
|
|
2631
|
+
};
|
|
2632
|
+
|
|
2633
|
+
const getSavedRoot = (savedState, workspacePath) => {
|
|
2634
|
+
return workspacePath;
|
|
2635
|
+
};
|
|
2636
|
+
const restoreState = savedState => {
|
|
2637
|
+
if (!savedState) {
|
|
2638
|
+
return {
|
|
2639
|
+
minLineY: 0,
|
|
2640
|
+
deltaY: 0
|
|
2641
|
+
};
|
|
2642
|
+
}
|
|
2643
|
+
const root = getSavedRoot(savedState, savedState.workspacePath || '');
|
|
2644
|
+
let minLineY = 0;
|
|
2645
|
+
if (savedState && typeof savedState.minLineY === 'number') {
|
|
2646
|
+
minLineY = savedState.minLineY;
|
|
2647
|
+
}
|
|
2648
|
+
let deltaY = 0;
|
|
2649
|
+
if (savedState && typeof savedState.deltaY === 'number') {
|
|
2650
|
+
deltaY = savedState.deltaY;
|
|
2651
|
+
}
|
|
2652
|
+
return {
|
|
2653
|
+
...savedState,
|
|
2654
|
+
root,
|
|
2655
|
+
minLineY,
|
|
2656
|
+
deltaY
|
|
2657
|
+
};
|
|
2658
|
+
};
|
|
2659
|
+
|
|
2660
|
+
const isExpandedDirectory = dirent => {
|
|
2661
|
+
return dirent.type === DirectoryExpanded;
|
|
2662
|
+
};
|
|
2663
|
+
const getPath = dirent => {
|
|
2664
|
+
return dirent.path;
|
|
2665
|
+
};
|
|
2666
|
+
const saveState = state => {
|
|
2667
|
+
const {
|
|
2668
|
+
items,
|
|
2669
|
+
root,
|
|
2670
|
+
deltaY,
|
|
2671
|
+
minLineY,
|
|
2672
|
+
maxLineY
|
|
2673
|
+
} = state;
|
|
2674
|
+
const expandedPaths = items.filter(isExpandedDirectory).map(getPath);
|
|
2675
|
+
return {
|
|
2676
|
+
expandedPaths,
|
|
2677
|
+
root,
|
|
2678
|
+
minLineY,
|
|
2679
|
+
maxLineY,
|
|
2680
|
+
deltaY
|
|
2681
|
+
};
|
|
2682
|
+
};
|
|
2683
|
+
|
|
2067
2684
|
const commandMap = {
|
|
2685
|
+
'Explorer.acceptEdit': acceptEdit,
|
|
2068
2686
|
'Explorer.cancelEdit': cancelEdit,
|
|
2069
|
-
'Explorer.copyPath': copyPath,
|
|
2070
|
-
'Explorer.copyRelativePath': copyRelativePath,
|
|
2687
|
+
'Explorer.copyPath': copyPath$1,
|
|
2688
|
+
'Explorer.copyRelativePath': copyRelativePath$1,
|
|
2071
2689
|
'Explorer.expandAll': expandAll,
|
|
2690
|
+
'Explorer.expandRecursively': expandRecursively,
|
|
2072
2691
|
'Explorer.focusFirst': focusFirst,
|
|
2073
2692
|
'Explorer.focusIndex': focusIndex,
|
|
2074
2693
|
'Explorer.focusLast': focusLast,
|
|
2075
2694
|
'Explorer.focusNext': focusNext,
|
|
2076
2695
|
'Explorer.focusPrevious': focusPrevious,
|
|
2077
2696
|
'Explorer.getKeyBindings': getKeyBindings,
|
|
2697
|
+
'Explorer.getMenuEntries': getMenuEntries,
|
|
2078
2698
|
'Explorer.getVirtualDom': getExplorerVirtualDom,
|
|
2079
2699
|
'Explorer.getVisibleItems': getVisibleExplorerItems,
|
|
2080
2700
|
'Explorer.handleArrowLeft': handleArrowLeft,
|
|
@@ -2084,17 +2704,23 @@ const commandMap = {
|
|
|
2084
2704
|
'Explorer.handleClickAt': handleClickAt,
|
|
2085
2705
|
'Explorer.handleClickCurrentButKeepFocus': handleClickCurrentButKeepFocus,
|
|
2086
2706
|
'Explorer.handleClickOpenFolder': handleClickOpenFolder,
|
|
2707
|
+
'Explorer.handleContextMenu': handleContextMenu,
|
|
2087
2708
|
'Explorer.handleIconThemeChange': handleIconThemeChange,
|
|
2709
|
+
'Explorer.handlePointerDown': handlePointerDown,
|
|
2088
2710
|
'Explorer.handleWheel': handleWheel,
|
|
2089
2711
|
'Explorer.loadContent': loadContent,
|
|
2712
|
+
'Explorer.openContainingFolder': openContainingFolder,
|
|
2090
2713
|
'Explorer.removeDirent': removeDirent,
|
|
2091
|
-
'Explorer.renameDirent': renameDirent
|
|
2714
|
+
'Explorer.renameDirent': renameDirent,
|
|
2715
|
+
'Explorer.restoreState': restoreState,
|
|
2716
|
+
'Explorer.saveState': saveState
|
|
2092
2717
|
};
|
|
2093
2718
|
|
|
2094
2719
|
const listen = async () => {
|
|
2095
|
-
await WebWorkerRpcClient.create({
|
|
2720
|
+
const rpc = await WebWorkerRpcClient.create({
|
|
2096
2721
|
commandMap: commandMap
|
|
2097
2722
|
});
|
|
2723
|
+
setRpc(rpc);
|
|
2098
2724
|
};
|
|
2099
2725
|
|
|
2100
2726
|
const main = async () => {
|