@lvce-editor/explorer-view 7.26.0 → 7.27.1

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.
@@ -2816,6 +2816,7 @@ const collapseAll = async state => {
2816
2816
  const newDirents = items.filter(isTopLevel).map(toCollapsedDirent);
2817
2817
  return {
2818
2818
  ...state,
2819
+ expandedPaths: [],
2819
2820
  focusedIndex: 0,
2820
2821
  items: newDirents
2821
2822
  };
@@ -3717,6 +3718,34 @@ const getVisibleExplorerItems = (items, minLineY, maxLineY, focusedIndex, editin
3717
3718
  return visible;
3718
3719
  };
3719
3720
 
3721
+ const syncExpandedPaths = state => {
3722
+ const {
3723
+ expandedPaths: oldExpandedPaths,
3724
+ items,
3725
+ preserveExpandState
3726
+ } = state;
3727
+ if (!preserveExpandState) {
3728
+ return state;
3729
+ }
3730
+ const expandedPaths = new Set(oldExpandedPaths);
3731
+ for (const item of items) {
3732
+ const type = normalizeDirentType(item.type);
3733
+ if (type === DirectoryExpanded || type === DirectoryExpanding) {
3734
+ expandedPaths.add(item.path);
3735
+ } else if (type === Directory || type === SymLinkFolder) {
3736
+ expandedPaths.delete(item.path);
3737
+ }
3738
+ }
3739
+ const newExpandedPaths = [...expandedPaths];
3740
+ if (newExpandedPaths.length === oldExpandedPaths.length && newExpandedPaths.every((path, index) => path === oldExpandedPaths[index])) {
3741
+ return state;
3742
+ }
3743
+ return {
3744
+ ...state,
3745
+ expandedPaths: newExpandedPaths
3746
+ };
3747
+ };
3748
+
3720
3749
  const {
3721
3750
  get,
3722
3751
  getCommandIds,
@@ -3755,6 +3784,9 @@ const enqueueCommand = async (id, command) => {
3755
3784
  const hasSameVisibleExplorerItemInputs = (oldState, newState) => {
3756
3785
  return oldState.items === newState.items && oldState.minLineY === newState.minLineY && oldState.height === newState.height && oldState.itemHeight === newState.itemHeight && oldState.focusedIndex === newState.focusedIndex && oldState.editingIndex === newState.editingIndex && oldState.editingSessionId === newState.editingSessionId && oldState.editingIcon === newState.editingIcon && oldState.cutItems === newState.cutItems && oldState.editingErrorMessage === newState.editingErrorMessage && oldState.dropTargets === newState.dropTargets && oldState.fileIconCache === newState.fileIconCache && oldState.decorations === newState.decorations && oldState.useChevrons === newState.useChevrons && oldState.sourceControlIgnoredUris === newState.sourceControlIgnoredUris;
3757
3786
  };
3787
+ const hasSameDragDataInputs = (oldState, newState) => {
3788
+ return oldState.isPointerDown === newState.isPointerDown && oldState.pointerDownIndex === newState.pointerDownIndex;
3789
+ };
3758
3790
  const updateGitIgnoredUris = (state, generation, sourceControlIgnoredUris) => {
3759
3791
  const {
3760
3792
  gitIgnoreGeneration
@@ -3821,8 +3853,9 @@ const wrapListItemCommandInternal = (fn, queued) => {
3821
3853
  const {
3822
3854
  newState
3823
3855
  } = get(id);
3824
- const rawUpdatedState = await fn(newState, ...args);
3825
- const completion = take(rawUpdatedState);
3856
+ const commandState = await fn(newState, ...args);
3857
+ const completion = take(commandState);
3858
+ const rawUpdatedState = syncExpandedPaths(commandState);
3826
3859
  if (newState === rawUpdatedState) {
3827
3860
  return {
3828
3861
  completion
@@ -3852,7 +3885,7 @@ const wrapListItemCommandInternal = (fn, queued) => {
3852
3885
  const intermediate = get(id);
3853
3886
  set$1(id, intermediate.oldState, updatedState, intermediate.scheduledState);
3854
3887
  if (hasSameVisibleExplorerItemInputs(intermediate.newState, updatedState)) {
3855
- if (updatedState.inputSource === User) {
3888
+ if (updatedState.inputSource === User || !hasSameDragDataInputs(intermediate.newState, updatedState)) {
3856
3889
  set$1(id, intermediate.oldState, updatedState);
3857
3890
  }
3858
3891
  return {
@@ -3938,6 +3971,7 @@ const create = (id, uri, x, y, width, height, args, parentUid, platform = 0, ass
3938
3971
  errorMessageTop: 0,
3939
3972
  errorMessageWidth: 0,
3940
3973
  excluded: [],
3974
+ expandedPaths: [],
3941
3975
  fileIconCache: Object.create(null),
3942
3976
  fileIconWidth: 16,
3943
3977
  finalDeltaY: 0,
@@ -3969,6 +4003,7 @@ const create = (id, uri, x, y, width, height, args, parentUid, platform = 0, ass
3969
4003
  pathSeparator: Slash,
3970
4004
  platform,
3971
4005
  pointerDownIndex: -1,
4006
+ preserveExpandState: true,
3972
4007
  root: '',
3973
4008
  scrollBarActive: false,
3974
4009
  scrollBarHeight: 0,
@@ -4688,11 +4723,82 @@ const handleArrowRightDirectoryExpanded = (state, dirent) => {
4688
4723
  return state;
4689
4724
  };
4690
4725
 
4726
+ const getPathDirentsMap = async allPaths => {
4727
+ const pathToDirents = Object.create(null);
4728
+ await Promise.all(allPaths.map(async path => {
4729
+ try {
4730
+ const dirents = await readDirWithFileTypes(path);
4731
+ pathToDirents[path] = dirents;
4732
+ } catch {
4733
+ // ignore
4734
+ }
4735
+ }));
4736
+ return pathToDirents;
4737
+ };
4738
+
4739
+ const restoreDirentType = (rawDirentType, path, expandedPaths) => {
4740
+ if (rawDirentType === Directory && expandedPaths.includes(path)) {
4741
+ return DirectoryExpanded;
4742
+ }
4743
+ return rawDirentType;
4744
+ };
4745
+
4746
+ const getProtoMapInternal = (root, pathToDirents, expandedPaths, depth, excluded = [], workspaceRoot = root) => {
4747
+ if (!(root in pathToDirents)) {
4748
+ return [];
4749
+ }
4750
+ const items = (pathToDirents[root] || []).filter(item => !isExcluded(workspaceRoot, join2(root, item.name), excluded));
4751
+ const protoMap = [];
4752
+ for (let i = 0; i < items.length; i++) {
4753
+ const item = items[i];
4754
+ const path = join2(root, item.name);
4755
+ const displayDirent = {
4756
+ depth,
4757
+ icon: '',
4758
+ name: item.name,
4759
+ path,
4760
+ posInSet: i + 1,
4761
+ selected: false,
4762
+ setSize: items.length,
4763
+ type: restoreDirentType(item.type, path, expandedPaths)
4764
+ };
4765
+ const children = getProtoMapInternal(path, pathToDirents, expandedPaths, depth + 1, excluded, workspaceRoot);
4766
+ protoMap.push(displayDirent, ...children);
4767
+ }
4768
+ return protoMap;
4769
+ };
4770
+
4771
+ const sortPathDirentsMap = map => {
4772
+ const sortedMap = Object.create(null);
4773
+ for (const [key, value] of Object.entries(map)) {
4774
+ const sorted = sortExplorerItems(value);
4775
+ sortedMap[key] = sorted;
4776
+ }
4777
+ return sortedMap;
4778
+ };
4779
+
4780
+ const getRestoredChildDirents = async (state, dirent) => {
4781
+ const {
4782
+ excluded,
4783
+ expandedPaths,
4784
+ pathSeparator,
4785
+ preserveExpandState,
4786
+ root
4787
+ } = state;
4788
+ const descendantPrefix = dirent.path.endsWith(pathSeparator) ? dirent.path : `${dirent.path}${pathSeparator}`;
4789
+ const descendantExpandedPaths = preserveExpandState ? expandedPaths.filter(path => path.startsWith(descendantPrefix)) : [];
4790
+ if (descendantExpandedPaths.length === 0) {
4791
+ return getChildDirents(pathSeparator, dirent.path, dirent.depth, excluded, root);
4792
+ }
4793
+ const pathToDirents = await getPathDirentsMap([dirent.path, ...descendantExpandedPaths]);
4794
+ const sortedPathDirents = sortPathDirentsMap(pathToDirents);
4795
+ return getProtoMapInternal(dirent.path, sortedPathDirents, descendantExpandedPaths, dirent.depth + 1, excluded, root);
4796
+ };
4691
4797
  const handleClickDirectory = async (state, dirent, index, keepFocus) => {
4692
4798
  // @ts-ignore
4693
4799
  dirent.type = DirectoryExpanding;
4694
4800
  // TODO handle error
4695
- const dirents = await getChildDirents(state.pathSeparator, dirent.path, dirent.depth, state.excluded, state.root);
4801
+ const dirents = await getRestoredChildDirents(state, dirent);
4696
4802
  const state2 = state;
4697
4803
  if (!state2) {
4698
4804
  return state;
@@ -4989,19 +5095,6 @@ const getExpandedDirents = items => {
4989
5095
  return items.filter(isExpanded);
4990
5096
  };
4991
5097
 
4992
- const getPathDirentsMap = async allPaths => {
4993
- const pathToDirents = Object.create(null);
4994
- await Promise.all(allPaths.map(async path => {
4995
- try {
4996
- const dirents = await readDirWithFileTypes(path);
4997
- pathToDirents[path] = dirents;
4998
- } catch {
4999
- // ignore
5000
- }
5001
- }));
5002
- return pathToDirents;
5003
- };
5004
-
5005
5098
  const getPath = item => {
5006
5099
  return item.path;
5007
5100
  };
@@ -5010,62 +5103,23 @@ const getPaths = items => {
5010
5103
  return items.map(getPath);
5011
5104
  };
5012
5105
 
5013
- const restoreDirentType = (rawDirentType, path, expandedPaths) => {
5014
- if (rawDirentType === Directory && expandedPaths.includes(path)) {
5015
- return DirectoryExpanded;
5016
- }
5017
- return rawDirentType;
5018
- };
5019
-
5020
- const getProtoMapInternal = (root, pathToDirents, expandedPaths, depth, excluded = [], workspaceRoot = root) => {
5021
- if (!(root in pathToDirents)) {
5022
- return [];
5023
- }
5024
- const items = (pathToDirents[root] || []).filter(item => !isExcluded(workspaceRoot, join2(root, item.name), excluded));
5025
- const protoMap = [];
5026
- for (let i = 0; i < items.length; i++) {
5027
- const item = items[i];
5028
- const path = join2(root, item.name);
5029
- const displayDirent = {
5030
- depth,
5031
- icon: '',
5032
- name: item.name,
5033
- path,
5034
- posInSet: i + 1,
5035
- selected: false,
5036
- setSize: items.length,
5037
- type: restoreDirentType(item.type, path, expandedPaths)
5038
- };
5039
- const children = getProtoMapInternal(path, pathToDirents, expandedPaths, depth + 1, excluded, workspaceRoot);
5040
- protoMap.push(displayDirent, ...children);
5041
- }
5042
- return protoMap;
5043
- };
5044
-
5045
5106
  const getProtoMap = (root, pathToDirents, expandedPaths, excluded = []) => {
5046
5107
  return getProtoMapInternal(root, pathToDirents, expandedPaths, 1, excluded, root);
5047
5108
  };
5048
5109
 
5049
- const sortPathDirentsMap = map => {
5050
- const sortedMap = Object.create(null);
5051
- for (const [key, value] of Object.entries(map)) {
5052
- const sorted = sortExplorerItems(value);
5053
- sortedMap[key] = sorted;
5054
- }
5055
- return sortedMap;
5056
- };
5057
-
5058
5110
  const refresh = async state => {
5059
5111
  const {
5060
5112
  excluded,
5113
+ expandedPaths: preservedExpandedPaths,
5061
5114
  focusedIndex,
5062
5115
  gitIgnoreDecorations,
5063
5116
  items,
5064
5117
  pathSeparator,
5118
+ preserveExpandState,
5065
5119
  root
5066
5120
  } = state;
5067
- const expandedDirents = getExpandedDirents(items);
5068
- const expandedPaths = getPaths(expandedDirents);
5121
+ const legacyExpandedPaths = getPaths(getExpandedDirents(items));
5122
+ const expandedPaths = preserveExpandState ? preservedExpandedPaths : legacyExpandedPaths;
5069
5123
  const allPaths = [root, ...expandedPaths];
5070
5124
  const pathToDirents = await getPathDirentsMap(allPaths);
5071
5125
  const sortedPathDirents = sortPathDirentsMap(pathToDirents);
@@ -5697,6 +5751,8 @@ const getSettings = async () => {
5697
5751
  const excluded = getExcluded(excludedRaw);
5698
5752
  const gitIgnoreDecorationsRaw = await invoke$3('Preferences.get', 'explorer.gitIgnoreDecorations');
5699
5753
  const gitIgnoreDecorations = gitIgnoreDecorationsRaw === false ? false : true;
5754
+ const preserveExpandStateRaw = await invoke$3('Preferences.get', 'explorer.preserveExpandState');
5755
+ const preserveExpandState = preserveExpandStateRaw === false ? false : true;
5700
5756
  const sourceControlDecorationsRaw = await invoke$3('Preferences.get', 'explorer.sourceControlDecorations');
5701
5757
  const sourceControlDecorations = sourceControlDecorationsRaw === false ? false : true;
5702
5758
  return {
@@ -5704,6 +5760,7 @@ const getSettings = async () => {
5704
5760
  confirmPaste,
5705
5761
  excluded,
5706
5762
  gitIgnoreDecorations,
5763
+ preserveExpandState,
5707
5764
  sourceControlDecorations,
5708
5765
  useChevrons
5709
5766
  };
@@ -5784,17 +5841,16 @@ const getSavedExpandedPaths = (savedState, root) => {
5784
5841
  return [];
5785
5842
  }
5786
5843
  if (savedState && savedState.expandedPaths && Array.isArray(savedState.expandedPaths)) {
5787
- return savedState.expandedPaths;
5844
+ return savedState.expandedPaths.filter(path => typeof path === 'string');
5788
5845
  }
5789
5846
  return [];
5790
5847
  };
5791
- const restoreExpandedState = async (savedState, root, pathSeparator, excluded) => {
5848
+ const restoreExpandedState = async (expandedPaths, root, pathSeparator, excluded) => {
5792
5849
  // TODO read all opened folders in parallel
5793
5850
  // ignore ENOENT errors
5794
5851
  // ignore ENOTDIR errors
5795
5852
  // merge all dirents
5796
5853
  // restore scroll location
5797
- const expandedPaths = getSavedExpandedPaths(savedState, root);
5798
5854
  if (root === EmptyString) {
5799
5855
  return [];
5800
5856
  }
@@ -5807,28 +5863,44 @@ const restoreExpandedState = async (savedState, root, pathSeparator, excluded) =
5807
5863
  return dirents;
5808
5864
  };
5809
5865
 
5866
+ const getExpandedPaths = (savedState, root, currentRoot, currentExpandedPaths, preserveExpandState) => {
5867
+ if (!preserveExpandState) {
5868
+ return [];
5869
+ }
5870
+ if (savedState !== undefined && savedState !== null) {
5871
+ return getSavedExpandedPaths(savedState, root);
5872
+ }
5873
+ if (currentRoot === root) {
5874
+ return currentExpandedPaths;
5875
+ }
5876
+ return [];
5877
+ };
5810
5878
  const loadContent = async (state, savedState) => {
5811
5879
  const {
5812
5880
  assetDir,
5881
+ expandedPaths: currentExpandedPaths,
5813
5882
  height,
5814
5883
  itemHeight,
5815
- platform
5884
+ platform,
5885
+ root: currentRoot
5816
5886
  } = state;
5817
5887
  const {
5818
5888
  confirmDelete,
5819
5889
  excluded,
5820
5890
  gitIgnoreDecorations,
5891
+ preserveExpandState,
5821
5892
  sourceControlDecorations,
5822
5893
  useChevrons
5823
5894
  } = await getSettings();
5824
5895
  const workspacePath = await getWorkspacePath();
5825
5896
  const root = getSavedRoot(savedState, workspacePath);
5897
+ const expandedPaths = getExpandedPaths(savedState, root, currentRoot, currentExpandedPaths, preserveExpandState);
5826
5898
  try {
5827
5899
  // TODO path separator could be restored from saved state
5828
5900
  const [pathSeparator, isReadonly$1] = await Promise.all([getPathSeparator(root),
5829
5901
  // TODO only load path separator once
5830
5902
  root === '' ? false : isReadonly(root)]);
5831
- const restoredDirents = await restoreExpandedState(savedState, root, pathSeparator, excluded);
5903
+ const restoredDirents = await restoreExpandedState(expandedPaths, root, pathSeparator, excluded);
5832
5904
  const rawDeltaY = getRestoredDeltaY(savedState);
5833
5905
  const maxDeltaY = Math.max(restoredDirents.length * itemHeight - height, 0);
5834
5906
  const deltaY = Math.min(Math.max(rawDeltaY, 0), maxDeltaY);
@@ -5844,6 +5916,7 @@ const loadContent = async (state, savedState) => {
5844
5916
  errorCode: '',
5845
5917
  errorMessage: '',
5846
5918
  excluded,
5919
+ expandedPaths,
5847
5920
  gitIgnoreDecorations,
5848
5921
  hasError: false,
5849
5922
  initial: false,
@@ -5852,6 +5925,7 @@ const loadContent = async (state, savedState) => {
5852
5925
  maxIndent: 10,
5853
5926
  minLineY,
5854
5927
  pathSeparator,
5928
+ preserveExpandState,
5855
5929
  root,
5856
5930
  sourceControlIgnoredUris,
5857
5931
  useChevrons
@@ -5864,11 +5938,13 @@ const loadContent = async (state, savedState) => {
5864
5938
  confirmDelete,
5865
5939
  errorCode,
5866
5940
  errorMessage,
5941
+ expandedPaths,
5867
5942
  gitIgnoreDecorations,
5868
5943
  hasError: true,
5869
5944
  initial: false,
5870
5945
  isReadonly: false,
5871
5946
  items: [],
5947
+ preserveExpandState,
5872
5948
  root,
5873
5949
  useChevrons
5874
5950
  };
@@ -6200,8 +6276,10 @@ const expandTargetFolder = (items, targetFolder) => {
6200
6276
  };
6201
6277
  const handleInternalDrop = async (state, sourcePaths, index) => {
6202
6278
  const {
6279
+ expandedPaths: oldExpandedPaths,
6203
6280
  isReadonly,
6204
- items
6281
+ items,
6282
+ preserveExpandState
6205
6283
  } = state;
6206
6284
  if (isReadonly) {
6207
6285
  return state;
@@ -6218,8 +6296,10 @@ const handleInternalDrop = async (state, sourcePaths, index) => {
6218
6296
  await rename$1(operation.from, operation.path);
6219
6297
  }
6220
6298
  const expandedItems = expandTargetFolder(items, targetFolder);
6299
+ const expandedPaths = preserveExpandState ? [...new Set([...oldExpandedPaths, targetFolder])] : oldExpandedPaths;
6221
6300
  const updated = await refresh({
6222
6301
  ...state,
6302
+ expandedPaths,
6223
6303
  items: expandedItems
6224
6304
  });
6225
6305
  return {
@@ -7054,9 +7134,13 @@ const handleWheel = (state, deltaMode, deltaY) => {
7054
7134
  };
7055
7135
 
7056
7136
  const handleWorkspaceChange = async (state, _workspacePath, savedState) => {
7137
+ const {
7138
+ root
7139
+ } = state;
7057
7140
  const newRoot = await getWorkspacePath();
7058
- const state1 = {
7141
+ const state1 = newRoot === root ? state : {
7059
7142
  ...state,
7143
+ expandedPaths: [],
7060
7144
  root: newRoot
7061
7145
  };
7062
7146
  const newState = await loadContent(state1, savedState);
@@ -8215,12 +8299,14 @@ const isExpandedDirectory = dirent => {
8215
8299
  const saveState = state => {
8216
8300
  const {
8217
8301
  deltaY,
8302
+ expandedPaths: preservedExpandedPaths,
8218
8303
  items,
8219
8304
  maxLineY,
8220
8305
  minLineY,
8306
+ preserveExpandState,
8221
8307
  root
8222
8308
  } = state;
8223
- const expandedPaths = items.filter(isExpandedDirectory).map(getPath);
8309
+ const expandedPaths = preserveExpandState ? preservedExpandedPaths : items.filter(isExpandedDirectory).map(getPath);
8224
8310
  return {
8225
8311
  deltaY,
8226
8312
  expandedPaths,
@@ -31,6 +31,14 @@
31
31
  "type": 3,
32
32
  "value": "true"
33
33
  },
34
+ {
35
+ "category": "explorer",
36
+ "description": "Controls whether expanded folders are preserved for the current workspace when the Explorer rebuilds and across editor restarts.",
37
+ "heading": "Preserve Expand State",
38
+ "id": "explorer.preserveExpandState",
39
+ "type": 3,
40
+ "value": "true"
41
+ },
34
42
  {
35
43
  "category": "explorer",
36
44
  "description": "Controls whether source control decorations are shown in the Explorer.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/explorer-view",
3
- "version": "7.26.0",
3
+ "version": "7.27.1",
4
4
  "description": "Explorer Worker",
5
5
  "repository": {
6
6
  "type": "git",