Skip to content

Commit

Permalink
Select multiple files in the project navigator (#1906)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>

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

Co-authored-by: Austin Condiff <[email protected]>

* Update CodeEdit/Features/NavigatorArea/ProjectNavigator/OutlineView/ProjectNavigatorMenuActions.swift

Co-authored-by: Austin Condiff <[email protected]>

* Fixed SwiftLint errors

* Update CEWorkspaceFileManager+FileManagement.swift

---------

Co-authored-by: Leonardo Larrañaga <[email protected]>
Co-authored-by: Austin Condiff <[email protected]>
  • Loading branch information
3 people authored Oct 23, 2024
1 parent 51669a3 commit 9d743ec
Show file tree
Hide file tree
Showing 6 changed files with 251 additions and 99 deletions.
4 changes: 4 additions & 0 deletions CodeEdit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
30E6D0012A6E505200A58B20 /* NavigatorSidebarViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30E6D0002A6E505200A58B20 /* NavigatorSidebarViewModel.swift */; };
3E0196732A3921AC002648D8 /* codeedit_shell_integration_rc.zsh in Resources */ = {isa = PBXBuildFile; fileRef = 3E0196722A3921AC002648D8 /* codeedit_shell_integration_rc.zsh */; };
3E01967A2A392B45002648D8 /* codeedit_shell_integration.bash in Resources */ = {isa = PBXBuildFile; fileRef = 3E0196792A392B45002648D8 /* codeedit_shell_integration.bash */; };
4A6F0DB52CBA462B00499627 /* ProjectNavigatorMenuActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A6F0DB42CBA462B00499627 /* ProjectNavigatorMenuActions.swift */; };
4E7F066629602E7B00BB3C12 /* CodeEditSplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E7F066529602E7B00BB3C12 /* CodeEditSplitViewController.swift */; };
4EE96ECB2960565E00FFBEA8 /* DocumentsUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE96ECA2960565E00FFBEA8 /* DocumentsUnitTests.swift */; };
4EE96ECE296059E000FFBEA8 /* NSHapticFeedbackPerformerMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE96ECD296059E000FFBEA8 /* NSHapticFeedbackPerformerMock.swift */; };
Expand Down Expand Up @@ -773,6 +774,7 @@
30E6D0002A6E505200A58B20 /* NavigatorSidebarViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigatorSidebarViewModel.swift; sourceTree = "<group>"; };
3E0196722A3921AC002648D8 /* codeedit_shell_integration_rc.zsh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = codeedit_shell_integration_rc.zsh; sourceTree = "<group>"; };
3E0196792A392B45002648D8 /* codeedit_shell_integration.bash */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = codeedit_shell_integration.bash; sourceTree = "<group>"; };
4A6F0DB42CBA462B00499627 /* ProjectNavigatorMenuActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectNavigatorMenuActions.swift; sourceTree = "<group>"; };
4E7F066529602E7B00BB3C12 /* CodeEditSplitViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodeEditSplitViewController.swift; sourceTree = "<group>"; };
4EE96ECA2960565E00FFBEA8 /* DocumentsUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentsUnitTests.swift; sourceTree = "<group>"; };
4EE96ECD296059E000FFBEA8 /* NSHapticFeedbackPerformerMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSHapticFeedbackPerformerMock.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1461,6 +1463,7 @@
6CC17B502C43311900834E2C /* ProjectNavigatorViewController+NSOutlineViewDataSource.swift */,
285FEC6F27FE4B9800E57D53 /* ProjectNavigatorTableViewCell.swift */,
285FEC7127FE4EEF00E57D53 /* ProjectNavigatorMenu.swift */,
4A6F0DB42CBA462B00499627 /* ProjectNavigatorMenuActions.swift */,
D7DC4B75298FFBE900D6C83D /* ProjectNavigatorViewController+OutlineTableViewCellDelegate.swift */,
EC0870F62A455F6400EB8692 /* ProjectNavigatorViewController+NSMenuDelegate.swift */,
);
Expand Down Expand Up @@ -4286,6 +4289,7 @@
587B9DA629300ABD00AC7927 /* ToolbarBranchPicker.swift in Sources */,
6C6BD6F629CD145F00235D17 /* ExtensionInfo.swift in Sources */,
04BA7C202AE2D92B00584E1C /* GitClient+Status.swift in Sources */,
4A6F0DB52CBA462B00499627 /* ProjectNavigatorMenuActions.swift in Sources */,
58F2EB05292FB2B0004A9BDE /* Settings.swift in Sources */,
6CBD1BC62978DE53006639D5 /* Font+Caption3.swift in Sources */,
30E6D0012A6E505200A58B20 /* NavigatorSidebarViewModel.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,30 @@ extension CEWorkspaceFileManager {
}
}

/// This function deletes multiple files or folders from the current project by erasing immediately.
/// - Parameters:
/// - files: The files to delete
/// - confirmDelete: True to present an alert to confirm the delete.
public func batchDelete(files: Set<CEWorkspaceFile>, confirmDelete: Bool = true) {
let deleteConfirmation = NSAlert()
deleteConfirmation.messageText = "Are you sure you want to delete the \(files.count) selected items?"
// swiftlint:disable:next line_length
deleteConfirmation.informativeText = "\(files.count) items will be deleted immediately. You cannot undo this action."
deleteConfirmation.alertStyle = .critical
deleteConfirmation.addButton(withTitle: "Delete")
deleteConfirmation.buttons.last?.hasDestructiveAction = true
deleteConfirmation.addButton(withTitle: "Cancel")
if !confirmDelete || deleteConfirmation.runModal() == .alertFirstButtonReturn {
for file in files where fileManager.fileExists(atPath: file.url.path) {
do {
try fileManager.removeItem(at: file.url)
} catch {
print(error.localizedDescription)
}
}
}
}

/// This function duplicates the item or folder
/// - Parameter file: The file to duplicate
/// - Authors: Mattijs Eikelenboom, KaiTheRedNinja. *Moved from 7c27b1e*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ final class ProjectNavigatorMenu: NSMenu {
/// The workspace, for opening the item
var workspace: WorkspaceDocument?

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

init(sender: NSOutlineView) {
outlineView = sender
init(_ sender: ProjectNavigatorViewController) {
self.sender = sender
super.init(title: "Options")
}

Expand All @@ -42,9 +44,9 @@ final class ProjectNavigatorMenu: NSMenu {
return mItem
}

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

Expand Down Expand Up @@ -92,19 +94,30 @@ final class ProjectNavigatorMenu: NSMenu {
showFileInspector,
NSMenuItem.separator(),
newFile,
newFolder,
NSMenuItem.separator(),
rename,
trash,
delete,
duplicate,
NSMenuItem.separator(),
sortByName,
sortByType,
NSMenuItem.separator(),
sourceControl,
newFolder
]

if canCreateFolderFromSelection() {
items.append(menuItem("New Folder from Selection", action: #selector(newFolderFromSelection)))
}
items.append(NSMenuItem.separator())
if selectedItems().count == 1 {
items.append(rename)
}

items.append(
contentsOf: [
trash,
delete,
duplicate,
NSMenuItem.separator(),
sortByName,
sortByType,
NSMenuItem.separator(),
sourceControl,
]
)

setSubmenu(openAsMenu(item: item), for: openAs)
setSubmenu(sourceControlMenu(item: item), for: sourceControl)
}
Expand Down Expand Up @@ -183,87 +196,6 @@ final class ProjectNavigatorMenu: NSMenu {
removeAllItems()
setupMenu()
}

/// Action that opens **Finder** at the items location.
@objc
private func showInFinder() {
item?.showInFinder()
}

/// Action that opens the item, identical to clicking it.
@objc
private func openInTab() {
if let item {
workspace?.editorManager?.openTab(item: item)
}
}

/// Action that opens in an external editor
@objc
private func openWithExternalEditor() {
item?.openWithExternalEditor()
}

// TODO: allow custom file names
/// Action that creates a new untitled file
@objc
private func newFile() {
guard let item else { return }
do {
try workspace?.workspaceFileManager?.addFile(fileName: "untitled", toFile: item)
} catch {
let alert = NSAlert(error: error)
alert.addButton(withTitle: "Dismiss")
alert.runModal()
}
outlineView.expandItem(item.isFolder ? item : item.parent)
}

// TODO: allow custom folder names
/// Action that creates a new untitled folder
@objc
private func newFolder() {
guard let item else { return }
workspace?.workspaceFileManager?.addFolder(folderName: "untitled", toFile: item)
outlineView.expandItem(item)
outlineView.expandItem(item.isFolder ? item : item.parent)
}

/// Opens the rename file dialogue on the cell this was presented from.
@objc
private func renameFile() {
let row = outlineView.row(forItem: item)
guard row > 0,
let cell = outlineView.view(
atColumn: 0,
row: row,
makeIfNecessary: false
) as? ProjectNavigatorTableViewCell else {
return
}
outlineView.window?.makeFirstResponder(cell.textField)
}

/// Action that moves the item to trash.
@objc
private func trash() {
guard let item else { return }
workspace?.workspaceFileManager?.trash(file: item)
}

/// Action that deletes the item immediately.
@objc
private func delete() {
guard let item else { return }
workspace?.workspaceFileManager?.delete(file: item)
}

/// Action that duplicates the item
@objc
private func duplicate() {
guard let item else { return }
workspace?.workspaceFileManager?.duplicate(file: item)
}
}

extension NSMenuItem {
Expand Down
Loading

0 comments on commit 9d743ec

Please sign in to comment.