Skip to content

Open Quickly #119

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
Mar 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions CodeEdit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
04660F6427E3ACAF00477777 /* Appearances.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04660F6327E3ACAF00477777 /* Appearances.swift */; };
04660F6627E3ACEF00477777 /* ReopenBehavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04660F6527E3ACEF00477777 /* ReopenBehavior.swift */; };
04660F6A27E51E5C00477777 /* CodeEditWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04660F6927E51E5C00477777 /* CodeEditWindowController.swift */; };
0485EB1927E70F4900138301 /* QuickOpenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0485EB1827E70F4900138301 /* QuickOpenView.swift */; };
0485EB1D27E7338100138301 /* QuickOpenItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0485EB1C27E7338100138301 /* QuickOpenItem.swift */; };
0485EB1F27E7458B00138301 /* WorkspaceCodeFileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0485EB1E27E7458B00138301 /* WorkspaceCodeFileView.swift */; };
0485EB2327E7791400138301 /* QuickOpenPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0485EB2227E7791400138301 /* QuickOpenPreviewView.swift */; };
0485EB2527E7B9C800138301 /* Overlays in Frameworks */ = {isa = PBXBuildFile; productRef = 0485EB2427E7B9C800138301 /* Overlays */; };
286620A527E4AB6900E18C2B /* BreadcrumbsComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 286620A427E4AB6900E18C2B /* BreadcrumbsComponent.swift */; };
2875A46D27E3BE5B007805F8 /* BreadcrumbsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2875A46C27E3BE5B007805F8 /* BreadcrumbsView.swift */; };
287776E727E3413200D46668 /* SideBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 287776E627E3413200D46668 /* SideBar.swift */; };
Expand Down Expand Up @@ -68,6 +73,10 @@
04660F6527E3ACEF00477777 /* ReopenBehavior.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReopenBehavior.swift; sourceTree = "<group>"; };
04660F6927E51E5C00477777 /* CodeEditWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodeEditWindowController.swift; sourceTree = "<group>"; };
0468438427DC76E200F8E88E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
0485EB1827E70F4900138301 /* QuickOpenView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickOpenView.swift; sourceTree = "<group>"; };
0485EB1C27E7338100138301 /* QuickOpenItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickOpenItem.swift; sourceTree = "<group>"; };
0485EB1E27E7458B00138301 /* WorkspaceCodeFileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkspaceCodeFileView.swift; sourceTree = "<group>"; };
0485EB2227E7791400138301 /* QuickOpenPreviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickOpenPreviewView.swift; sourceTree = "<group>"; };
04ADA0C827E5D29000BF00B2 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
04ADA0CA27E5D41F00BF00B2 /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/Localizable.strings; sourceTree = "<group>"; };
04F2BF0E27DBB28E0024EAB1 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -106,6 +115,7 @@
buildActionMask = 2147483647;
files = (
5C403B8F27E20F8000788241 /* WorkspaceClient in Frameworks */,
0485EB2527E7B9C800138301 /* Overlays in Frameworks */,
B65E614627E6765D00255275 /* Introspect in Frameworks */,
D70F5E2C27E4E8CF004EE4B9 /* WelcomeModule in Frameworks */,
28CE5EA027E6493D0065D29C /* StatusBar in Frameworks */,
Expand Down Expand Up @@ -136,6 +146,7 @@
043C321327E31FF6006AE443 /* CodeEditDocumentController.swift */,
043C321527E3201F006AE443 /* WorkspaceDocument.swift */,
04660F6927E51E5C00477777 /* CodeEditWindowController.swift */,
0485EB1E27E7458B00138301 /* WorkspaceCodeFileView.swift */,
);
path = Documents;
sourceTree = "<group>";
Expand All @@ -150,6 +161,16 @@
path = Models;
sourceTree = "<group>";
};
0485EB1727E7016400138301 /* Quick Open */ = {
isa = PBXGroup;
children = (
0485EB1827E70F4900138301 /* QuickOpenView.swift */,
0485EB1C27E7338100138301 /* QuickOpenItem.swift */,
0485EB2227E7791400138301 /* QuickOpenPreviewView.swift */,
);
path = "Quick Open";
sourceTree = "<group>";
};
04F2BF1027DBB3AF0024EAB1 /* Settings */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -234,6 +255,7 @@
B658FB2E27DA9E0F00EA4DBD /* CodeEdit */ = {
isa = PBXGroup;
children = (
0485EB1727E7016400138301 /* Quick Open */,
04660F6027E3A68A00477777 /* Info.plist */,
D72E1A8127E3B0A300EB11B9 /* Welcome */,
043C321227E31FE8006AE443 /* Documents */,
Expand Down Expand Up @@ -305,6 +327,7 @@
D70F5E2B27E4E8CF004EE4B9 /* WelcomeModule */,
B65E614527E6765D00255275 /* Introspect */,
28CE5E9F27E6493D0065D29C /* StatusBar */,
0485EB2427E7B9C800138301 /* Overlays */,
);
productName = CodeEdit;
productReference = B658FB2C27DA9E0F00EA4DBD /* CodeEdit.app */;
Expand Down Expand Up @@ -471,6 +494,7 @@
files = (
2B7A583527E4BA0100D25D4E /* AppDelegate.swift in Sources */,
2875A46D27E3BE5B007805F8 /* BreadcrumbsView.swift in Sources */,
0485EB1D27E7338100138301 /* QuickOpenItem.swift in Sources */,
04540D5B27DD08C300E91B77 /* SettingsView.swift in Sources */,
D72E1A8927E44D7C00EB11B9 /* WelcomeWindowView.swift in Sources */,
04540D5C27DD08C300E91B77 /* GeneralSettingsView.swift in Sources */,
Expand All @@ -480,6 +504,8 @@
34EE19BE27E0469C00F152CE /* BlurView.swift in Sources */,
D7211D4327E066CE008F2ED7 /* Localized+Ex.swift in Sources */,
287776E927E34BC700D46668 /* TabBar.swift in Sources */,
0485EB1F27E7458B00138301 /* WorkspaceCodeFileView.swift in Sources */,
0485EB1927E70F4900138301 /* QuickOpenView.swift in Sources */,
286620A527E4AB6900E18C2B /* BreadcrumbsComponent.swift in Sources */,
287776EF27E3515300D46668 /* TabBarItem.swift in Sources */,
D72E1A8727E4242900EB11B9 /* RecentProjectsView.swift in Sources */,
Expand All @@ -493,6 +519,7 @@
28B0A19827E385C300B73177 /* SideBarToolbarTop.swift in Sources */,
345F667527DF6C180069BD69 /* FileTabRow.swift in Sources */,
28FFE1BF27E3A441001939DB /* SideBarToolbarBottom.swift in Sources */,
0485EB2327E7791400138301 /* QuickOpenPreviewView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -870,6 +897,10 @@
/* End XCRemoteSwiftPackageReference section */

/* Begin XCSwiftPackageProductDependency section */
0485EB2427E7B9C800138301 /* Overlays */ = {
isa = XCSwiftPackageProductDependency;
productName = Overlays;
};
28CE5E9F27E6493D0065D29C /* StatusBar */ = {
isa = XCSwiftPackageProductDependency;
productName = StatusBar;
Expand Down
80 changes: 41 additions & 39 deletions CodeEdit/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,47 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
return true
}

func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
if flag {
return false
}

handleOpen()

return false
}

func applicationShouldOpenUntitledFile(_ sender: NSApplication) -> Bool {
return false
}

func handleOpen() {
let behavior = ReopenBehavior(rawValue: UserDefaults.standard.string(forKey: ReopenBehavior.storageKey)
?? ReopenBehavior.default.rawValue) ?? ReopenBehavior.default

switch behavior {
case .welcome:
openWelcome(self)
case .openPanel:
CodeEditDocumentController.shared.openDocument(self)
case .newDocument:
CodeEditDocumentController.shared.newDocument(self)
}
}

func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
CodeEditDocumentController.shared.documents.flatMap { doc in
return doc.windowControllers
}.forEach { windowContoller in
if let windowContoller = windowContoller as? CodeEditWindowController {
windowContoller.workspace?.close()
}
}
return .terminateNow
}

// MARK: - Open windows

@IBAction func openPreferences(_ sender: Any) {
if let window = NSApp.windows.filter({ window in
return (window.contentView as? NSHostingView<SettingsView>) != nil
Expand Down Expand Up @@ -86,43 +127,4 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
window.contentView = NSHostingView(rootView: contentView)
window.makeKeyAndOrderFront(self)
}

func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
if flag {
return false
}

handleOpen()

return false
}

func applicationShouldOpenUntitledFile(_ sender: NSApplication) -> Bool {
return false
}

func handleOpen() {
let behavior = ReopenBehavior(rawValue: UserDefaults.standard.string(forKey: ReopenBehavior.storageKey)
?? ReopenBehavior.default.rawValue) ?? ReopenBehavior.default

switch behavior {
case .welcome:
openWelcome(self)
case .openPanel:
CodeEditDocumentController.shared.openDocument(self)
case .newDocument:
CodeEditDocumentController.shared.newDocument(self)
}
}

func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
CodeEditDocumentController.shared.documents.flatMap { doc in
return doc.windowControllers
}.forEach { windowContoller in
if let windowContoller = windowContoller as? CodeEditWindowController {
windowContoller.workspace?.close()
}
}
return .terminateNow
}
}
2 changes: 1 addition & 1 deletion CodeEdit/Breadcrumbs/BreadcrumbsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ struct BreadcrumbsView: View {
}

private func fileInfo() {
guard let projName = workspace.folderURL?.lastPathComponent,
guard let projName = workspace.fileURL?.lastPathComponent,
var components = file.url.pathComponents.split(separator: projName).last else { return }
components.removeLast()

Expand Down
21 changes: 21 additions & 0 deletions CodeEdit/Documents/CodeEditWindowController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
//

import Cocoa
import SwiftUI
import CodeFile
import Overlays

class CodeEditWindowController: NSWindowController {

Expand All @@ -31,4 +33,23 @@ class CodeEditWindowController: NSWindowController {
@IBAction func saveDocument(_ sender: Any) {
getSelectedCodeFile()?.save(sender)
}

@IBAction func openQuickly(_ sender: Any) {
if let workspace = workspace, let state = workspace.quickOpenState {
if let window = window?.childWindows?.filter({ window in
return (window.contentView as? NSHostingView<QuickOpenView>) != nil
}).first {
window.close()
return
}

let panel = OverlayPanel()
let contentView = QuickOpenView(state: state) {
panel.close()
}
panel.contentView = NSHostingView(rootView: contentView)
window?.addChildWindow(panel, ordered: .above)
panel.makeKeyAndOrderFront(self)
}
}
}
40 changes: 40 additions & 0 deletions CodeEdit/Documents/WorkspaceCodeFileView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//
// WorkspaceCodeFileEditor.swift
// CodeEdit
//
// Created by Pavel Kasila on 20.03.22.
//

import SwiftUI
import CodeFile
import WorkspaceClient
import StatusBar

struct WorkspaceCodeFileView: View {
var windowController: NSWindowController
@ObservedObject var workspace: WorkspaceDocument

@ViewBuilder var body: some View {
if let item = workspace.openFileItems.first(where: { file in
return file.id == workspace.selectedId
}) {
if let codeFile = workspace.openedCodeFiles[item] {
CodeFileView(codeFile: codeFile)
.safeAreaInset(edge: .top, spacing: 0) {
VStack(spacing: 0) {
TabBar(windowController: windowController, workspace: workspace)
CustomDivider()
BreadcrumbsView(item, workspace: workspace)
}
}
.safeAreaInset(edge: .bottom) {
StatusBarView()
}
} else {
Text("CodeEdit cannot open this file because its file type is not supported.")
}
} else {
Text("Open file from sidebar")
}
}
}
66 changes: 61 additions & 5 deletions CodeEdit/Documents/WorkspaceDocument.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,16 @@ import CodeFile

@objc(WorkspaceDocument)
class WorkspaceDocument: NSDocument, ObservableObject, NSToolbarDelegate {
@Published var workspaceClient: WorkspaceClient?
var workspaceClient: WorkspaceClient?

@Published var selectedId: String?
@Published var openFileItems: [WorkspaceClient.FileItem] = []
@Published var sortFoldersOnTop: Bool = true
@Published var fileItems: [WorkspaceClient.FileItem] = []

var quickOpenState: QuickOpenState?

var openedCodeFiles: [WorkspaceClient.FileItem: CodeFileDocument] = [:]
var folderURL: URL?
private var cancellables = Set<AnyCancellable>()

deinit {
Expand All @@ -36,11 +38,10 @@ class WorkspaceDocument: NSDocument, ObservableObject, NSToolbarDelegate {

guard let idx = openFileItems.firstIndex(of: item) else { return }
let closedFileItem = openFileItems.remove(at: idx)
guard closedFileItem.id == selectedId else { return }
guard closedFileItem.id == item.id else { return }

if openFileItems.isEmpty {
selectedId = nil
self.windowControllers.first?.document = self
} else if idx == 0 {
selectedId = openFileItems.first?.id
} else {
Expand Down Expand Up @@ -101,12 +102,12 @@ class WorkspaceDocument: NSDocument, ObservableObject, NSToolbarDelegate {
}

override func read(from url: URL, ofType typeName: String) throws {
self.folderURL = url
self.workspaceClient = try .default(
fileManager: .default,
folderURL: url,
ignoredFilesAndFolders: ignoredFilesAndDirectory
)
self.quickOpenState = .init(self)
workspaceClient?
.getFiles
.sink { [weak self] files in
Expand Down Expand Up @@ -146,3 +147,58 @@ class WorkspaceDocument: NSDocument, ObservableObject, NSToolbarDelegate {
super.close()
}
}

// MARK: - Quick Open

extension WorkspaceDocument {

class QuickOpenState: ObservableObject {
init(_ workspace: WorkspaceDocument) {
self.workspace = workspace
}

var workspace: WorkspaceDocument

@Published var openQuicklyQuery: String = ""
@Published var openQuicklyFiles: [WorkspaceClient.FileItem] = []
@Published var isShowingOpenQuicklyFiles: Bool = false

func fetchOpenQuickly() {
if openQuicklyQuery == "" {
openQuicklyFiles = []
self.isShowingOpenQuicklyFiles = !openQuicklyFiles.isEmpty
return
}

DispatchQueue(label: "austincondiff.CodeEdit.quickOpen.searchFiles").async {
if let url = self.workspace.fileURL {
let enumerator = FileManager.default.enumerator(at: url,
includingPropertiesForKeys: [
.isRegularFileKey
],
options: [
.skipsHiddenFiles,
.skipsPackageDescendants
])
if let filePaths = enumerator?.allObjects as? [URL] {
let files = filePaths.filter { url in
let state1 = url.lastPathComponent.lowercased().contains(self.openQuicklyQuery.lowercased())
do {
let values = try url.resourceValues(forKeys: [.isRegularFileKey])
return state1 && (values.isRegularFile ?? false)
} catch {
return false
}
}.map { url in
WorkspaceClient.FileItem(url: url, children: nil)
}
DispatchQueue.main.async {
self.openQuicklyFiles = files
self.isShowingOpenQuicklyFiles = !self.openQuicklyFiles.isEmpty
}
}
}
}
}
}
}
Loading