diff --git a/CodeEdit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/CodeEdit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 465ebbcf1..94ead82e8 100644 --- a/CodeEdit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/CodeEdit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -222,8 +222,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/lukepistrol/SwiftLintPlugin", "state" : { - "revision" : "3825ebf8d55bb877c91bc897e8e3d0c001f16fba", - "version" : "0.58.2" + "revision" : "3780efccceaa87f17ec39638a9d263d0e742b71c", + "version" : "0.59.1" } }, { diff --git a/CodeEdit/Features/CEWorkspace/Models/CEWorkspaceFileManager+FileManagement.swift b/CodeEdit/Features/CEWorkspace/Models/CEWorkspaceFileManager+FileManagement.swift index 96115088f..a6101799f 100644 --- a/CodeEdit/Features/CEWorkspace/Models/CEWorkspaceFileManager+FileManagement.swift +++ b/CodeEdit/Features/CEWorkspace/Models/CEWorkspaceFileManager+FileManagement.swift @@ -62,7 +62,8 @@ extension CEWorkspaceFileManager { func addFile( fileName: String, toFile file: CEWorkspaceFile, - useExtension: String? = nil + useExtension: String? = nil, + contents: Data? = nil ) throws -> CEWorkspaceFile { // check the folder for other files, and see what the most common file extension is do { @@ -95,7 +96,7 @@ extension CEWorkspaceFileManager { // Create the file guard fileManager.createFile( atPath: fileUrl.path, - contents: nil, + contents: contents, attributes: [FileAttributeKey.creationDate: Date()] ) else { throw CocoaError.error(.fileWriteUnknown, url: fileUrl) diff --git a/CodeEdit/Features/NavigatorArea/ProjectNavigator/OutlineView/ProjectNavigatorMenu.swift b/CodeEdit/Features/NavigatorArea/ProjectNavigator/OutlineView/ProjectNavigatorMenu.swift index 553e61406..8fd37b7db 100644 --- a/CodeEdit/Features/NavigatorArea/ProjectNavigator/OutlineView/ProjectNavigatorMenu.swift +++ b/CodeEdit/Features/NavigatorArea/ProjectNavigator/OutlineView/ProjectNavigatorMenu.swift @@ -61,6 +61,12 @@ final class ProjectNavigatorMenu: NSMenu { let showFileInspector = menuItem("Show File Inspector", action: nil) let newFile = menuItem("New File...", action: #selector(newFile)) + let newFileFromClipboard = menuItem( + "New File from Clipboard", + action: #selector(newFileFromClipboard), + key: "v" + ) + newFileFromClipboard.keyEquivalentModifierMask = [.command] let newFolder = menuItem("New Folder", action: #selector(newFolder)) let rename = menuItem("Rename", action: #selector(renameFile)) @@ -100,6 +106,7 @@ final class ProjectNavigatorMenu: NSMenu { showFileInspector, NSMenuItem.separator(), newFile, + newFileFromClipboard, newFolder ] diff --git a/CodeEdit/Features/NavigatorArea/ProjectNavigator/OutlineView/ProjectNavigatorMenuActions.swift b/CodeEdit/Features/NavigatorArea/ProjectNavigator/OutlineView/ProjectNavigatorMenuActions.swift index e80890aa7..1aa65af92 100644 --- a/CodeEdit/Features/NavigatorArea/ProjectNavigator/OutlineView/ProjectNavigatorMenuActions.swift +++ b/CodeEdit/Features/NavigatorArea/ProjectNavigator/OutlineView/ProjectNavigatorMenuActions.swift @@ -98,6 +98,47 @@ extension ProjectNavigatorMenu { } } + /// Opens the rename file dialogue on the cell this was presented from. + @objc + func renameFile() { + guard let newFile = workspace?.listenerModel.highlightedFileItem else { return } + let row = sender.outlineView.row(forItem: newFile) + guard row > 0, + let cell = sender.outlineView.view( + atColumn: 0, + row: row, + makeIfNecessary: false + ) as? ProjectNavigatorTableViewCell else { + return + } + sender.outlineView.window?.makeFirstResponder(cell.textField) + } + + // TODO: Automatically identified the file type + /// Action that creates a new file with clipboard content + @objc + func newFileFromClipboard() { + guard let item else { return } + do { + let clipBoardContent = NSPasteboard.general.string(forType: .string)?.data(using: .utf8) + if let clipBoardContent, !clipBoardContent.isEmpty, let newFile = try workspace? + .workspaceFileManager? + .addFile( + fileName: "untitled", + toFile: item, + contents: clipBoardContent + ) { + workspace?.listenerModel.highlightedFileItem = newFile + workspace?.editorManager?.openTab(item: newFile) + renameFile() + } + } catch { + let alert = NSAlert(error: error) + alert.addButton(withTitle: "Dismiss") + alert.runModal() + } + } + // TODO: allow custom folder names /// Action that creates a new untitled folder @objc @@ -143,21 +184,6 @@ extension ProjectNavigatorMenu { reloadData() } - /// Opens the rename file dialogue on the cell this was presented from. - @objc - func renameFile() { - let row = sender.outlineView.row(forItem: item) - guard row > 0, - let cell = sender.outlineView.view( - atColumn: 0, - row: row, - makeIfNecessary: false - ) as? ProjectNavigatorTableViewCell else { - return - } - sender.outlineView.window?.makeFirstResponder(cell.textField) - } - /// Action that moves the item to trash. @objc func trash() { diff --git a/CodeEdit/Features/NavigatorArea/ProjectNavigator/OutlineView/ProjectNavigatorNSOutlineView.swift b/CodeEdit/Features/NavigatorArea/ProjectNavigator/OutlineView/ProjectNavigatorNSOutlineView.swift new file mode 100644 index 000000000..58e77ebfc --- /dev/null +++ b/CodeEdit/Features/NavigatorArea/ProjectNavigator/OutlineView/ProjectNavigatorNSOutlineView.swift @@ -0,0 +1,37 @@ +// +// ProjectNavigatorNSOutlineView.swift +// CodeEdit +// +// Created by Khan Winter on 6/10/25. +// + +import AppKit + +final class ProjectNavigatorNSOutlineView: NSOutlineView, NSMenuItemValidation { + override func performKeyEquivalent(with event: NSEvent) -> Bool { + guard event.window === window && window?.firstResponder === self else { + return super.performKeyEquivalent(with: event) + } + + if event.charactersIgnoringModifiers == "v" + && event.modifierFlags.intersection(.deviceIndependentFlagsMask) == .command { + guard let menu = menu as? ProjectNavigatorMenu else { + return super.performKeyEquivalent(with: event) + } + menu.delegate?.menuNeedsUpdate?(menu) + for fileItem in selectedRowIndexes.compactMap({ item(atRow: $0) as? CEWorkspaceFile }) { + menu.item = fileItem + menu.newFileFromClipboard() + } + return true + } + return super.performKeyEquivalent(with: event) + } + + func validateMenuItem(_ menuItem: NSMenuItem) -> Bool { + if menuItem.action == #selector(ProjectNavigatorMenu.newFileFromClipboard) { + return !selectedRowIndexes.isEmpty + } + return false + } +} diff --git a/CodeEdit/Features/NavigatorArea/ProjectNavigator/OutlineView/ProjectNavigatorViewController+NSMenuDelegate.swift b/CodeEdit/Features/NavigatorArea/ProjectNavigator/OutlineView/ProjectNavigatorViewController+NSMenuDelegate.swift index faa70275c..0b080127b 100644 --- a/CodeEdit/Features/NavigatorArea/ProjectNavigator/OutlineView/ProjectNavigatorViewController+NSMenuDelegate.swift +++ b/CodeEdit/Features/NavigatorArea/ProjectNavigator/OutlineView/ProjectNavigatorViewController+NSMenuDelegate.swift @@ -18,12 +18,12 @@ extension ProjectNavigatorViewController: NSMenuDelegate { let row = outlineView.clickedRow guard let menu = menu as? ProjectNavigatorMenu else { return } + menu.workspace = workspace if row == -1 { menu.item = nil } else { if let item = outlineView.item(atRow: row) as? CEWorkspaceFile { menu.item = item - menu.workspace = workspace } else { menu.item = nil } diff --git a/CodeEdit/Features/NavigatorArea/ProjectNavigator/OutlineView/ProjectNavigatorViewController.swift b/CodeEdit/Features/NavigatorArea/ProjectNavigator/OutlineView/ProjectNavigatorViewController.swift index ea9e2ee25..c71d8d2fa 100644 --- a/CodeEdit/Features/NavigatorArea/ProjectNavigator/OutlineView/ProjectNavigatorViewController.swift +++ b/CodeEdit/Features/NavigatorArea/ProjectNavigator/OutlineView/ProjectNavigatorViewController.swift @@ -74,7 +74,7 @@ final class ProjectNavigatorViewController: NSViewController { self.scrollView.hasVerticalScroller = true self.view = scrollView - self.outlineView = NSOutlineView() + self.outlineView = ProjectNavigatorNSOutlineView() self.outlineView.dataSource = self self.outlineView.delegate = self self.outlineView.autosaveExpandedItems = true