Skip to content

Commit

Permalink
Lazily Load Files (#1438)
Browse files Browse the repository at this point in the history
* Remove `CEWorkspaceFile.children`, Lazily Load Files

* Fix navigator, update docs, add tests

* Notify observers only when needed

* Copy file when copying file

* Fix dummy file observer

* Remove Extraneous Print
  • Loading branch information
thecoolwinter authored Oct 5, 2023
1 parent 5122227 commit d1a712b
Show file tree
Hide file tree
Showing 29 changed files with 754 additions and 582 deletions.
26 changes: 15 additions & 11 deletions CodeEdit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@
58798285292ED0FB0085B254 /* TerminalEmulatorView+Coordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58798281292ED0FB0085B254 /* TerminalEmulatorView+Coordinator.swift */; };
58798286292ED0FB0085B254 /* SwiftTerm+Color+Init.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58798283292ED0FB0085B254 /* SwiftTerm+Color+Init.swift */; };
5879828A292ED15F0085B254 /* SwiftTerm in Frameworks */ = {isa = PBXBuildFile; productRef = 58798289292ED15F0085B254 /* SwiftTerm */; };
587B60F82934124200D5CD8F /* WorkspaceClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587B60F72934124100D5CD8F /* WorkspaceClientTests.swift */; };
587B60F82934124200D5CD8F /* CEWorkspaceFileManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587B60F72934124100D5CD8F /* CEWorkspaceFileManagerTests.swift */; };
587B61012934170A00D5CD8F /* UnitTests_Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587B61002934170A00D5CD8F /* UnitTests_Extensions.swift */; };
587B612E293419B700D5CD8F /* CodeFileTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587B612D293419B700D5CD8F /* CodeFileTests.swift */; };
587B9D57292FC27A00AC7927 /* FolderMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587B9D54292FC27A00AC7927 /* FolderMonitor.swift */; };
Expand Down Expand Up @@ -208,7 +208,7 @@
58822532292C280D00E83CDE /* UtilityAreaViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5882251C292C280D00E83CDE /* UtilityAreaViewModel.swift */; };
58822534292C280D00E83CDE /* CursorLocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5882251E292C280D00E83CDE /* CursorLocation.swift */; };
588847632992A2A200996D95 /* CEWorkspaceFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 588847622992A2A200996D95 /* CEWorkspaceFile.swift */; };
588847692992ABCA00996D95 /* Array+CEWorkspaceFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 588847682992ABCA00996D95 /* Array+CEWorkspaceFile.swift */; };
588847692992ABCA00996D95 /* Array+SortURLs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 588847682992ABCA00996D95 /* Array+SortURLs.swift */; };
5894E59729FEF7740077E59C /* CEWorkspaceFile+Recursion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5894E59629FEF7740077E59C /* CEWorkspaceFile+Recursion.swift */; };
58A2E40C29C3975D005CB615 /* CEWorkspaceFileIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A2E40629C3975D005CB615 /* CEWorkspaceFileIcon.swift */; };
58A5DF7D2931787A00D1BD5D /* ShellClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A5DF7C2931787A00D1BD5D /* ShellClient.swift */; };
Expand Down Expand Up @@ -314,6 +314,7 @@
6CAAF69429BCD78600A1F48A /* (null) in Sources */ = {isa = PBXBuildFile; };
6CABB19E29C5591D00340467 /* NSTableViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CABB19C29C5591D00340467 /* NSTableViewWrapper.swift */; };
6CABB1A129C5593800340467 /* OverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CABB1A029C5593800340467 /* OverlayView.swift */; };
6CB52DC92AC8DC3E002E75B3 /* CEWorkspaceFileManager+FileManagement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CB52DC82AC8DC3E002E75B3 /* CEWorkspaceFileManager+FileManagement.swift */; };
6CB9144B29BEC7F100BC47F2 /* (null) in Sources */ = {isa = PBXBuildFile; };
6CBA0D512A1BF524002C6FAA /* SegmentedControlImproved.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CBA0D502A1BF524002C6FAA /* SegmentedControlImproved.swift */; };
6CBD1BC62978DE53006639D5 /* Font+Caption3.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CBD1BC52978DE53006639D5 /* Font+Caption3.swift */; };
Expand Down Expand Up @@ -584,7 +585,7 @@
58798280292ED0FB0085B254 /* TerminalEmulatorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TerminalEmulatorView.swift; sourceTree = "<group>"; };
58798281292ED0FB0085B254 /* TerminalEmulatorView+Coordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TerminalEmulatorView+Coordinator.swift"; sourceTree = "<group>"; };
58798283292ED0FB0085B254 /* SwiftTerm+Color+Init.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SwiftTerm+Color+Init.swift"; sourceTree = "<group>"; };
587B60F72934124100D5CD8F /* WorkspaceClientTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WorkspaceClientTests.swift; sourceTree = "<group>"; };
587B60F72934124100D5CD8F /* CEWorkspaceFileManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CEWorkspaceFileManagerTests.swift; sourceTree = "<group>"; };
587B61002934170A00D5CD8F /* UnitTests_Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnitTests_Extensions.swift; sourceTree = "<group>"; };
587B612D293419B700D5CD8F /* CodeFileTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CodeFileTests.swift; sourceTree = "<group>"; };
587B9D54292FC27A00AC7927 /* FolderMonitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FolderMonitor.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -679,7 +680,7 @@
5882251C292C280D00E83CDE /* UtilityAreaViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UtilityAreaViewModel.swift; sourceTree = "<group>"; };
5882251E292C280D00E83CDE /* CursorLocation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CursorLocation.swift; sourceTree = "<group>"; };
588847622992A2A200996D95 /* CEWorkspaceFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CEWorkspaceFile.swift; sourceTree = "<group>"; };
588847682992ABCA00996D95 /* Array+CEWorkspaceFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+CEWorkspaceFile.swift"; sourceTree = "<group>"; };
588847682992ABCA00996D95 /* Array+SortURLs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+SortURLs.swift"; sourceTree = "<group>"; };
5894E59629FEF7740077E59C /* CEWorkspaceFile+Recursion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CEWorkspaceFile+Recursion.swift"; sourceTree = "<group>"; };
589F3E342936185400E1A4DA /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Platforms/MacOSX.platform/Developer/Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
58A2E40629C3975D005CB615 /* CEWorkspaceFileIcon.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CEWorkspaceFileIcon.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -770,6 +771,7 @@
6C97EBCB2978760400302F95 /* AcknowledgementsWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AcknowledgementsWindowController.swift; sourceTree = "<group>"; };
6CABB19C29C5591D00340467 /* NSTableViewWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NSTableViewWrapper.swift; path = CodeEdit/Features/QuickOpen/Views/NSTableViewWrapper.swift; sourceTree = SOURCE_ROOT; };
6CABB1A029C5593800340467 /* OverlayView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OverlayView.swift; sourceTree = "<group>"; };
6CB52DC82AC8DC3E002E75B3 /* CEWorkspaceFileManager+FileManagement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CEWorkspaceFileManager+FileManagement.swift"; sourceTree = "<group>"; };
6CBA0D502A1BF524002C6FAA /* SegmentedControlImproved.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SegmentedControlImproved.swift; sourceTree = "<group>"; };
6CBD1BC52978DE53006639D5 /* Font+Caption3.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Font+Caption3.swift"; sourceTree = "<group>"; };
6CC9E4B129B5669900C97388 /* Environment+ActiveEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Environment+ActiveEditor.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1548,17 +1550,17 @@
isa = PBXGroup;
children = (
587B61002934170A00D5CD8F /* UnitTests_Extensions.swift */,
587B60F52934124100D5CD8F /* WorkspaceClient */,
587B60F52934124100D5CD8F /* CEWorkspaceFileManager */,
);
path = Utils;
sourceTree = "<group>";
};
587B60F52934124100D5CD8F /* WorkspaceClient */ = {
587B60F52934124100D5CD8F /* CEWorkspaceFileManager */ = {
isa = PBXGroup;
children = (
587B60F72934124100D5CD8F /* WorkspaceClientTests.swift */,
587B60F72934124100D5CD8F /* CEWorkspaceFileManagerTests.swift */,
);
path = WorkspaceClient;
path = CEWorkspaceFileManager;
sourceTree = "<group>";
};
587B60FE293416C900D5CD8F /* Features */ = {
Expand Down Expand Up @@ -1891,6 +1893,7 @@
5894E59629FEF7740077E59C /* CEWorkspaceFile+Recursion.swift */,
58A2E40629C3975D005CB615 /* CEWorkspaceFileIcon.swift */,
58710158298EB80000951BA4 /* CEWorkspaceFileManager.swift */,
6CB52DC82AC8DC3E002E75B3 /* CEWorkspaceFileManager+FileManagement.swift */,
6C049A362A49E2DB00D42923 /* DirectoryEventStream.swift */,
);
path = Models;
Expand All @@ -1899,7 +1902,7 @@
588847672992AAB800996D95 /* Array */ = {
isa = PBXGroup;
children = (
588847682992ABCA00996D95 /* Array+CEWorkspaceFile.swift */,
588847682992ABCA00996D95 /* Array+SortURLs.swift */,
);
path = Array;
sourceTree = "<group>";
Expand Down Expand Up @@ -3173,7 +3176,7 @@
B6EA1FF829DB78DB001BF195 /* ThemeSettingThemeRow.swift in Sources */,
587B9E7629301D8F00AC7927 /* GitTime.swift in Sources */,
587B9E5D29301D8F00AC7927 /* GitLabUserRouter.swift in Sources */,
588847692992ABCA00996D95 /* Array+CEWorkspaceFile.swift in Sources */,
588847692992ABCA00996D95 /* Array+SortURLs.swift in Sources */,
58822530292C280D00E83CDE /* FilterTextField.swift in Sources */,
6C82D6B929BFE34900495C54 /* HelpCommands.swift in Sources */,
6C147C4929A32A080089B630 /* EditorLayoutView.swift in Sources */,
Expand Down Expand Up @@ -3228,6 +3231,7 @@
6CDA84AD284C1BA000C1CC3A /* EditorTabBarContextMenu.swift in Sources */,
6C81916729B3E80700B75C92 /* ModifierKeysObserver.swift in Sources */,
587B9E8129301D8F00AC7927 /* PublicKey.swift in Sources */,
6CB52DC92AC8DC3E002E75B3 /* CEWorkspaceFileManager+FileManagement.swift in Sources */,
58F2EB0B292FB2B0004A9BDE /* AccountsSettings.swift in Sources */,
5882252A292C280D00E83CDE /* StatusBarToggleUtilityAreaButton.swift in Sources */,
B6AB09A12AAABAAE0003A3A6 /* EditorTabs.swift in Sources */,
Expand All @@ -3247,7 +3251,7 @@
buildActionMask = 2147483647;
files = (
583E528C29361B39001AB554 /* CodeEditUITests.swift in Sources */,
587B60F82934124200D5CD8F /* WorkspaceClientTests.swift in Sources */,
587B60F82934124200D5CD8F /* CEWorkspaceFileManagerTests.swift in Sources */,
587B61012934170A00D5CD8F /* UnitTests_Extensions.swift in Sources */,
283BDCC52972F236002AFF81 /* AcknowledgementsTests.swift in Sources */,
4EE96ECB2960565E00FFBEA8 /* DocumentsUnitTests.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/ChimeHQ/Rearrange",
"state" : {
"revision" : "8f97f721d8a08c6e01ab9f7460e53819bef72dfa",
"version" : "1.5.3"
"revision" : "0fb658e721c68495f6340c211cc6d4719e6b52d8",
"version" : "1.6.0"
}
},
{
Expand Down Expand Up @@ -248,8 +248,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/ChimeHQ/TextFormation",
"state" : {
"revision" : "2b56879bd7aa753a2ba97b271f456c59614b39d4",
"version" : "0.8.0"
"revision" : "b4987856bc860643ac2c9cdbc7d5f3e9ade68377",
"version" : "0.8.1"
}
},
{
Expand Down
131 changes: 45 additions & 86 deletions CodeEdit/Features/CEWorkspace/Models/CEWorkspaceFile+Recursion.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,113 +8,45 @@
import Foundation

extension CEWorkspaceFile {

func childrenDescription(tabCount: Int) -> String {
var myDetails = "\(String(repeating: "| ", count: max(tabCount - 1, 0)))\(tabCount != 0 ? "╰--" : "")"
myDetails += "\(url.path)"
if !self.isFolder { // if im a file, just return the url
return myDetails
} else { // if im a folder, return the url and its children's details
var childDetails = "\(myDetails)"
for child in children ?? [] {
childDetails += "\n\(child.childrenDescription(tabCount: tabCount + 1))"
}
return childDetails
}
}

/// Flattens the children of ``self`` recursively with depth.
/// Flattens the children of `self` recursively with depth.
/// - Parameters:
/// - depth: An int that indicates the how deep the tree files need to be flattened
/// - ignoringFolders: A boolean on whether to ignore files that are Folders
/// - fileManager: The workspace's file manager to use.
/// - Returns: An array of flattened `CEWorkspaceFiles`
func flattenedChildren(withDepth depth: Int, ignoringFolders: Bool) -> [CEWorkspaceFile] {
func flattenedChildren(
withDepth depth: Int,
ignoringFolders: Bool,
using fileManager: CEWorkspaceFileManager
) -> [CEWorkspaceFile] {
guard depth > 0 else { return [] }
guard self.isFolder else { return [self] }
var childItems: [CEWorkspaceFile] = ignoringFolders ? [] : [self]
children?.forEach { child in
fileManager.childrenOfFile(self)?.forEach { child in
childItems.append(contentsOf: child.flattenedChildren(
withDepth: depth - 1,
ignoringFolders: ignoringFolders
ignoringFolders: ignoringFolders,
using: fileManager
))
}
return childItems
}

/// Returns a list of `CEWorkspaceFiles` that are sibilings of ``self``.
/// Returns a list of `CEWorkspaceFiles` that are sibilings of `self`.
/// The `height` parameter lets the function navigate up the folder hierarchy to
/// select a starting point from which it should start flettening the items.
/// - Parameters:
/// - height: `Int` that tells where to start in the hierarchy
/// - ignoringFolders: Wether the sibling folders should be flattened
/// - fileManager: The workspace's file manager to use.
/// - Returns: A list of `FileSystemItems`
func flattenedSiblings(withHeight height: Int, ignoringFolders: Bool) -> [CEWorkspaceFile] {
func flattenedSiblings(
withHeight height: Int,
ignoringFolders: Bool,
using fileManager: CEWorkspaceFileManager
) -> [CEWorkspaceFile] {
let topMostParent = self.getParent(withHeight: height)
return topMostParent.flattenedChildren(withDepth: height, ignoringFolders: ignoringFolders)
}

/// Recursive function that returns the number of children
/// that contain the `searchString` in their path or their subitems' paths.
/// Returns `0` if the item is not a folder.
/// - Parameters:
/// - searchString: The string
/// - ignoredStrings: The prefixes to ignore if they prefix file names
/// - Returns: The number of children that match the conditiions
func appearanceWithinChildrenOf(searchString: String, ignoredStrings: [String] = [".", "~"]) -> Int {
var count = 0
guard self.isFolder else { return 0 }
for child in self.children ?? [] {
var isIgnored: Bool = false
for ignoredString in ignoredStrings where child.name.hasPrefix(ignoredString) {
isIgnored = true // can use regex later
}

if isIgnored {
continue
}

guard !searchString.isEmpty else { count += 1; continue }
if child.isFolder {
count += child.appearanceWithinChildrenOf(searchString: searchString) > 0 ? 1 : 0
} else {
count += child.name.lowercased().contains(searchString.lowercased()) ? 1 : 0
}
}
return count
}

/// Function that returns an array of the children
/// that contain the `searchString` in their path or their subitems' paths.
/// Similar to `appearanceWithinChildrenOf(searchString: String)`
/// Returns `[]` if the item is not a folder.
/// - Parameter searchString: The string
/// - Parameter ignoredStrings: The prefixes to ignore if they prefix file names
/// - Returns: The children that match the conditiions
func childrenSatisfying(searchString: String, ignoredStrings: [String] = [".", "~"]) -> [CEWorkspaceFile] {
var satisfyingChildren: [CEWorkspaceFile] = []
guard self.isFolder else { return [] }
for child in self.children ?? [] {
var isIgnored: Bool = false
for ignoredString in ignoredStrings where child.name.hasPrefix(ignoredString) {
isIgnored = true // can use regex later
}

if isIgnored {
continue
}

guard !searchString.isEmpty else { satisfyingChildren.append(child); continue }
if child.isFolder {
if child.appearanceWithinChildrenOf(searchString: searchString) > 0 {
satisfyingChildren.append(child)
}
} else {
if child.name.lowercased().contains(searchString.lowercased()) {
satisfyingChildren.append(child)
}
}
}
return satisfyingChildren
return topMostParent.flattenedChildren(withDepth: height, ignoringFolders: ignoringFolders, using: fileManager)
}

/// Using the current instance of `FileSystemItem` it will walk back up the Workspace file hiarchy
Expand All @@ -131,4 +63,31 @@ extension CEWorkspaceFile {
return topmostParent
}

#if DEBUG
/// Print a debug description of the file.
/// - Parameters:
/// - tabCount: The number of tabs to tab the description over (for recursive calls)
/// - fileManager: The file manager to use to find children.
/// - Returns: A string describing the file and it's children.
/// - Authors: Mattijs Eikelenboom, KaiTheRedNinja. *Moved from 7c27b1e*
func childrenDescription(tabCount: Int = 0, using fileManager: CEWorkspaceFileManager) -> String {
var myDetails = "\(String(repeating: "| ", count: max(tabCount - 1, 0)))\(tabCount != 0 ? "╰--" : "")"
myDetails += "\(url.path(percentEncoded: false))"
if !self.isFolder { // if im a file, just return the url
return myDetails
} else { // if im a folder, return the url and its children's details
var childDetails = "\(myDetails)"
if fileManager.hasLoadedChildrenFor(file: self) {
for child in fileManager.childrenOfFile(self) ?? [] {
childDetails += "\n\(child.childrenDescription(tabCount: tabCount + 1, using: fileManager))"
}
} else {
// Disabling for debug line.
// swiftlint:disable:next line_length
childDetails += "\n\(String(repeating: "| ", count: max(tabCount - 1, 0)))\(tabCount != 0 ? "╰--" : "") Children Not Loaded"
}
return childDetails
}
}
#endif
}
Loading

0 comments on commit d1a712b

Please sign in to comment.