Skip to content

Commit 9d743ec

Browse files
LeonardoLarranagaLeonardo Larrañagaaustincondiff
authored
Select multiple files in the project navigator (#1906)
* Selecting multiple files. Deleting, duplications, new folder from selection where supported, open in finder, open in external editor. * Select multiple files. Delete, duplicate, new folder from selection, open in Finder, open in external editor. * Update comments in ProjectNavigatorMenuActions.swift * Changed batch delete so that the user only gets asked for a single confirmation instead of one for every file. * Update comments CEWorkspaceFileManager+FileManagement.swift * Update CodeEdit/Features/NavigatorArea/ProjectNavigator/OutlineView/ProjectNavigatorMenuActions.swift Co-authored-by: Austin Condiff <austin.condiff@gmail.com> * Update CodeEdit/Features/CEWorkspace/Models/CEWorkspaceFileManager+FileManagement.swift Co-authored-by: Austin Condiff <austin.condiff@gmail.com> * Update CodeEdit/Features/NavigatorArea/ProjectNavigator/OutlineView/ProjectNavigatorMenuActions.swift Co-authored-by: Austin Condiff <austin.condiff@gmail.com> * Fixed SwiftLint errors * Update CEWorkspaceFileManager+FileManagement.swift --------- Co-authored-by: Leonardo Larrañaga <leonardolarranaga@icloud.com> Co-authored-by: Austin Condiff <austin.condiff@gmail.com>
1 parent 51669a3 commit 9d743ec

File tree

6 files changed

+251
-99
lines changed

6 files changed

+251
-99
lines changed

CodeEdit.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@
9797
30E6D0012A6E505200A58B20 /* NavigatorSidebarViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30E6D0002A6E505200A58B20 /* NavigatorSidebarViewModel.swift */; };
9898
3E0196732A3921AC002648D8 /* codeedit_shell_integration_rc.zsh in Resources */ = {isa = PBXBuildFile; fileRef = 3E0196722A3921AC002648D8 /* codeedit_shell_integration_rc.zsh */; };
9999
3E01967A2A392B45002648D8 /* codeedit_shell_integration.bash in Resources */ = {isa = PBXBuildFile; fileRef = 3E0196792A392B45002648D8 /* codeedit_shell_integration.bash */; };
100+
4A6F0DB52CBA462B00499627 /* ProjectNavigatorMenuActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A6F0DB42CBA462B00499627 /* ProjectNavigatorMenuActions.swift */; };
100101
4E7F066629602E7B00BB3C12 /* CodeEditSplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E7F066529602E7B00BB3C12 /* CodeEditSplitViewController.swift */; };
101102
4EE96ECB2960565E00FFBEA8 /* DocumentsUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE96ECA2960565E00FFBEA8 /* DocumentsUnitTests.swift */; };
102103
4EE96ECE296059E000FFBEA8 /* NSHapticFeedbackPerformerMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE96ECD296059E000FFBEA8 /* NSHapticFeedbackPerformerMock.swift */; };
@@ -773,6 +774,7 @@
773774
30E6D0002A6E505200A58B20 /* NavigatorSidebarViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigatorSidebarViewModel.swift; sourceTree = "<group>"; };
774775
3E0196722A3921AC002648D8 /* codeedit_shell_integration_rc.zsh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = codeedit_shell_integration_rc.zsh; sourceTree = "<group>"; };
775776
3E0196792A392B45002648D8 /* codeedit_shell_integration.bash */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = codeedit_shell_integration.bash; sourceTree = "<group>"; };
777+
4A6F0DB42CBA462B00499627 /* ProjectNavigatorMenuActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectNavigatorMenuActions.swift; sourceTree = "<group>"; };
776778
4E7F066529602E7B00BB3C12 /* CodeEditSplitViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodeEditSplitViewController.swift; sourceTree = "<group>"; };
777779
4EE96ECA2960565E00FFBEA8 /* DocumentsUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentsUnitTests.swift; sourceTree = "<group>"; };
778780
4EE96ECD296059E000FFBEA8 /* NSHapticFeedbackPerformerMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSHapticFeedbackPerformerMock.swift; sourceTree = "<group>"; };
@@ -1461,6 +1463,7 @@
14611463
6CC17B502C43311900834E2C /* ProjectNavigatorViewController+NSOutlineViewDataSource.swift */,
14621464
285FEC6F27FE4B9800E57D53 /* ProjectNavigatorTableViewCell.swift */,
14631465
285FEC7127FE4EEF00E57D53 /* ProjectNavigatorMenu.swift */,
1466+
4A6F0DB42CBA462B00499627 /* ProjectNavigatorMenuActions.swift */,
14641467
D7DC4B75298FFBE900D6C83D /* ProjectNavigatorViewController+OutlineTableViewCellDelegate.swift */,
14651468
EC0870F62A455F6400EB8692 /* ProjectNavigatorViewController+NSMenuDelegate.swift */,
14661469
);
@@ -4286,6 +4289,7 @@
42864289
587B9DA629300ABD00AC7927 /* ToolbarBranchPicker.swift in Sources */,
42874290
6C6BD6F629CD145F00235D17 /* ExtensionInfo.swift in Sources */,
42884291
04BA7C202AE2D92B00584E1C /* GitClient+Status.swift in Sources */,
4292+
4A6F0DB52CBA462B00499627 /* ProjectNavigatorMenuActions.swift in Sources */,
42894293
58F2EB05292FB2B0004A9BDE /* Settings.swift in Sources */,
42904294
6CBD1BC62978DE53006639D5 /* Font+Caption3.swift in Sources */,
42914295
30E6D0012A6E505200A58B20 /* NavigatorSidebarViewModel.swift in Sources */,

CodeEdit/Features/CEWorkspace/Models/CEWorkspaceFileManager+FileManagement.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,30 @@ extension CEWorkspaceFileManager {
141141
}
142142
}
143143

144+
/// This function deletes multiple files or folders from the current project by erasing immediately.
145+
/// - Parameters:
146+
/// - files: The files to delete
147+
/// - confirmDelete: True to present an alert to confirm the delete.
148+
public func batchDelete(files: Set<CEWorkspaceFile>, confirmDelete: Bool = true) {
149+
let deleteConfirmation = NSAlert()
150+
deleteConfirmation.messageText = "Are you sure you want to delete the \(files.count) selected items?"
151+
// swiftlint:disable:next line_length
152+
deleteConfirmation.informativeText = "\(files.count) items will be deleted immediately. You cannot undo this action."
153+
deleteConfirmation.alertStyle = .critical
154+
deleteConfirmation.addButton(withTitle: "Delete")
155+
deleteConfirmation.buttons.last?.hasDestructiveAction = true
156+
deleteConfirmation.addButton(withTitle: "Cancel")
157+
if !confirmDelete || deleteConfirmation.runModal() == .alertFirstButtonReturn {
158+
for file in files where fileManager.fileExists(atPath: file.url.path) {
159+
do {
160+
try fileManager.removeItem(at: file.url)
161+
} catch {
162+
print(error.localizedDescription)
163+
}
164+
}
165+
}
166+
}
167+
144168
/// This function duplicates the item or folder
145169
/// - Parameter file: The file to duplicate
146170
/// - Authors: Mattijs Eikelenboom, KaiTheRedNinja. *Moved from 7c27b1e*

CodeEdit/Features/NavigatorArea/ProjectNavigator/OutlineView/ProjectNavigatorMenu.swift

Lines changed: 30 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@ final class ProjectNavigatorMenu: NSMenu {
1717
/// The workspace, for opening the item
1818
var workspace: WorkspaceDocument?
1919

20-
var outlineView: NSOutlineView
20+
/// The `ProjectNavigatorViewController` is being called from.
21+
/// By sending it, we can access it's variables and functions.
22+
var sender: ProjectNavigatorViewController
2123

22-
init(sender: NSOutlineView) {
23-
outlineView = sender
24+
init(_ sender: ProjectNavigatorViewController) {
25+
self.sender = sender
2426
super.init(title: "Options")
2527
}
2628

@@ -42,9 +44,9 @@ final class ProjectNavigatorMenu: NSMenu {
4244
return mItem
4345
}
4446

45-
/// Setup the menu and disables certain items when `isFile` is false
46-
/// - Parameter isFile: A flag indicating that the item is a file instead of a directory
47-
private func setupMenu() {
47+
/// Configures the menu based on the current selection in the outline view.
48+
/// - Menu items get added depending on the amount of selected items.
49+
private func setupMenu() { // swiftlint:disable:this function_body_length
4850
guard let item else { return }
4951
let showInFinder = menuItem("Show in Finder", action: #selector(showInFinder))
5052

@@ -92,19 +94,30 @@ final class ProjectNavigatorMenu: NSMenu {
9294
showFileInspector,
9395
NSMenuItem.separator(),
9496
newFile,
95-
newFolder,
96-
NSMenuItem.separator(),
97-
rename,
98-
trash,
99-
delete,
100-
duplicate,
101-
NSMenuItem.separator(),
102-
sortByName,
103-
sortByType,
104-
NSMenuItem.separator(),
105-
sourceControl,
97+
newFolder
10698
]
10799

100+
if canCreateFolderFromSelection() {
101+
items.append(menuItem("New Folder from Selection", action: #selector(newFolderFromSelection)))
102+
}
103+
items.append(NSMenuItem.separator())
104+
if selectedItems().count == 1 {
105+
items.append(rename)
106+
}
107+
108+
items.append(
109+
contentsOf: [
110+
trash,
111+
delete,
112+
duplicate,
113+
NSMenuItem.separator(),
114+
sortByName,
115+
sortByType,
116+
NSMenuItem.separator(),
117+
sourceControl,
118+
]
119+
)
120+
108121
setSubmenu(openAsMenu(item: item), for: openAs)
109122
setSubmenu(sourceControlMenu(item: item), for: sourceControl)
110123
}
@@ -183,87 +196,6 @@ final class ProjectNavigatorMenu: NSMenu {
183196
removeAllItems()
184197
setupMenu()
185198
}
186-
187-
/// Action that opens **Finder** at the items location.
188-
@objc
189-
private func showInFinder() {
190-
item?.showInFinder()
191-
}
192-
193-
/// Action that opens the item, identical to clicking it.
194-
@objc
195-
private func openInTab() {
196-
if let item {
197-
workspace?.editorManager?.openTab(item: item)
198-
}
199-
}
200-
201-
/// Action that opens in an external editor
202-
@objc
203-
private func openWithExternalEditor() {
204-
item?.openWithExternalEditor()
205-
}
206-
207-
// TODO: allow custom file names
208-
/// Action that creates a new untitled file
209-
@objc
210-
private func newFile() {
211-
guard let item else { return }
212-
do {
213-
try workspace?.workspaceFileManager?.addFile(fileName: "untitled", toFile: item)
214-
} catch {
215-
let alert = NSAlert(error: error)
216-
alert.addButton(withTitle: "Dismiss")
217-
alert.runModal()
218-
}
219-
outlineView.expandItem(item.isFolder ? item : item.parent)
220-
}
221-
222-
// TODO: allow custom folder names
223-
/// Action that creates a new untitled folder
224-
@objc
225-
private func newFolder() {
226-
guard let item else { return }
227-
workspace?.workspaceFileManager?.addFolder(folderName: "untitled", toFile: item)
228-
outlineView.expandItem(item)
229-
outlineView.expandItem(item.isFolder ? item : item.parent)
230-
}
231-
232-
/// Opens the rename file dialogue on the cell this was presented from.
233-
@objc
234-
private func renameFile() {
235-
let row = outlineView.row(forItem: item)
236-
guard row > 0,
237-
let cell = outlineView.view(
238-
atColumn: 0,
239-
row: row,
240-
makeIfNecessary: false
241-
) as? ProjectNavigatorTableViewCell else {
242-
return
243-
}
244-
outlineView.window?.makeFirstResponder(cell.textField)
245-
}
246-
247-
/// Action that moves the item to trash.
248-
@objc
249-
private func trash() {
250-
guard let item else { return }
251-
workspace?.workspaceFileManager?.trash(file: item)
252-
}
253-
254-
/// Action that deletes the item immediately.
255-
@objc
256-
private func delete() {
257-
guard let item else { return }
258-
workspace?.workspaceFileManager?.delete(file: item)
259-
}
260-
261-
/// Action that duplicates the item
262-
@objc
263-
private func duplicate() {
264-
guard let item else { return }
265-
workspace?.workspaceFileManager?.duplicate(file: item)
266-
}
267199
}
268200

269201
extension NSMenuItem {

0 commit comments

Comments
 (0)