@lvce-editor/explorer-view 1.5.0 → 1.6.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 +429 -188
- package/package.json +1 -1
|
@@ -949,7 +949,7 @@ const computeExplorerRenamedDirent = (dirents, index, newName) => {
|
|
|
949
949
|
const None$3 = 0;
|
|
950
950
|
const CreateFile = 1;
|
|
951
951
|
const CreateFolder = 2;
|
|
952
|
-
const Rename = 3;
|
|
952
|
+
const Rename$1 = 3;
|
|
953
953
|
|
|
954
954
|
const state = {
|
|
955
955
|
rpc: undefined
|
|
@@ -981,6 +981,9 @@ const stat = async dirent => {
|
|
|
981
981
|
const createFile = async uri => {
|
|
982
982
|
return invoke('FileSystem.createFile', uri);
|
|
983
983
|
};
|
|
984
|
+
const writeFile = async (uri, content) => {
|
|
985
|
+
return invoke('FileSystem.writeFile', uri, content);
|
|
986
|
+
};
|
|
984
987
|
const mkdir = async uri => {
|
|
985
988
|
return invoke('FileSystem.mkdir', uri);
|
|
986
989
|
};
|
|
@@ -1132,13 +1135,57 @@ const acceptEdit = state => {
|
|
|
1132
1135
|
return acceptCreate(state, File, createFile);
|
|
1133
1136
|
case CreateFolder:
|
|
1134
1137
|
return acceptCreate(state, Directory, mkdir);
|
|
1135
|
-
case Rename:
|
|
1138
|
+
case Rename$1:
|
|
1136
1139
|
return acceptRename(state);
|
|
1137
1140
|
default:
|
|
1138
1141
|
return state;
|
|
1139
1142
|
}
|
|
1140
1143
|
};
|
|
1141
1144
|
|
|
1145
|
+
const cancelEdit = state => {
|
|
1146
|
+
const {
|
|
1147
|
+
editingIndex
|
|
1148
|
+
} = state;
|
|
1149
|
+
return {
|
|
1150
|
+
...state,
|
|
1151
|
+
focusedIndex: editingIndex,
|
|
1152
|
+
focused: true,
|
|
1153
|
+
editingIndex: -1,
|
|
1154
|
+
editingValue: '',
|
|
1155
|
+
editingType: None$3
|
|
1156
|
+
};
|
|
1157
|
+
};
|
|
1158
|
+
|
|
1159
|
+
const isTopLevel$1 = dirent => {
|
|
1160
|
+
return dirent.depth === 1;
|
|
1161
|
+
};
|
|
1162
|
+
|
|
1163
|
+
const IsTopLevel = {
|
|
1164
|
+
__proto__: null,
|
|
1165
|
+
isTopLevel: isTopLevel$1
|
|
1166
|
+
};
|
|
1167
|
+
|
|
1168
|
+
const toCollapsedDirent = dirent => {
|
|
1169
|
+
if (dirent.type === DirectoryExpanded) {
|
|
1170
|
+
return {
|
|
1171
|
+
...dirent,
|
|
1172
|
+
type: Directory
|
|
1173
|
+
};
|
|
1174
|
+
}
|
|
1175
|
+
return dirent;
|
|
1176
|
+
};
|
|
1177
|
+
|
|
1178
|
+
const collapseAll$1 = state => {
|
|
1179
|
+
const {
|
|
1180
|
+
items
|
|
1181
|
+
} = state;
|
|
1182
|
+
const newDirents = items.filter(IsTopLevel).map(toCollapsedDirent);
|
|
1183
|
+
return {
|
|
1184
|
+
...state,
|
|
1185
|
+
items: newDirents
|
|
1186
|
+
};
|
|
1187
|
+
};
|
|
1188
|
+
|
|
1142
1189
|
const getFocusedDirent$1 = state => {
|
|
1143
1190
|
const {
|
|
1144
1191
|
focusedIndex,
|
|
@@ -1160,6 +1207,9 @@ const writeText = async text => {
|
|
|
1160
1207
|
const readNativeFiles = async () => {
|
|
1161
1208
|
return invoke('ClipBoard.readNativeFiles');
|
|
1162
1209
|
};
|
|
1210
|
+
const writeNativeFiles = async (type, files) => {
|
|
1211
|
+
return invoke('ClipBoard.writeNativeFiles', type, files);
|
|
1212
|
+
};
|
|
1163
1213
|
|
|
1164
1214
|
const copyRelativePath$1 = async state => {
|
|
1165
1215
|
const dirent = getFocusedDirent$1(state);
|
|
@@ -1407,6 +1457,7 @@ const makeExpanded = dirent => {
|
|
|
1407
1457
|
}
|
|
1408
1458
|
return dirent;
|
|
1409
1459
|
};
|
|
1460
|
+
|
|
1410
1461
|
const expandRecursively = async state => {
|
|
1411
1462
|
const {
|
|
1412
1463
|
items,
|
|
@@ -1563,29 +1614,12 @@ const focusPrevious = state => {
|
|
|
1563
1614
|
}
|
|
1564
1615
|
};
|
|
1565
1616
|
|
|
1566
|
-
const
|
|
1567
|
-
const Tree = 'tree';
|
|
1568
|
-
const TreeItem$1 = 'treeitem';
|
|
1569
|
-
|
|
1570
|
-
const Button$1 = 'Button';
|
|
1571
|
-
const ButtonPrimary = 'ButtonPrimary';
|
|
1572
|
-
const Explorer$1 = 'Explorer';
|
|
1573
|
-
const FileIcon = 'FileIcon';
|
|
1574
|
-
const InputBox = 'InputBox';
|
|
1575
|
-
const Label = 'Label';
|
|
1576
|
-
const TreeItem = 'TreeItem';
|
|
1577
|
-
const TreeItemActive = 'TreeItemActive';
|
|
1578
|
-
const Viewlet = 'Viewlet';
|
|
1579
|
-
const Welcome = 'Welcome';
|
|
1580
|
-
const WelcomeMessage = 'WelcomeMessage';
|
|
1617
|
+
const Button$2 = 1;
|
|
1581
1618
|
|
|
1582
|
-
const
|
|
1583
|
-
const
|
|
1584
|
-
const
|
|
1585
|
-
const
|
|
1586
|
-
const HandleFocus = 'handleFocus';
|
|
1587
|
-
const HandlePointerDown = 'handlePointerDown';
|
|
1588
|
-
const HandleWheel = 'handleWheel';
|
|
1619
|
+
const CollapseAll = 'CollapseAll';
|
|
1620
|
+
const NewFile$1 = 'NewFile';
|
|
1621
|
+
const NewFolder$1 = 'NewFolder';
|
|
1622
|
+
const Refresh = 'Refresh';
|
|
1589
1623
|
|
|
1590
1624
|
const emptyObject = {};
|
|
1591
1625
|
const RE_PLACEHOLDER = /\{(PH\d+)\}/g;
|
|
@@ -1599,72 +1633,126 @@ const i18nString = (key, placeholders = emptyObject) => {
|
|
|
1599
1633
|
return key.replaceAll(RE_PLACEHOLDER, replacer);
|
|
1600
1634
|
};
|
|
1601
1635
|
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
const
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
};
|
|
1625
|
-
const newFile = () => {
|
|
1626
|
-
return i18nString(UiStrings.NewFile);
|
|
1627
|
-
};
|
|
1628
|
-
const newFolder = () => {
|
|
1629
|
-
return i18nString(UiStrings.NewFolder);
|
|
1636
|
+
const NewFile = 'New File...';
|
|
1637
|
+
const NewFolder = 'New Folder...';
|
|
1638
|
+
const OpenContainingFolder = 'Open Containing Folder';
|
|
1639
|
+
const OpenInIntegratedTerminal = 'Open in integrated Terminal';
|
|
1640
|
+
const Cut$1 = 'Cut';
|
|
1641
|
+
const Copy$1 = 'Copy';
|
|
1642
|
+
const Paste = 'Paste';
|
|
1643
|
+
const CopyPath = 'Copy Path';
|
|
1644
|
+
const CopyRelativePath = 'Copy Relative Path';
|
|
1645
|
+
const Rename = 'Rename';
|
|
1646
|
+
const Delete$1 = 'Delete';
|
|
1647
|
+
const RefreshExplorer = 'Refresh Explorer';
|
|
1648
|
+
const CollapseAllFoldersInExplorer = 'Collapse All Folders in Explorer';
|
|
1649
|
+
const FilesExplorer = 'Files Explorer';
|
|
1650
|
+
const YouHaveNotYetOpenedAFolder = 'You have not yet opened a folder';
|
|
1651
|
+
const OpenFolder = 'Open folder';
|
|
1652
|
+
|
|
1653
|
+
const newFile$1 = () => {
|
|
1654
|
+
return i18nString(NewFile);
|
|
1655
|
+
};
|
|
1656
|
+
const newFolder$1 = () => {
|
|
1657
|
+
return i18nString(NewFolder);
|
|
1630
1658
|
};
|
|
1631
1659
|
const openContainingFolder$1 = () => {
|
|
1632
|
-
return i18nString(
|
|
1660
|
+
return i18nString(OpenContainingFolder);
|
|
1633
1661
|
};
|
|
1634
1662
|
const openInIntegratedTerminal = () => {
|
|
1635
|
-
return i18nString(
|
|
1663
|
+
return i18nString(OpenInIntegratedTerminal);
|
|
1636
1664
|
};
|
|
1637
1665
|
const cut = () => {
|
|
1638
|
-
return i18nString(
|
|
1666
|
+
return i18nString(Cut$1);
|
|
1639
1667
|
};
|
|
1640
1668
|
const copy = () => {
|
|
1641
|
-
return i18nString(
|
|
1669
|
+
return i18nString(Copy$1);
|
|
1642
1670
|
};
|
|
1643
1671
|
const paste = () => {
|
|
1644
|
-
return i18nString(
|
|
1672
|
+
return i18nString(Paste);
|
|
1645
1673
|
};
|
|
1646
1674
|
const copyPath = () => {
|
|
1647
|
-
return i18nString(
|
|
1675
|
+
return i18nString(CopyPath);
|
|
1648
1676
|
};
|
|
1649
1677
|
const copyRelativePath = () => {
|
|
1650
|
-
return i18nString(
|
|
1678
|
+
return i18nString(CopyRelativePath);
|
|
1651
1679
|
};
|
|
1652
1680
|
const rename = () => {
|
|
1653
|
-
return i18nString(
|
|
1681
|
+
return i18nString(Rename);
|
|
1654
1682
|
};
|
|
1655
1683
|
const deleteItem = () => {
|
|
1656
|
-
return i18nString(
|
|
1684
|
+
return i18nString(Delete$1);
|
|
1685
|
+
};
|
|
1686
|
+
const refresh = () => {
|
|
1687
|
+
return i18nString(RefreshExplorer);
|
|
1688
|
+
};
|
|
1689
|
+
const collapseAll = () => {
|
|
1690
|
+
return i18nString(CollapseAllFoldersInExplorer);
|
|
1657
1691
|
};
|
|
1658
1692
|
const filesExplorer = () => {
|
|
1659
|
-
return i18nString(
|
|
1693
|
+
return i18nString(FilesExplorer);
|
|
1660
1694
|
};
|
|
1661
1695
|
const youHaveNotYetOpenedAFolder = () => {
|
|
1662
|
-
return i18nString(
|
|
1696
|
+
return i18nString(YouHaveNotYetOpenedAFolder);
|
|
1663
1697
|
};
|
|
1664
1698
|
const openFolder$1 = () => {
|
|
1665
|
-
return i18nString(
|
|
1699
|
+
return i18nString(OpenFolder);
|
|
1700
|
+
};
|
|
1701
|
+
|
|
1702
|
+
const getActions = root => {
|
|
1703
|
+
if (!root) {
|
|
1704
|
+
return [];
|
|
1705
|
+
}
|
|
1706
|
+
return [{
|
|
1707
|
+
type: Button$2,
|
|
1708
|
+
id: newFile$1(),
|
|
1709
|
+
icon: NewFile$1,
|
|
1710
|
+
command: 'newFile'
|
|
1711
|
+
}, {
|
|
1712
|
+
type: Button$2,
|
|
1713
|
+
id: newFolder$1(),
|
|
1714
|
+
icon: NewFolder$1,
|
|
1715
|
+
command: 'newFolder'
|
|
1716
|
+
}, {
|
|
1717
|
+
type: Button$2,
|
|
1718
|
+
id: refresh(),
|
|
1719
|
+
icon: Refresh,
|
|
1720
|
+
command: 'refresh'
|
|
1721
|
+
}, {
|
|
1722
|
+
type: Button$2,
|
|
1723
|
+
id: collapseAll(),
|
|
1724
|
+
icon: CollapseAll,
|
|
1725
|
+
command: 'collapseAll'
|
|
1726
|
+
}];
|
|
1666
1727
|
};
|
|
1667
1728
|
|
|
1729
|
+
const None$2 = 'none';
|
|
1730
|
+
const ToolBar = 'toolbar';
|
|
1731
|
+
const Tree = 'tree';
|
|
1732
|
+
const TreeItem$1 = 'treeitem';
|
|
1733
|
+
|
|
1734
|
+
const Actions = 'Actions';
|
|
1735
|
+
const Button$1 = 'Button';
|
|
1736
|
+
const ButtonPrimary = 'ButtonPrimary';
|
|
1737
|
+
const Explorer$1 = 'Explorer';
|
|
1738
|
+
const FileIcon = 'FileIcon';
|
|
1739
|
+
const IconButton = 'IconButton';
|
|
1740
|
+
const InputBox = 'InputBox';
|
|
1741
|
+
const Label = 'Label';
|
|
1742
|
+
const TreeItem = 'TreeItem';
|
|
1743
|
+
const TreeItemActive = 'TreeItemActive';
|
|
1744
|
+
const Viewlet = 'Viewlet';
|
|
1745
|
+
const Welcome = 'Welcome';
|
|
1746
|
+
const WelcomeMessage = 'WelcomeMessage';
|
|
1747
|
+
|
|
1748
|
+
const HandleBlur = 'handleBlur';
|
|
1749
|
+
const HandleClick = 'handleClick';
|
|
1750
|
+
const handleClickOpenFolder$1 = 'handleClickOpenFolder';
|
|
1751
|
+
const HandleContextMenu = 'handleContextMenu';
|
|
1752
|
+
const HandleFocus = 'handleFocus';
|
|
1753
|
+
const HandlePointerDown = 'handlePointerDown';
|
|
1754
|
+
const HandleWheel = 'handleWheel';
|
|
1755
|
+
|
|
1668
1756
|
const Button = 1;
|
|
1669
1757
|
const Div = 4;
|
|
1670
1758
|
const Input = 6;
|
|
@@ -1972,13 +2060,13 @@ const menuEntrySeparator = {
|
|
|
1972
2060
|
|
|
1973
2061
|
const menuEntryNewFile = {
|
|
1974
2062
|
id: 'newFile',
|
|
1975
|
-
label: newFile(),
|
|
2063
|
+
label: newFile$1(),
|
|
1976
2064
|
flags: None$1,
|
|
1977
2065
|
command: 'Explorer.newFile'
|
|
1978
2066
|
};
|
|
1979
2067
|
const menuEntryNewFolder = {
|
|
1980
2068
|
id: 'newFolder',
|
|
1981
|
-
label: newFolder(),
|
|
2069
|
+
label: newFolder$1(),
|
|
1982
2070
|
flags: None$1,
|
|
1983
2071
|
command: 'Explorer.newFolder'
|
|
1984
2072
|
};
|
|
@@ -2279,6 +2367,40 @@ const getIndexFromPosition = (state, eventX, eventY) => {
|
|
|
2279
2367
|
return index;
|
|
2280
2368
|
};
|
|
2281
2369
|
|
|
2370
|
+
const handleClickDirectory = async (state, dirent, index, keepFocus) => {
|
|
2371
|
+
dirent.type = DirectoryExpanding;
|
|
2372
|
+
// TODO handle error
|
|
2373
|
+
const dirents = await getChildDirents(state.pathSeparator, dirent);
|
|
2374
|
+
const state2 = state;
|
|
2375
|
+
if (!state2) {
|
|
2376
|
+
return state;
|
|
2377
|
+
}
|
|
2378
|
+
// TODO use Viewlet.getState here and check if it exists
|
|
2379
|
+
const newIndex = state2.items.indexOf(dirent);
|
|
2380
|
+
// TODO if viewlet is disposed or root has changed, return
|
|
2381
|
+
if (newIndex === -1) {
|
|
2382
|
+
return state;
|
|
2383
|
+
}
|
|
2384
|
+
const newDirents = [...state2.items];
|
|
2385
|
+
newDirents.splice(newIndex + 1, 0, ...dirents);
|
|
2386
|
+
dirent.type = DirectoryExpanded;
|
|
2387
|
+
dirent.icon = getIcon();
|
|
2388
|
+
const {
|
|
2389
|
+
height,
|
|
2390
|
+
itemHeight,
|
|
2391
|
+
minLineY
|
|
2392
|
+
} = state2;
|
|
2393
|
+
// TODO when focused index has changed while expanding, don't update it
|
|
2394
|
+
const maxLineY = getExplorerMaxLineY(minLineY, height, itemHeight, newDirents.length);
|
|
2395
|
+
return {
|
|
2396
|
+
...state,
|
|
2397
|
+
items: newDirents,
|
|
2398
|
+
focusedIndex: newIndex,
|
|
2399
|
+
focused: keepFocus,
|
|
2400
|
+
maxLineY
|
|
2401
|
+
};
|
|
2402
|
+
};
|
|
2403
|
+
|
|
2282
2404
|
const getParentStartIndex = (dirents, index) => {
|
|
2283
2405
|
const dirent = dirents[index];
|
|
2284
2406
|
let startIndex = index - 1;
|
|
@@ -2291,10 +2413,6 @@ const getParentStartIndex = (dirents, index) => {
|
|
|
2291
2413
|
const Keyboard = -1;
|
|
2292
2414
|
const LeftClick = 0;
|
|
2293
2415
|
|
|
2294
|
-
const openFolder = async () => {
|
|
2295
|
-
// TODO
|
|
2296
|
-
};
|
|
2297
|
-
|
|
2298
2416
|
// TODO viewlet should only have create and refresh functions
|
|
2299
2417
|
// every thing else can be in a separate module <viewlet>.lazy.js
|
|
2300
2418
|
// and <viewlet>.ipc.js
|
|
@@ -2322,7 +2440,7 @@ const handleIconThemeChange = state => {
|
|
|
2322
2440
|
};
|
|
2323
2441
|
|
|
2324
2442
|
// TODO rename dirents to items, then can use virtual list component directly
|
|
2325
|
-
const setDeltaY = (state, deltaY) => {
|
|
2443
|
+
const setDeltaY$1 = (state, deltaY) => {
|
|
2326
2444
|
const {
|
|
2327
2445
|
itemHeight,
|
|
2328
2446
|
height,
|
|
@@ -2346,93 +2464,11 @@ const setDeltaY = (state, deltaY) => {
|
|
|
2346
2464
|
};
|
|
2347
2465
|
};
|
|
2348
2466
|
const handleWheel = (state, deltaMode, deltaY) => {
|
|
2349
|
-
return setDeltaY(state, state.deltaY + deltaY);
|
|
2350
|
-
};
|
|
2351
|
-
|
|
2352
|
-
// TODO support multiselection and removing multiple dirents
|
|
2353
|
-
const removeDirent = async state => {
|
|
2354
|
-
if (state.focusedIndex < 0) {
|
|
2355
|
-
return state;
|
|
2356
|
-
}
|
|
2357
|
-
const dirent = getFocusedDirent$1(state);
|
|
2358
|
-
const absolutePath = dirent.path;
|
|
2359
|
-
try {
|
|
2360
|
-
// TODO handle error
|
|
2361
|
-
await remove(absolutePath);
|
|
2362
|
-
} catch (error) {
|
|
2363
|
-
// TODO vscode shows error as alert (no stacktrace) and retry button
|
|
2364
|
-
// maybe should show alert as well, but where to put stacktrace?
|
|
2365
|
-
// on web should probably show notification (dialog)
|
|
2366
|
-
// ErrorHandling.handleError(error)
|
|
2367
|
-
// await ErrorHandling.showErrorDialog(error)
|
|
2368
|
-
return;
|
|
2369
|
-
}
|
|
2370
|
-
// TODO avoid state mutation
|
|
2371
|
-
const newVersion = ++state.version;
|
|
2372
|
-
// TODO race condition
|
|
2373
|
-
// const newState = await loadContent(state:any)
|
|
2374
|
-
if (state.version !== newVersion || state.disposed) {
|
|
2375
|
-
return state;
|
|
2376
|
-
}
|
|
2377
|
-
// TODO is it possible to make this more functional instead of mutating state?
|
|
2378
|
-
// maybe every function returns a new state?
|
|
2379
|
-
const index = state.items.indexOf(dirent);
|
|
2380
|
-
let deleteEnd = index + 1;
|
|
2381
|
-
for (; deleteEnd < state.items.length; deleteEnd++) {
|
|
2382
|
-
if (state.items[deleteEnd].depth <= dirent.depth) {
|
|
2383
|
-
break;
|
|
2384
|
-
}
|
|
2385
|
-
}
|
|
2386
|
-
const deleteCount = deleteEnd - index;
|
|
2387
|
-
const newDirents = [...state.items];
|
|
2388
|
-
newDirents.splice(index, deleteCount);
|
|
2389
|
-
let indexToFocus = -1;
|
|
2390
|
-
if (newDirents.length === 0) {
|
|
2391
|
-
indexToFocus = -1;
|
|
2392
|
-
} else if (index < state.focusedIndex) {
|
|
2393
|
-
indexToFocus = state.focusedIndex - 1;
|
|
2394
|
-
} else if (index === state.focusedIndex) {
|
|
2395
|
-
indexToFocus = Math.max(state.focusedIndex - 1, 0);
|
|
2396
|
-
} else {
|
|
2397
|
-
indexToFocus = Math.max(state.focusedIndex - 1, 0);
|
|
2398
|
-
}
|
|
2399
|
-
return {
|
|
2400
|
-
...state,
|
|
2401
|
-
items: newDirents,
|
|
2402
|
-
focusedIndex: indexToFocus
|
|
2403
|
-
};
|
|
2404
|
-
};
|
|
2405
|
-
const renameDirent = state => {
|
|
2406
|
-
const {
|
|
2407
|
-
focusedIndex,
|
|
2408
|
-
items
|
|
2409
|
-
} = state;
|
|
2410
|
-
const item = items[focusedIndex];
|
|
2411
|
-
// Focus.setFocus(FocusKey.ExplorerEditBox)
|
|
2412
|
-
return {
|
|
2413
|
-
...state,
|
|
2414
|
-
editingIndex: focusedIndex,
|
|
2415
|
-
editingType: Rename,
|
|
2416
|
-
editingValue: item.name
|
|
2417
|
-
};
|
|
2467
|
+
return setDeltaY$1(state, state.deltaY + deltaY);
|
|
2418
2468
|
};
|
|
2419
2469
|
|
|
2420
2470
|
// TODO use posInSet and setSize properties to compute more effectively
|
|
2421
2471
|
|
|
2422
|
-
const cancelEdit = state => {
|
|
2423
|
-
const {
|
|
2424
|
-
editingIndex
|
|
2425
|
-
} = state;
|
|
2426
|
-
return {
|
|
2427
|
-
...state,
|
|
2428
|
-
focusedIndex: editingIndex,
|
|
2429
|
-
focused: true,
|
|
2430
|
-
editingIndex: -1,
|
|
2431
|
-
editingValue: '',
|
|
2432
|
-
editingType: None$3
|
|
2433
|
-
};
|
|
2434
|
-
};
|
|
2435
|
-
|
|
2436
2472
|
// TODO much shared logic with newFolder
|
|
2437
2473
|
|
|
2438
2474
|
const handleClickFile = async (state, dirent, index, keepFocus = false) => {
|
|
@@ -2443,39 +2479,6 @@ const handleClickFile = async (state, dirent, index, keepFocus = false) => {
|
|
|
2443
2479
|
focused: keepFocus
|
|
2444
2480
|
};
|
|
2445
2481
|
};
|
|
2446
|
-
const handleClickDirectory = async (state, dirent, index, keepFocus) => {
|
|
2447
|
-
dirent.type = DirectoryExpanding;
|
|
2448
|
-
// TODO handle error
|
|
2449
|
-
const dirents = await getChildDirents(state.pathSeparator, dirent);
|
|
2450
|
-
const state2 = {};
|
|
2451
|
-
if (!state2) {
|
|
2452
|
-
return state;
|
|
2453
|
-
}
|
|
2454
|
-
// TODO use Viewlet.getState here and check if it exists
|
|
2455
|
-
const newIndex = state2.items.indexOf(dirent);
|
|
2456
|
-
// TODO if viewlet is disposed or root has changed, return
|
|
2457
|
-
if (newIndex === -1) {
|
|
2458
|
-
return state;
|
|
2459
|
-
}
|
|
2460
|
-
const newDirents = [...state2.items];
|
|
2461
|
-
newDirents.splice(newIndex + 1, 0, ...dirents);
|
|
2462
|
-
dirent.type = DirectoryExpanded;
|
|
2463
|
-
dirent.icon = getIcon();
|
|
2464
|
-
const {
|
|
2465
|
-
height,
|
|
2466
|
-
itemHeight,
|
|
2467
|
-
minLineY
|
|
2468
|
-
} = state2;
|
|
2469
|
-
// TODO when focused index has changed while expanding, don't update it
|
|
2470
|
-
const maxLineY = getExplorerMaxLineY(minLineY, height, itemHeight, newDirents.length);
|
|
2471
|
-
return {
|
|
2472
|
-
...state,
|
|
2473
|
-
items: newDirents,
|
|
2474
|
-
focusedIndex: newIndex,
|
|
2475
|
-
focused: keepFocus,
|
|
2476
|
-
maxLineY
|
|
2477
|
-
};
|
|
2478
|
-
};
|
|
2479
2482
|
const handleClickDirectoryExpanded = (state, dirent, index, keepFocus) => {
|
|
2480
2483
|
const {
|
|
2481
2484
|
minLineY,
|
|
@@ -2625,6 +2628,10 @@ const handleArrowLeft = state => {
|
|
|
2625
2628
|
|
|
2626
2629
|
// TODO maybe just insert items into explorer and refresh whole explorer
|
|
2627
2630
|
|
|
2631
|
+
const openFolder = async () => {
|
|
2632
|
+
// TODO
|
|
2633
|
+
};
|
|
2634
|
+
|
|
2628
2635
|
const handleClickOpenFolder = async state => {
|
|
2629
2636
|
await openFolder();
|
|
2630
2637
|
return state;
|
|
@@ -2671,6 +2678,21 @@ const handleContextMenu = (state, button, x, y) => {
|
|
|
2671
2678
|
}
|
|
2672
2679
|
};
|
|
2673
2680
|
|
|
2681
|
+
const handleCopy = async state => {
|
|
2682
|
+
// TODO handle multiple files
|
|
2683
|
+
// TODO if not file is selected, what happens?
|
|
2684
|
+
const dirent = getFocusedDirent$1(state);
|
|
2685
|
+
if (!dirent) {
|
|
2686
|
+
console.info('[ViewletExplorer/handleCopy] no dirent selected');
|
|
2687
|
+
return;
|
|
2688
|
+
}
|
|
2689
|
+
const absolutePath = dirent.path;
|
|
2690
|
+
// TODO handle copy error gracefully
|
|
2691
|
+
const files = [absolutePath];
|
|
2692
|
+
await writeNativeFiles('copy', files);
|
|
2693
|
+
return state;
|
|
2694
|
+
};
|
|
2695
|
+
|
|
2674
2696
|
const getFilePathElectron = async file => {
|
|
2675
2697
|
return invoke('GetFilePathElectron.getFilePathElectron', file);
|
|
2676
2698
|
};
|
|
@@ -3001,6 +3023,25 @@ const handlePointerDown = (state, button, x, y) => {
|
|
|
3001
3023
|
return state;
|
|
3002
3024
|
};
|
|
3003
3025
|
|
|
3026
|
+
const handleUpload = async (state, dirents) => {
|
|
3027
|
+
const {
|
|
3028
|
+
root,
|
|
3029
|
+
pathSeparator
|
|
3030
|
+
} = state;
|
|
3031
|
+
for (const dirent of dirents) {
|
|
3032
|
+
// TODO switch
|
|
3033
|
+
// TODO symlink might not be possible to be copied
|
|
3034
|
+
// TODO create folder if type is 2
|
|
3035
|
+
if (dirent.type === /* File */1) {
|
|
3036
|
+
// TODO reading text might be inefficient for binary files
|
|
3037
|
+
// but not sure how else to send them via jsonrpc
|
|
3038
|
+
const content = await dirent.file.text();
|
|
3039
|
+
const absolutePath = [root, dirent.file.name].join(pathSeparator);
|
|
3040
|
+
await writeFile(absolutePath, content);
|
|
3041
|
+
}
|
|
3042
|
+
}
|
|
3043
|
+
};
|
|
3044
|
+
|
|
3004
3045
|
const EmptyString = '';
|
|
3005
3046
|
|
|
3006
3047
|
const Fulfilled = 'fulfilled';
|
|
@@ -3155,6 +3196,45 @@ const loadContent = async (state, savedState) => {
|
|
|
3155
3196
|
};
|
|
3156
3197
|
};
|
|
3157
3198
|
|
|
3199
|
+
const ExplorerEditBox = FocusExplorerEditBox;
|
|
3200
|
+
|
|
3201
|
+
const setFocus = key => {
|
|
3202
|
+
return invoke('Focus.setFocus', key);
|
|
3203
|
+
};
|
|
3204
|
+
|
|
3205
|
+
const newDirent = async (state, editingType) => {
|
|
3206
|
+
// TODO make focus functional instead of side effect
|
|
3207
|
+
await setFocus(ExplorerEditBox);
|
|
3208
|
+
// TODO do it like vscode, select position between folders and files
|
|
3209
|
+
const {
|
|
3210
|
+
focusedIndex,
|
|
3211
|
+
items
|
|
3212
|
+
} = state;
|
|
3213
|
+
if (focusedIndex >= 0) {
|
|
3214
|
+
const dirent = items[focusedIndex];
|
|
3215
|
+
if (dirent.type === Directory) {
|
|
3216
|
+
// TODO handle error
|
|
3217
|
+
// @ts-ignore
|
|
3218
|
+
await undefined(state, dirent, focusedIndex);
|
|
3219
|
+
}
|
|
3220
|
+
}
|
|
3221
|
+
return {
|
|
3222
|
+
...state,
|
|
3223
|
+
editingIndex: focusedIndex,
|
|
3224
|
+
editingType,
|
|
3225
|
+
editingValue: ''
|
|
3226
|
+
};
|
|
3227
|
+
};
|
|
3228
|
+
|
|
3229
|
+
// TODO much shared logic with newFolder
|
|
3230
|
+
const newFile = state => {
|
|
3231
|
+
return newDirent(state, CreateFile);
|
|
3232
|
+
};
|
|
3233
|
+
|
|
3234
|
+
const newFolder = state => {
|
|
3235
|
+
return newDirent(state, CreateFolder);
|
|
3236
|
+
};
|
|
3237
|
+
|
|
3158
3238
|
const getContaingingFolder = (root, dirents, focusedIndex, pathSeparator) => {
|
|
3159
3239
|
if (focusedIndex < 0) {
|
|
3160
3240
|
return root;
|
|
@@ -3177,6 +3257,123 @@ const openContainingFolder = async state => {
|
|
|
3177
3257
|
return state;
|
|
3178
3258
|
};
|
|
3179
3259
|
|
|
3260
|
+
// TODO support multiselection and removing multiple dirents
|
|
3261
|
+
const removeDirent = async state => {
|
|
3262
|
+
if (state.focusedIndex < 0) {
|
|
3263
|
+
return state;
|
|
3264
|
+
}
|
|
3265
|
+
const dirent = getFocusedDirent$1(state);
|
|
3266
|
+
const absolutePath = dirent.path;
|
|
3267
|
+
try {
|
|
3268
|
+
// TODO handle error
|
|
3269
|
+
await remove(absolutePath);
|
|
3270
|
+
} catch (error) {
|
|
3271
|
+
// TODO vscode shows error as alert (no stacktrace) and retry button
|
|
3272
|
+
// maybe should show alert as well, but where to put stacktrace?
|
|
3273
|
+
// on web should probably show notification (dialog)
|
|
3274
|
+
// ErrorHandling.handleError(error)
|
|
3275
|
+
// await ErrorHandling.showErrorDialog(error)
|
|
3276
|
+
return;
|
|
3277
|
+
}
|
|
3278
|
+
// TODO avoid state mutation
|
|
3279
|
+
const newVersion = ++state.version;
|
|
3280
|
+
// TODO race condition
|
|
3281
|
+
// const newState = await loadContent(state:any)
|
|
3282
|
+
if (state.version !== newVersion || state.disposed) {
|
|
3283
|
+
return state;
|
|
3284
|
+
}
|
|
3285
|
+
// TODO is it possible to make this more functional instead of mutating state?
|
|
3286
|
+
// maybe every function returns a new state?
|
|
3287
|
+
const index = state.items.indexOf(dirent);
|
|
3288
|
+
let deleteEnd = index + 1;
|
|
3289
|
+
for (; deleteEnd < state.items.length; deleteEnd++) {
|
|
3290
|
+
if (state.items[deleteEnd].depth <= dirent.depth) {
|
|
3291
|
+
break;
|
|
3292
|
+
}
|
|
3293
|
+
}
|
|
3294
|
+
const deleteCount = deleteEnd - index;
|
|
3295
|
+
const newDirents = [...state.items];
|
|
3296
|
+
newDirents.splice(index, deleteCount);
|
|
3297
|
+
let indexToFocus = -1;
|
|
3298
|
+
if (newDirents.length === 0) {
|
|
3299
|
+
indexToFocus = -1;
|
|
3300
|
+
} else if (index < state.focusedIndex) {
|
|
3301
|
+
indexToFocus = state.focusedIndex - 1;
|
|
3302
|
+
} else if (index === state.focusedIndex) {
|
|
3303
|
+
indexToFocus = Math.max(state.focusedIndex - 1, 0);
|
|
3304
|
+
} else {
|
|
3305
|
+
indexToFocus = Math.max(state.focusedIndex - 1, 0);
|
|
3306
|
+
}
|
|
3307
|
+
return {
|
|
3308
|
+
...state,
|
|
3309
|
+
items: newDirents,
|
|
3310
|
+
focusedIndex: indexToFocus
|
|
3311
|
+
};
|
|
3312
|
+
};
|
|
3313
|
+
|
|
3314
|
+
const renameDirent = state => {
|
|
3315
|
+
const {
|
|
3316
|
+
focusedIndex,
|
|
3317
|
+
items
|
|
3318
|
+
} = state;
|
|
3319
|
+
const item = items[focusedIndex];
|
|
3320
|
+
// Focus.setFocus(FocusKey.ExplorerEditBox)
|
|
3321
|
+
return {
|
|
3322
|
+
...state,
|
|
3323
|
+
editingIndex: focusedIndex,
|
|
3324
|
+
editingType: Rename$1,
|
|
3325
|
+
editingValue: item.name
|
|
3326
|
+
};
|
|
3327
|
+
};
|
|
3328
|
+
|
|
3329
|
+
const getIconVirtualDom = (icon, type = Div) => {
|
|
3330
|
+
return {
|
|
3331
|
+
type,
|
|
3332
|
+
className: `MaskIcon MaskIcon${icon}`,
|
|
3333
|
+
role: None$2,
|
|
3334
|
+
childCount: 0
|
|
3335
|
+
};
|
|
3336
|
+
};
|
|
3337
|
+
|
|
3338
|
+
const getActionButtonVirtualDom = action => {
|
|
3339
|
+
const {
|
|
3340
|
+
id,
|
|
3341
|
+
icon,
|
|
3342
|
+
command
|
|
3343
|
+
} = action;
|
|
3344
|
+
return [{
|
|
3345
|
+
type: Button,
|
|
3346
|
+
className: IconButton,
|
|
3347
|
+
title: id,
|
|
3348
|
+
'data-command': command,
|
|
3349
|
+
childCount: 1
|
|
3350
|
+
}, getIconVirtualDom(icon)];
|
|
3351
|
+
};
|
|
3352
|
+
|
|
3353
|
+
const getActionVirtualDom = action => {
|
|
3354
|
+
switch (action.type) {
|
|
3355
|
+
case Button$2:
|
|
3356
|
+
return getActionButtonVirtualDom(action);
|
|
3357
|
+
default:
|
|
3358
|
+
return [];
|
|
3359
|
+
}
|
|
3360
|
+
};
|
|
3361
|
+
|
|
3362
|
+
const getActionsVirtualDom = actions => {
|
|
3363
|
+
return [{
|
|
3364
|
+
type: Div,
|
|
3365
|
+
className: Actions,
|
|
3366
|
+
role: ToolBar,
|
|
3367
|
+
childCount: actions.length
|
|
3368
|
+
}, ...actions.flatMap(getActionVirtualDom)];
|
|
3369
|
+
};
|
|
3370
|
+
|
|
3371
|
+
const renderActions = state => {
|
|
3372
|
+
const actions = getActions(state.root);
|
|
3373
|
+
const dom = getActionsVirtualDom(actions);
|
|
3374
|
+
return dom;
|
|
3375
|
+
};
|
|
3376
|
+
|
|
3180
3377
|
const getSavedRoot = (savedState, workspacePath) => {
|
|
3181
3378
|
return workspacePath;
|
|
3182
3379
|
};
|
|
@@ -3452,9 +3649,45 @@ const saveState = state => {
|
|
|
3452
3649
|
};
|
|
3453
3650
|
};
|
|
3454
3651
|
|
|
3652
|
+
const setDeltaY = (state, deltaY) => {
|
|
3653
|
+
const {
|
|
3654
|
+
itemHeight,
|
|
3655
|
+
height,
|
|
3656
|
+
items
|
|
3657
|
+
} = state;
|
|
3658
|
+
if (deltaY < 0) {
|
|
3659
|
+
deltaY = 0;
|
|
3660
|
+
} else if (deltaY > items.length * itemHeight - height) {
|
|
3661
|
+
deltaY = Math.max(items.length * itemHeight - height, 0);
|
|
3662
|
+
}
|
|
3663
|
+
if (state.deltaY === deltaY) {
|
|
3664
|
+
return state;
|
|
3665
|
+
}
|
|
3666
|
+
const minLineY = Math.round(deltaY / itemHeight);
|
|
3667
|
+
const maxLineY = minLineY + Math.round(height / itemHeight);
|
|
3668
|
+
return {
|
|
3669
|
+
...state,
|
|
3670
|
+
deltaY,
|
|
3671
|
+
minLineY,
|
|
3672
|
+
maxLineY
|
|
3673
|
+
};
|
|
3674
|
+
};
|
|
3675
|
+
|
|
3676
|
+
const updateEditingValue = (state, value) => {
|
|
3677
|
+
const editingIcon = getFileIcon({
|
|
3678
|
+
name: value
|
|
3679
|
+
});
|
|
3680
|
+
return {
|
|
3681
|
+
...state,
|
|
3682
|
+
editingValue: value,
|
|
3683
|
+
editingIcon
|
|
3684
|
+
};
|
|
3685
|
+
};
|
|
3686
|
+
|
|
3455
3687
|
const commandMap = {
|
|
3456
3688
|
'Explorer.acceptEdit': acceptEdit,
|
|
3457
3689
|
'Explorer.cancelEdit': cancelEdit,
|
|
3690
|
+
'Explorer.collapseAll': collapseAll$1,
|
|
3458
3691
|
'Explorer.copyPath': copyPath$1,
|
|
3459
3692
|
'Explorer.copyRelativePath': copyRelativePath$1,
|
|
3460
3693
|
'Explorer.expandAll': expandAll,
|
|
@@ -3464,6 +3697,7 @@ const commandMap = {
|
|
|
3464
3697
|
'Explorer.focusLast': focusLast,
|
|
3465
3698
|
'Explorer.focusNext': focusNext,
|
|
3466
3699
|
'Explorer.focusPrevious': focusPrevious,
|
|
3700
|
+
'Explorer.getActions': getActions,
|
|
3467
3701
|
'Explorer.getKeyBindings': getKeyBindings,
|
|
3468
3702
|
'Explorer.getMenuEntries': getMenuEntries,
|
|
3469
3703
|
'Explorer.getVirtualDom': getExplorerVirtualDom,
|
|
@@ -3476,18 +3710,25 @@ const commandMap = {
|
|
|
3476
3710
|
'Explorer.handleClickCurrentButKeepFocus': handleClickCurrentButKeepFocus,
|
|
3477
3711
|
'Explorer.handleClickOpenFolder': handleClickOpenFolder,
|
|
3478
3712
|
'Explorer.handleContextMenu': handleContextMenu,
|
|
3713
|
+
'Explorer.handleCopy': handleCopy,
|
|
3479
3714
|
'Explorer.handleDrop': handleDrop,
|
|
3480
3715
|
'Explorer.handleIconThemeChange': handleIconThemeChange,
|
|
3481
3716
|
'Explorer.handlePaste': handlePaste,
|
|
3482
3717
|
'Explorer.handlePointerDown': handlePointerDown,
|
|
3718
|
+
'Explorer.handleUpload': handleUpload,
|
|
3483
3719
|
'Explorer.handleWheel': handleWheel,
|
|
3484
3720
|
'Explorer.loadContent': loadContent,
|
|
3721
|
+
'Explorer.newFile': newFile,
|
|
3722
|
+
'Explorer.newFolder': newFolder,
|
|
3485
3723
|
'Explorer.openContainingFolder': openContainingFolder,
|
|
3486
3724
|
'Explorer.removeDirent': removeDirent,
|
|
3487
3725
|
'Explorer.renameDirent': renameDirent,
|
|
3726
|
+
'Explorer.renderActions': renderActions,
|
|
3488
3727
|
'Explorer.restoreState': restoreState,
|
|
3489
3728
|
'Explorer.revealItem': revealItem,
|
|
3490
|
-
'Explorer.saveState': saveState
|
|
3729
|
+
'Explorer.saveState': saveState,
|
|
3730
|
+
'Explorer.setDeltaY': setDeltaY,
|
|
3731
|
+
'Explorer.updateEditingValue': updateEditingValue
|
|
3491
3732
|
};
|
|
3492
3733
|
|
|
3493
3734
|
const listen = async () => {
|