Commit 6eb1bcc847
Verified · cmc
Layout: unified · split
Package.swift +3 −1
| @@ -10,7 +10,8 @@ let package = Package( | |||
| 10 | .library(name: "OrgIndex", targets: ["OrgIndex"]), | 10 | .library(name: "OrgIndex", targets: ["OrgIndex"]), |
| 11 | .library(name: "OrgWorkspace", targets: ["OrgWorkspace"]), | 11 | .library(name: "OrgWorkspace", targets: ["OrgWorkspace"]), |
| 12 | .library(name: "OrgPresentation", targets: ["OrgPresentation"]), | 12 | .library(name: "OrgPresentation", targets: ["OrgPresentation"]), |
| 13 | .library(name: "OrgEditorAppKit", targets: ["OrgEditorAppKit"]) | 13 | .library(name: "OrgEditorAppKit", targets: ["OrgEditorAppKit"]), |
| 14 | .executable(name: "OrgstarSpike", targets: ["OrgstarSpike"]) | ||
| 14 | ], | 15 | ], |
| 15 | dependencies: [ | 16 | dependencies: [ |
| 16 | .package(url: "https://github.com/groue/GRDB.swift", from: "7.11.1") | 17 | .package(url: "https://github.com/groue/GRDB.swift", from: "7.11.1") |
| @@ -22,6 +23,7 @@ let package = Package( | |||
| 22 | .target(name: "OrgWorkspace", dependencies: ["OrgIndex"]), | 23 | .target(name: "OrgWorkspace", dependencies: ["OrgIndex"]), |
| 23 | .target(name: "OrgPresentation", dependencies: ["OrgCore"]), | 24 | .target(name: "OrgPresentation", dependencies: ["OrgCore"]), |
| 24 | .target(name: "OrgEditorAppKit", dependencies: ["OrgDocument", "OrgPresentation"]), | 25 | .target(name: "OrgEditorAppKit", dependencies: ["OrgDocument", "OrgPresentation"]), |
| 26 | .executableTarget(name: "OrgstarSpike", dependencies: ["OrgEditorAppKit"]), | ||
| 25 | .testTarget(name: "OrgCoreTests", dependencies: ["OrgCore"]), | 27 | .testTarget(name: "OrgCoreTests", dependencies: ["OrgCore"]), |
| 26 | .testTarget(name: "OrgDocumentTests", dependencies: ["OrgDocument"]), | 28 | .testTarget(name: "OrgDocumentTests", dependencies: ["OrgDocument"]), |
| 27 | .testTarget(name: "OrgIndexTests", dependencies: ["OrgIndex"]), | 29 | .testTarget(name: "OrgIndexTests", dependencies: ["OrgIndex"]), |
Sources/OrgstarSpike/main.swift added +89
| @@ -0,0 +1,89 @@ | |||
| 1 | #if os(macOS) | ||
| 2 | import AppKit | ||
| 3 | import OrgDocument | ||
| 4 | import OrgEditorAppKit | ||
| 5 | |||
| 6 | /// A throwaway app for trying the editor by hand: `swift run OrgstarSpike path/to/file.org`. | ||
| 7 | /// TAB on a heading folds it, Shift-TAB folds everything, ⌘S saves. Edit timings are printed | ||
| 8 | /// every 50 edits. | ||
| 9 | @MainActor | ||
| 10 | final class SpikeDelegate: NSObject, NSApplicationDelegate { | ||
| 11 | let url: URL | ||
| 12 | var editor: OrgEditor? | ||
| 13 | var window: NSWindow? | ||
| 14 | var timings: [Duration] = [] | ||
| 15 | |||
| 16 | init(url: URL) { | ||
| 17 | self.url = url | ||
| 18 | } | ||
| 19 | |||
| 20 | func applicationDidFinishLaunching(_ notification: Notification) { | ||
| 21 | let bytes = (try? [UInt8](Data(contentsOf: url))) ?? [] | ||
| 22 | let editor = OrgEditor(document: DocumentState(bytes: bytes), frame: NSRect(x: 0, y: 0, width: 900, height: 1000)) | ||
| 23 | editor.onEditTiming = { [weak self] in self?.record($0) } | ||
| 24 | let window = NSWindow( | ||
| 25 | contentRect: NSRect(x: 100, y: 100, width: 900, height: 1000), | ||
| 26 | styleMask: [.titled, .closable, .resizable, .miniaturizable], backing: .buffered, defer: false | ||
| 27 | ) | ||
| 28 | window.title = url.lastPathComponent | ||
| 29 | window.contentView = editor.makeScrollView() | ||
| 30 | window.makeKeyAndOrderFront(nil) | ||
| 31 | window.makeFirstResponder(editor.textView) | ||
| 32 | self.editor = editor | ||
| 33 | self.window = window | ||
| 34 | buildMenu() | ||
| 35 | NSApp.activate() | ||
| 36 | } | ||
| 37 | |||
| 38 | func buildMenu() { | ||
| 39 | let main = NSMenu() | ||
| 40 | let appItem = NSMenuItem() | ||
| 41 | appItem.submenu = NSMenu() | ||
| 42 | appItem.submenu?.addItem(withTitle: "Quit", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q") | ||
| 43 | let fileItem = NSMenuItem() | ||
| 44 | fileItem.submenu = NSMenu(title: "File") | ||
| 45 | fileItem.submenu?.addItem(withTitle: "Save", action: #selector(save), keyEquivalent: "s").target = self | ||
| 46 | let editItem = NSMenuItem() | ||
| 47 | editItem.submenu = NSMenu(title: "Edit") | ||
| 48 | editItem.submenu?.addItem(withTitle: "Undo", action: Selector(("undo:")), keyEquivalent: "z") | ||
| 49 | editItem.submenu?.addItem(withTitle: "Redo", action: Selector(("redo:")), keyEquivalent: "Z") | ||
| 50 | editItem.submenu?.addItem(withTitle: "Cut", action: #selector(NSText.cut(_:)), keyEquivalent: "x") | ||
| 51 | editItem.submenu?.addItem(withTitle: "Copy", action: #selector(NSText.copy(_:)), keyEquivalent: "c") | ||
| 52 | editItem.submenu?.addItem(withTitle: "Paste", action: #selector(NSText.paste(_:)), keyEquivalent: "v") | ||
| 53 | editItem.submenu?.addItem(withTitle: "Select All", action: #selector(NSText.selectAll(_:)), keyEquivalent: "a") | ||
| 54 | [appItem, fileItem, editItem].forEach(main.addItem) | ||
| 55 | NSApp.mainMenu = main | ||
| 56 | } | ||
| 57 | |||
| 58 | @objc func save() { | ||
| 59 | guard let editor else { return } | ||
| 60 | let recovery = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0] | ||
| 61 | .appendingPathComponent("Orgstar/Recovery") | ||
| 62 | let saver = Saver(fileSystem: CoordinatedFileSystem(), recovery: FileRecoveryStore(directory: recovery)) | ||
| 63 | do { | ||
| 64 | print("save:", try editor.save(using: saver, to: url)) | ||
| 65 | } catch { | ||
| 66 | print("save failed:", error) | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | func record(_ duration: Duration) { | ||
| 71 | timings.append(duration) | ||
| 72 | guard timings.count % 50 == 0 else { return } | ||
| 73 | let sorted = timings.sorted() | ||
| 74 | print("edits: \(timings.count) p50 \(sorted[sorted.count / 2]) p95 \(sorted[sorted.count * 95 / 100])") | ||
| 75 | } | ||
| 76 | |||
| 77 | func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { true } | ||
| 78 | } | ||
| 79 | |||
| 80 | guard CommandLine.arguments.count > 1 else { | ||
| 81 | print("usage: OrgstarSpike path/to/file.org") | ||
| 82 | exit(2) | ||
| 83 | } | ||
| 84 | let app = NSApplication.shared | ||
| 85 | app.setActivationPolicy(.regular) | ||
| 86 | let delegate = SpikeDelegate(url: URL(fileURLWithPath: CommandLine.arguments[1])) | ||
| 87 | app.delegate = delegate | ||
| 88 | app.run() | ||
| 89 | #endif | ||