mirror of
https://github.com/qvacua/vimr.git
synced 2024-12-24 22:33:52 +03:00
Format VimR folder with swiftformat
This commit is contained in:
parent
ed05f433c9
commit
ac5d0ca055
@ -6,7 +6,6 @@
|
||||
import Foundation
|
||||
|
||||
class AdvancedPrefReducer: ReducerType {
|
||||
|
||||
typealias StateType = AppState
|
||||
typealias ActionType = AdvancedPref.Action
|
||||
|
||||
@ -14,7 +13,6 @@ class AdvancedPrefReducer: ReducerType {
|
||||
var state = pair.state
|
||||
|
||||
switch pair.action {
|
||||
|
||||
case let .setUseLiveResize(value):
|
||||
state.mainWindowTemplate.useLiveResize = value
|
||||
state.mainWindows.keys.forEach { state.mainWindows[$0]?.useLiveResize = value }
|
||||
|
@ -8,11 +8,9 @@ import PureLayout
|
||||
import RxSwift
|
||||
|
||||
class AdvancedPref: PrefPane, UiComponent, NSTextFieldDelegate {
|
||||
|
||||
typealias StateType = AppState
|
||||
|
||||
enum Action {
|
||||
|
||||
case setUseInteractiveZsh(Bool)
|
||||
case setUseSnapshotUpdate(Bool)
|
||||
case setTrackpadScrollResistance(Double)
|
||||
@ -21,11 +19,11 @@ class AdvancedPref: PrefPane, UiComponent, NSTextFieldDelegate {
|
||||
}
|
||||
|
||||
override var displayName: String {
|
||||
return "Advanced"
|
||||
"Advanced"
|
||||
}
|
||||
|
||||
override var pinToContainer: Bool {
|
||||
return true
|
||||
true
|
||||
}
|
||||
|
||||
required init(source: Observable<StateType>, emitter: ActionEmitter, state: StateType) {
|
||||
@ -46,10 +44,10 @@ class AdvancedPref: PrefPane, UiComponent, NSTextFieldDelegate {
|
||||
.observeOn(MainScheduler.instance)
|
||||
.subscribe(onNext: { state in
|
||||
if self.useInteractiveZsh != state.mainWindowTemplate.useInteractiveZsh
|
||||
|| self.useSnapshotUpdate != state.useSnapshotUpdate
|
||||
|| self.useLiveResize != state.mainWindowTemplate.useLiveResize
|
||||
|| self.drawsParallel != state.mainWindowTemplate.drawsParallel {
|
||||
|
||||
|| self.useSnapshotUpdate != state.useSnapshotUpdate
|
||||
|| self.useLiveResize != state.mainWindowTemplate.useLiveResize
|
||||
|| self.drawsParallel != state.mainWindowTemplate.drawsParallel
|
||||
{
|
||||
self.useInteractiveZsh = state.mainWindowTemplate.useInteractiveZsh
|
||||
self.useSnapshotUpdate = state.useSnapshotUpdate
|
||||
self.useLiveResize = state.mainWindowTemplate.useLiveResize
|
||||
@ -76,7 +74,8 @@ class AdvancedPref: PrefPane, UiComponent, NSTextFieldDelegate {
|
||||
private let drawsParallelCheckbox = NSButton(forAutoLayout: ())
|
||||
private let sensitivitySlider = NSSlider(forAutoLayout: ())
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
@available(*, unavailable)
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
@ -93,47 +92,55 @@ class AdvancedPref: PrefPane, UiComponent, NSTextFieldDelegate {
|
||||
let paneTitle = self.paneTitleTextField(title: "Advanced")
|
||||
|
||||
let useInteractiveZsh = self.useInteractiveZshCheckbox
|
||||
self.configureCheckbox(button: useInteractiveZsh,
|
||||
title: "Use interactive mode for zsh",
|
||||
action: #selector(AdvancedPref.useInteractiveZshAction(_:)))
|
||||
self.configureCheckbox(
|
||||
button: useInteractiveZsh,
|
||||
title: "Use interactive mode for zsh",
|
||||
action: #selector(AdvancedPref.useInteractiveZshAction(_:))
|
||||
)
|
||||
|
||||
let useInteractiveZshInfo = self.infoTextField(markdown: #"""
|
||||
If your login shell is `zsh`, when checked, the `-i` option will be used to launch `zsh`.\
|
||||
Checking this option may break VimR if your `.zshrc` contains complex stuff.\
|
||||
It may be a good idea to put the `PATH`-settings in `.zshenv` and let this unchecked.\
|
||||
*Use with caution.*
|
||||
"""#)
|
||||
If your login shell is `zsh`, when checked, the `-i` option will be used to launch `zsh`.\
|
||||
Checking this option may break VimR if your `.zshrc` contains complex stuff.\
|
||||
It may be a good idea to put the `PATH`-settings in `.zshenv` and let this unchecked.\
|
||||
*Use with caution.*
|
||||
"""#)
|
||||
|
||||
let useSnapshotUpdate = self.useSnapshotUpdateCheckbox
|
||||
self.configureCheckbox(button: self.useSnapshotUpdateCheckbox,
|
||||
title: "Use Snapshot Update Channel",
|
||||
action: #selector(AdvancedPref.useSnapshotUpdateChannelAction(_:)))
|
||||
self.configureCheckbox(
|
||||
button: self.useSnapshotUpdateCheckbox,
|
||||
title: "Use Snapshot Update Channel",
|
||||
action: #selector(AdvancedPref.useSnapshotUpdateChannelAction(_:))
|
||||
)
|
||||
|
||||
let useSnapshotUpdateInfo = self.infoTextField(markdown: #"""
|
||||
If you are adventurous, check this. You'll be test driving the newest snapshot builds\
|
||||
of VimR in no time!
|
||||
"""#)
|
||||
If you are adventurous, check this. You'll be test driving the newest snapshot builds\
|
||||
of VimR in no time!
|
||||
"""#)
|
||||
|
||||
let useLiveResize = self.useLiveResizeCheckbox
|
||||
self.configureCheckbox(button: useLiveResize,
|
||||
title: "Use Live Window Resizing",
|
||||
action: #selector(AdvancedPref.useLiveResizeAction(_:)))
|
||||
self.configureCheckbox(
|
||||
button: useLiveResize,
|
||||
title: "Use Live Window Resizing",
|
||||
action: #selector(AdvancedPref.useLiveResizeAction(_:))
|
||||
)
|
||||
|
||||
let useLiveResizeInfo = self.infoTextField(markdown: #"""
|
||||
The Live Resizing is yet experimental. You may experience some issues.\
|
||||
If you do, please report them at [GitHub](https://github.com/qvacua/vimr/issues).
|
||||
"""#)
|
||||
The Live Resizing is yet experimental. You may experience some issues.\
|
||||
If you do, please report them at [GitHub](https://github.com/qvacua/vimr/issues).
|
||||
"""#)
|
||||
|
||||
let drawsParallelBox = self.drawsParallelCheckbox
|
||||
self.configureCheckbox(button: drawsParallelBox,
|
||||
title: "Use Concurrent Rendering",
|
||||
action: #selector(AdvancedPref.drawParallelAction(_:)))
|
||||
self.configureCheckbox(
|
||||
button: drawsParallelBox,
|
||||
title: "Use Concurrent Rendering",
|
||||
action: #selector(AdvancedPref.drawParallelAction(_:))
|
||||
)
|
||||
|
||||
let drawsParallelInfo = self.infoTextField(markdown: #"""
|
||||
VimR can compute the glyphs concurrently. This may result in faster rendering,\
|
||||
depending on the situation. It will definitely result in higher CPU usage, e.g.\
|
||||
when scrolling very fast.
|
||||
"""#)
|
||||
VimR can compute the glyphs concurrently. This may result in faster rendering,\
|
||||
depending on the situation. It will definitely result in higher CPU usage, e.g.\
|
||||
when scrolling very fast.
|
||||
"""#)
|
||||
|
||||
let sensitivityTitle = self.titleTextField(title: "Scroll Sensitivity:")
|
||||
let sensitivity = self.sensitivitySlider
|
||||
@ -142,9 +149,9 @@ when scrolling very fast.
|
||||
sensitivity.target = self
|
||||
sensitivity.action = #selector(sensitivitySliderAction)
|
||||
let sensitivityInfo = self.infoTextField(markdown: #"""
|
||||
Trackpad scroll sensitivity is yet experimental. You may experience some issues.\
|
||||
If you do, please report them at [GitHub issue #572](https://github.com/qvacua/vimr/issues/572).
|
||||
"""#)
|
||||
Trackpad scroll sensitivity is yet experimental. You may experience some issues.\
|
||||
If you do, please report them at [GitHub issue #572](https://github.com/qvacua/vimr/issues/572).
|
||||
"""#)
|
||||
|
||||
// We set the value of the NSSlider only at the beginning.
|
||||
self.sensitivitySlider.doubleValue = self.sensitivity
|
||||
@ -156,7 +163,7 @@ If you do, please report them at [GitHub issue #572](https://github.com/qvacua/v
|
||||
self.addSubview(useInteractiveZsh)
|
||||
self.addSubview(useInteractiveZshInfo)
|
||||
self.addSubview(sensitivityTitle)
|
||||
self.addSubview(sensitivitySlider)
|
||||
self.addSubview(self.sensitivitySlider)
|
||||
self.addSubview(sensitivityInfo)
|
||||
self.addSubview(useLiveResize)
|
||||
self.addSubview(useLiveResizeInfo)
|
||||
@ -174,7 +181,7 @@ If you do, please report them at [GitHub issue #572](https://github.com/qvacua/v
|
||||
sensitivity.autoAlignAxis(.baseline, toSameAxisOf: sensitivityTitle)
|
||||
sensitivity.autoPinEdge(.left, to: .right, of: sensitivityTitle, withOffset: 5)
|
||||
|
||||
sensitivityInfo.autoPinEdge(.top, to: .bottom, of: sensitivitySlider, withOffset: 5)
|
||||
sensitivityInfo.autoPinEdge(.top, to: .bottom, of: self.sensitivitySlider, withOffset: 5)
|
||||
sensitivityInfo.autoPinEdge(.left, to: .right, of: sensitivityTitle, withOffset: 5)
|
||||
|
||||
useLiveResize.autoPinEdge(.top, to: .bottom, of: sensitivityInfo, withOffset: 18)
|
||||
@ -204,8 +211,8 @@ If you do, please report them at [GitHub issue #572](https://github.com/qvacua/v
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
extension AdvancedPref {
|
||||
|
||||
extension AdvancedPref {
|
||||
@objc func useLiveResizeAction(_ sender: NSButton) {
|
||||
self.emit(.setUseLiveResize(sender.boolState))
|
||||
}
|
||||
|
@ -4,21 +4,19 @@
|
||||
*/
|
||||
|
||||
import Cocoa
|
||||
import RxSwift
|
||||
import PureLayout
|
||||
import Sparkle
|
||||
import DictionaryCoding
|
||||
import os
|
||||
import Commons
|
||||
import CommonsObjC
|
||||
import DictionaryCoding
|
||||
import os
|
||||
import PureLayout
|
||||
import RxSwift
|
||||
import Sparkle
|
||||
|
||||
let debugMenuItemIdentifier = NSUserInterfaceItemIdentifier("debug-menu-item")
|
||||
|
||||
@NSApplicationMain
|
||||
class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDelegate {
|
||||
|
||||
struct OpenConfig {
|
||||
|
||||
var urls: [URL]
|
||||
var cwd: URL
|
||||
|
||||
@ -29,7 +27,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
||||
}
|
||||
|
||||
enum Action {
|
||||
|
||||
case newMainWindow(config: OpenConfig)
|
||||
case openInKeyWindow(config: OpenConfig)
|
||||
|
||||
@ -42,9 +39,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
||||
var initialAppState: AppState
|
||||
|
||||
let dictDecoder = DictionaryDecoder()
|
||||
if let stateDict = UserDefaults.standard.value(forKey: PrefMiddleware.compatibleVersion) as? [String: Any],
|
||||
let state = try? dictDecoder.decode(AppState.self, from: stateDict) {
|
||||
|
||||
if let stateDict = UserDefaults.standard
|
||||
.value(forKey: PrefMiddleware.compatibleVersion) as? [String: Any],
|
||||
let state = try? dictDecoder.decode(AppState.self, from: stateDict)
|
||||
{
|
||||
initialAppState = state
|
||||
} else {
|
||||
initialAppState = .default
|
||||
@ -113,8 +111,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
||||
|
||||
private var launching = true
|
||||
|
||||
private let log = OSLog(subsystem: Defs.loggerSubsystem,
|
||||
category: Defs.LoggerCategory.general)
|
||||
private let log = OSLog(subsystem: Defs.loggerSubsystem, category: Defs.LoggerCategory.general)
|
||||
|
||||
private func setSparkleUrl(_ snapshot: Bool) {
|
||||
if snapshot {
|
||||
@ -130,27 +127,29 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
||||
}
|
||||
|
||||
// MARK: - NSApplicationDelegate
|
||||
extension AppDelegate {
|
||||
|
||||
extension AppDelegate {
|
||||
func applicationWillFinishLaunching(_: Notification) {
|
||||
self.launching = true
|
||||
|
||||
let appleEventManager = NSAppleEventManager.shared()
|
||||
appleEventManager.setEventHandler(self,
|
||||
andSelector: #selector(AppDelegate.handle(getUrlEvent:replyEvent:)),
|
||||
forEventClass: UInt32(kInternetEventClass),
|
||||
andEventID: UInt32(kAEGetURL))
|
||||
appleEventManager.setEventHandler(
|
||||
self,
|
||||
andSelector: #selector(AppDelegate.handle(getUrlEvent:replyEvent:)),
|
||||
forEventClass: UInt32(kInternetEventClass),
|
||||
andEventID: UInt32(kAEGetURL)
|
||||
)
|
||||
}
|
||||
|
||||
func applicationDidFinishLaunching(_: Notification) {
|
||||
self.launching = false
|
||||
|
||||
#if DEBUG
|
||||
NSApp.mainMenu?.items.first { $0.identifier == debugMenuItemIdentifier }?.isHidden = false
|
||||
NSApp.mainMenu?.items.first { $0.identifier == debugMenuItemIdentifier }?.isHidden = false
|
||||
#endif
|
||||
}
|
||||
|
||||
func applicationOpenUntitledFile(_ sender: NSApplication) -> Bool {
|
||||
func applicationOpenUntitledFile(_: NSApplication) -> Bool {
|
||||
if self.launching {
|
||||
if self.openNewMainWindowOnLaunch {
|
||||
self.newDocument(self)
|
||||
@ -166,7 +165,7 @@ extension AppDelegate {
|
||||
return false
|
||||
}
|
||||
|
||||
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
|
||||
func applicationShouldTerminate(_: NSApplication) -> NSApplication.TerminateReply {
|
||||
self.context.savePrefs()
|
||||
|
||||
guard self.hasMainWindows else {
|
||||
@ -210,7 +209,8 @@ extension AppDelegate {
|
||||
func application(_ sender: NSApplication, openFiles filenames: [String]) {
|
||||
let urls = filenames.map { URL(fileURLWithPath: $0) }
|
||||
let config = OpenConfig(
|
||||
urls: urls, cwd: FileUtils.userHomeUrl, cliPipePath: nil, nvimArgs: nil, envDict: nil, line: nil
|
||||
urls: urls, cwd: FileUtils.userHomeUrl, cliPipePath: nil, nvimArgs: nil, envDict: nil,
|
||||
line: nil
|
||||
)
|
||||
self.emit(.newMainWindow(config: config))
|
||||
|
||||
@ -226,37 +226,32 @@ extension AppDelegate {
|
||||
}
|
||||
|
||||
// MARK: - AppleScript
|
||||
|
||||
extension AppDelegate {
|
||||
@objc func handle(
|
||||
getUrlEvent event: NSAppleEventDescriptor,
|
||||
replyEvent _: NSAppleEventDescriptor
|
||||
) {
|
||||
guard let urlString = event.paramDescriptor(forKeyword: UInt32(keyDirectObject))?.stringValue
|
||||
else { return }
|
||||
|
||||
@objc func handle(getUrlEvent event: NSAppleEventDescriptor, replyEvent: NSAppleEventDescriptor) {
|
||||
guard let urlString = event.paramDescriptor(forKeyword: UInt32(keyDirectObject))?.stringValue else {
|
||||
return
|
||||
}
|
||||
guard let url = URL(string: urlString) else { return }
|
||||
|
||||
guard let url = URL(string: urlString) else {
|
||||
return
|
||||
}
|
||||
guard url.scheme == "vimr" else { return }
|
||||
|
||||
guard url.scheme == "vimr" else {
|
||||
return
|
||||
}
|
||||
guard let rawAction = url.host else { return }
|
||||
|
||||
guard let rawAction = url.host else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let action = VimRUrlAction(rawValue: rawAction) else {
|
||||
return
|
||||
}
|
||||
guard let action = VimRUrlAction(rawValue: rawAction) else { return }
|
||||
|
||||
let rawParams = url.query?.components(separatedBy: "&") ?? []
|
||||
|
||||
guard let pipePath = queryParam(pipePathPrefix, from: rawParams, transforming: identity).first else {
|
||||
guard let pipePath = queryParam(pipePathPrefix, from: rawParams, transforming: identity).first
|
||||
else {
|
||||
let alert = NSAlert()
|
||||
alert.alertStyle = .informational
|
||||
alert.messageText = "Outdated Command Line Tool?"
|
||||
alert.informativeText = "It seems that the installed vimr command line tool is outdated." +
|
||||
"Please re-install it from the General Preferences."
|
||||
"Please re-install it from the General Preferences."
|
||||
alert.runModal()
|
||||
|
||||
return
|
||||
@ -275,7 +270,7 @@ extension AppDelegate {
|
||||
|
||||
let envDict: [String: String]?
|
||||
if let envPath = queryParam(envPathPrefix, from: rawParams, transforming: identity).first {
|
||||
envDict = stringDict(from: URL(fileURLWithPath: envPath))
|
||||
envDict = self.stringDict(from: URL(fileURLWithPath: envPath))
|
||||
if FileManager.default.fileExists(atPath: envPath) {
|
||||
do {
|
||||
try FileManager.default.removeItem(atPath: envPath)
|
||||
@ -287,45 +282,79 @@ extension AppDelegate {
|
||||
envDict = nil
|
||||
}
|
||||
|
||||
let line = queryParam(linePrefix, from: rawParams, transforming: { Int($0) }).compactMap { $0 }.first
|
||||
let urls = queryParam(filePrefix, from: rawParams, transforming: { URL(fileURLWithPath: $0) })
|
||||
let cwd = queryParam(cwdPrefix,
|
||||
from: rawParams,
|
||||
transforming: { URL(fileURLWithPath: $0) }).first ?? FileUtils.userHomeUrl
|
||||
let wait = queryParam(waitPrefix, from: rawParams, transforming: { $0 == "true" ? true : false }).first ?? false
|
||||
let line = self.queryParam(linePrefix, from: rawParams, transforming: { Int($0) })
|
||||
.compactMap { $0 }.first
|
||||
let urls = self.queryParam(
|
||||
filePrefix,
|
||||
from: rawParams,
|
||||
transforming: { URL(fileURLWithPath: $0) }
|
||||
)
|
||||
let cwd = self.queryParam(
|
||||
cwdPrefix,
|
||||
from: rawParams,
|
||||
transforming: { URL(fileURLWithPath: $0) }
|
||||
).first ?? FileUtils.userHomeUrl
|
||||
let wait = self.queryParam(
|
||||
waitPrefix,
|
||||
from: rawParams,
|
||||
transforming: { $0 == "true" ? true : false }
|
||||
).first ?? false
|
||||
|
||||
if wait == false {
|
||||
_ = Darwin.close(Darwin.open(pipePath, O_WRONLY))
|
||||
}
|
||||
if wait == false { _ = Darwin.close(Darwin.open(pipePath, O_WRONLY)) }
|
||||
|
||||
// If we don't do this, the window is active, but not in front.
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
|
||||
switch action {
|
||||
|
||||
case .activate, .newWindow:
|
||||
let config = OpenConfig(urls: urls, cwd: cwd, cliPipePath: pipePath, nvimArgs: nil, envDict: envDict, line: line)
|
||||
let config = OpenConfig(
|
||||
urls: urls,
|
||||
cwd: cwd,
|
||||
cliPipePath: pipePath,
|
||||
nvimArgs: nil,
|
||||
envDict: envDict,
|
||||
line: line
|
||||
)
|
||||
self.emit(.newMainWindow(config: config))
|
||||
|
||||
case .open:
|
||||
let config = OpenConfig(urls: urls, cwd: cwd, cliPipePath: pipePath, nvimArgs: nil, envDict: envDict, line: line)
|
||||
let config = OpenConfig(
|
||||
urls: urls,
|
||||
cwd: cwd,
|
||||
cliPipePath: pipePath,
|
||||
nvimArgs: nil,
|
||||
envDict: envDict,
|
||||
line: line
|
||||
)
|
||||
self.emit(.openInKeyWindow(config: config))
|
||||
|
||||
case .separateWindows:
|
||||
urls.forEach {
|
||||
let config = OpenConfig(urls: [$0], cwd: cwd, cliPipePath: pipePath, nvimArgs: nil, envDict: nil, line: line)
|
||||
let config = OpenConfig(
|
||||
urls: [$0],
|
||||
cwd: cwd,
|
||||
cliPipePath: pipePath,
|
||||
nvimArgs: nil,
|
||||
envDict: nil,
|
||||
line: line
|
||||
)
|
||||
self.emit(.newMainWindow(config: config))
|
||||
}
|
||||
|
||||
case .nvim:
|
||||
let config = OpenConfig(urls: urls,
|
||||
cwd: cwd,
|
||||
cliPipePath: pipePath,
|
||||
nvimArgs: queryParam(nvimArgsPrefix, from: rawParams, transforming: identity),
|
||||
envDict: envDict,
|
||||
line: line)
|
||||
let config = OpenConfig(
|
||||
urls: urls,
|
||||
cwd: cwd,
|
||||
cliPipePath: pipePath,
|
||||
nvimArgs: queryParam(
|
||||
nvimArgsPrefix,
|
||||
from: rawParams,
|
||||
transforming: identity
|
||||
),
|
||||
envDict: envDict,
|
||||
line: line
|
||||
)
|
||||
self.emit(.newMainWindow(config: config))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -345,9 +374,9 @@ extension AppDelegate {
|
||||
|
||||
private func queryParam<T>(_ prefix: String,
|
||||
from rawParams: [String],
|
||||
transforming transform: (String) -> T) -> [T] {
|
||||
|
||||
return rawParams
|
||||
transforming transform: (String) -> T) -> [T]
|
||||
{
|
||||
rawParams
|
||||
.filter { $0.hasPrefix(prefix) }
|
||||
.compactMap { $0.without(prefix: prefix).removingPercentEncoding }
|
||||
.map(transform)
|
||||
@ -355,26 +384,22 @@ extension AppDelegate {
|
||||
}
|
||||
|
||||
// MARK: - IBActions
|
||||
extension AppDelegate {
|
||||
|
||||
extension AppDelegate {
|
||||
@IBAction func checkForUpdates(_ sender: Any?) {
|
||||
updater.checkForUpdates(sender)
|
||||
}
|
||||
|
||||
@IBAction func newDocument(_ sender: Any?) {
|
||||
@IBAction func newDocument(_: Any?) {
|
||||
let config = OpenConfig(
|
||||
urls: [], cwd: FileUtils.userHomeUrl, cliPipePath: nil, nvimArgs: nil, envDict: nil, line: nil
|
||||
)
|
||||
self.emit(.newMainWindow(config: config))
|
||||
}
|
||||
|
||||
@IBAction func openInNewWindow(_ sender: Any?) {
|
||||
self.openDocument(sender)
|
||||
}
|
||||
@IBAction func openInNewWindow(_ sender: Any?) { self.openDocument(sender) }
|
||||
|
||||
@IBAction func showPrefWindow(_ sender: Any?) {
|
||||
self.emit(.preferences)
|
||||
}
|
||||
@IBAction func showPrefWindow(_: Any?) { self.emit(.preferences) }
|
||||
|
||||
// Invoked when no main window is open.
|
||||
@IBAction func openDocument(_: Any?) {
|
||||
@ -382,9 +407,7 @@ extension AppDelegate {
|
||||
panel.canChooseDirectories = true
|
||||
panel.allowsMultipleSelection = true
|
||||
panel.begin { result in
|
||||
guard result == .OK else {
|
||||
return
|
||||
}
|
||||
guard result == .OK else { return }
|
||||
|
||||
let urls = panel.urls
|
||||
let commonParentUrl = FileUtils.commonParent(of: urls)
|
||||
@ -398,20 +421,21 @@ extension AppDelegate {
|
||||
}
|
||||
|
||||
// MARK: - NSUserNotificationCenterDelegate
|
||||
extension AppDelegate {
|
||||
|
||||
public func userNotificationCenter(_ center: NSUserNotificationCenter, shouldPresent _: NSUserNotification) -> Bool {
|
||||
return true
|
||||
}
|
||||
extension AppDelegate {
|
||||
func userNotificationCenter(
|
||||
_: NSUserNotificationCenter,
|
||||
shouldPresent _: NSUserNotification
|
||||
) -> Bool { true }
|
||||
}
|
||||
|
||||
// Keep the rawValues in sync with Action in the `vimr` Python script.
|
||||
private enum VimRUrlAction: String {
|
||||
case activate = "activate"
|
||||
case open = "open"
|
||||
case activate
|
||||
case open
|
||||
case newWindow = "open-in-new-window"
|
||||
case separateWindows = "open-in-separate-windows"
|
||||
case nvim = "nvim"
|
||||
case nvim
|
||||
}
|
||||
|
||||
private let updater = SUUpdater()
|
||||
|
@ -6,7 +6,6 @@
|
||||
import Foundation
|
||||
|
||||
class AppDelegateReducer: ReducerType {
|
||||
|
||||
typealias StateType = AppState
|
||||
typealias ActionType = AppDelegate.Action
|
||||
|
||||
@ -18,7 +17,6 @@ class AppDelegateReducer: ReducerType {
|
||||
var state = pair.state
|
||||
|
||||
switch pair.action {
|
||||
|
||||
case let .newMainWindow(config):
|
||||
let mainWindow = self.newMainWindow(with: state, config: config)
|
||||
state.mainWindows[mainWindow.uuid] = mainWindow
|
||||
@ -30,7 +28,7 @@ class AppDelegateReducer: ReducerType {
|
||||
break
|
||||
}
|
||||
|
||||
state.mainWindows[uuid]?.urlsToOpen = config.urls.toDict { url in MainWindow.OpenMode.default }
|
||||
state.mainWindows[uuid]?.urlsToOpen = config.urls.toDict { _ in MainWindow.OpenMode.default }
|
||||
state.mainWindows[uuid]?.cwd = config.cwd
|
||||
if let line = config.line {
|
||||
state.mainWindows[uuid]?.goToLineFromCli = Marked(line)
|
||||
@ -38,7 +36,6 @@ class AppDelegateReducer: ReducerType {
|
||||
|
||||
case .preferences:
|
||||
state.preferencesOpen = Marked(true)
|
||||
|
||||
}
|
||||
|
||||
return (state, pair.action, true)
|
||||
@ -46,8 +43,9 @@ class AppDelegateReducer: ReducerType {
|
||||
|
||||
private let baseServerUrl: URL
|
||||
|
||||
private func newMainWindow(with state: AppState, config: AppDelegate.OpenConfig) -> MainWindow.State {
|
||||
|
||||
private func newMainWindow(with state: AppState, config: AppDelegate.OpenConfig) -> MainWindow
|
||||
.State
|
||||
{
|
||||
var mainWindow = state.mainWindowTemplate
|
||||
|
||||
mainWindow.uuid = UUID()
|
||||
@ -77,7 +75,7 @@ class AppDelegateReducer: ReducerType {
|
||||
}
|
||||
|
||||
private func frame(relativeTo refFrame: CGRect) -> CGRect {
|
||||
return refFrame.offsetBy(dx: cascadeX, dy: -cascadeY)
|
||||
refFrame.offsetBy(dx: cascadeX, dy: -cascadeY)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,15 +6,11 @@
|
||||
import Cocoa
|
||||
import Down
|
||||
|
||||
|
||||
extension NSView {
|
||||
|
||||
@objc var isFirstResponder: Bool { self.window?.firstResponder == self }
|
||||
}
|
||||
|
||||
|
||||
extension NSAttributedString {
|
||||
|
||||
static func infoLabel(markdown: String) -> NSAttributedString {
|
||||
let down = Down(markdownString: markdown)
|
||||
guard let result = try? down.toAttributedString(styler: downStyler) else {
|
||||
@ -26,7 +22,6 @@ extension NSAttributedString {
|
||||
}
|
||||
|
||||
extension NSTableView {
|
||||
|
||||
static func standardTableView() -> NSTableView {
|
||||
let tableView = NSTableView(frame: CGRect.zero)
|
||||
|
||||
@ -53,7 +48,6 @@ extension NSTableView {
|
||||
}
|
||||
|
||||
extension NSOutlineView {
|
||||
|
||||
static func standardOutlineView() -> NSOutlineView {
|
||||
let outlineView = NSOutlineView(frame: CGRect.zero)
|
||||
NSOutlineView.configure(toStandard: outlineView)
|
||||
@ -99,18 +93,16 @@ extension NSOutlineView {
|
||||
}
|
||||
|
||||
extension NSTextField {
|
||||
|
||||
static func defaultTitleTextField() -> NSTextField {
|
||||
let field = NSTextField(forAutoLayout: ())
|
||||
field.backgroundColor = NSColor.clear;
|
||||
field.isEditable = false;
|
||||
field.isBordered = false;
|
||||
field.backgroundColor = NSColor.clear
|
||||
field.isEditable = false
|
||||
field.isBordered = false
|
||||
return field
|
||||
}
|
||||
}
|
||||
|
||||
extension NSScrollView {
|
||||
|
||||
static func standardScrollView() -> NSScrollView {
|
||||
let scrollView = NSScrollView(frame: CGRect.zero)
|
||||
|
||||
@ -124,8 +116,7 @@ extension NSScrollView {
|
||||
}
|
||||
}
|
||||
|
||||
private class AttributedStringMarkdownStyler {
|
||||
|
||||
private enum AttributedStringMarkdownStyler {
|
||||
static func new() -> Styler {
|
||||
let fonts = StaticFontCollection(
|
||||
body: NSFont.systemFont(ofSize: NSFont.smallSystemFontSize),
|
||||
@ -138,7 +129,6 @@ private class AttributedStringMarkdownStyler {
|
||||
}
|
||||
|
||||
private struct ParagraphStyles: ParagraphStyleCollection {
|
||||
|
||||
let heading1: NSParagraphStyle
|
||||
let heading2: NSParagraphStyle
|
||||
let heading3: NSParagraphStyle
|
||||
@ -171,7 +161,6 @@ private struct ParagraphStyles: ParagraphStyleCollection {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private let fontCollection = StaticFontCollection(
|
||||
body: NSFont.systemFont(ofSize: NSFont.smallSystemFontSize),
|
||||
code: NSFont.userFixedPitchFont(ofSize: NSFont.smallSystemFontSize)!
|
||||
|
@ -4,16 +4,14 @@
|
||||
*/
|
||||
|
||||
import Cocoa
|
||||
import NvimView
|
||||
import PureLayout
|
||||
import RxSwift
|
||||
import NvimView
|
||||
|
||||
class AppearancePref: PrefPane, NSComboBoxDelegate, NSControlTextEditingDelegate, NSFontChanging {
|
||||
|
||||
typealias StateType = AppState
|
||||
|
||||
enum Action {
|
||||
|
||||
case setUsesColorscheme(Bool)
|
||||
case setShowsFileIcon(Bool)
|
||||
case setUsesLigatures(Bool)
|
||||
@ -58,12 +56,12 @@ class AppearancePref: PrefPane, NSComboBoxDelegate, NSControlTextEditingDelegate
|
||||
let appearance = state.mainWindowTemplate.appearance
|
||||
|
||||
guard self.font != appearance.font
|
||||
|| self.linespacing != appearance.linespacing
|
||||
|| self.characterspacing != appearance.characterspacing
|
||||
|| self.usesLigatures != appearance.usesLigatures
|
||||
|| self.usesColorscheme != appearance.usesTheme
|
||||
|| self.showsFileIcon != appearance.showsFileIcon
|
||||
else { return }
|
||||
|| self.linespacing != appearance.linespacing
|
||||
|| self.characterspacing != appearance.characterspacing
|
||||
|| self.usesLigatures != appearance.usesLigatures
|
||||
|| self.usesColorscheme != appearance.usesTheme
|
||||
|| self.showsFileIcon != appearance.showsFileIcon
|
||||
else { return }
|
||||
|
||||
self.font = appearance.font
|
||||
self.linespacing = appearance.linespacing
|
||||
@ -96,15 +94,16 @@ class AppearancePref: PrefPane, NSComboBoxDelegate, NSControlTextEditingDelegate
|
||||
private let previewArea = NSTextView(frame: .zero)
|
||||
|
||||
private let exampleText = #"""
|
||||
abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
||||
0123456789 -~ - ~
|
||||
(){}[] +-*/= .,;:!?#&$%@|^
|
||||
<- -> => >> << >>= =<< ..
|
||||
:: -< >- -<< >>- ++ /= ==
|
||||
"""#
|
||||
abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
||||
0123456789 -~ - ~
|
||||
(){}[] +-*/= .,;:!?#&$%@|^
|
||||
<- -> => >> << >>= =<< ..
|
||||
:: -< >- -<< >>- ++ /= ==
|
||||
"""#
|
||||
|
||||
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||||
@available(*, unavailable)
|
||||
required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||||
|
||||
private func addViews() {
|
||||
let paneTitle = self.paneTitleTextField(title: "Appearance")
|
||||
@ -117,9 +116,9 @@ ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
||||
)
|
||||
|
||||
let useColorschemeInfo = self.infoTextField(markdown: #"""
|
||||
If checked, the colors of the selected `colorscheme` will be used to render tools,\
|
||||
for example the file browser.
|
||||
"""#)
|
||||
If checked, the colors of the selected `colorscheme` will be used to render tools,\
|
||||
for example the file browser.
|
||||
"""#)
|
||||
|
||||
let fileIcon = self.fileIconCheckbox
|
||||
self.configureCheckbox(
|
||||
@ -129,9 +128,9 @@ for example the file browser.
|
||||
)
|
||||
|
||||
let fileIconInfo = self.infoTextField(markdown: #"""
|
||||
In case the selected `colorscheme` does not play well with the file icons\
|
||||
in the file browser and the buffer list, you can turn them off.
|
||||
"""#)
|
||||
In case the selected `colorscheme` does not play well with the file icons\
|
||||
in the file browser and the buffer list, you can turn them off.
|
||||
"""#)
|
||||
|
||||
let fontTitle = self.titleTextField(title: "Default Font:")
|
||||
let fontPanelButton = self.fontPanelButton
|
||||
@ -142,9 +141,9 @@ in the file browser and the buffer list, you can turn them off.
|
||||
fontPanelButton.action = #selector(AppearancePref.showFontPanel(_:))
|
||||
|
||||
let fontInfo = self.infoTextField(markdown: #"""
|
||||
The font panel will show variable width fonts, but VimR does not support them.\
|
||||
If you select a variable width font, the rendering will be ... well ... questionable.
|
||||
"""#)
|
||||
The font panel will show variable width fonts, but VimR does not support them.\
|
||||
If you select a variable width font, the rendering will be ... well ... questionable.
|
||||
"""#)
|
||||
|
||||
let linespacingTitle = self.titleTextField(title: "Line Spacing:")
|
||||
let linespacingField = self.linespacingField
|
||||
@ -278,8 +277,9 @@ If you select a variable width font, the rendering will be ... well ... question
|
||||
}
|
||||
|
||||
private func updateViews() {
|
||||
sharedFontPanel.setPanelFont(font, isMultiple: false)
|
||||
self.fontPanelButton.title = font.displayName.map { "\($0) \(font.pointSize)" } ?? "Show fonts..."
|
||||
sharedFontPanel.setPanelFont(self.font, isMultiple: false)
|
||||
self.fontPanelButton.title = self.font.displayName
|
||||
.map { "\($0) \(font.pointSize)" } ?? "Show fonts..."
|
||||
self.linespacingField.stringValue = String(format: "%.2f", self.linespacing)
|
||||
self.characterspacingField.stringValue = String(format: "%.2f", self.characterspacing)
|
||||
self.ligatureCheckbox.boolState = self.usesLigatures
|
||||
@ -296,6 +296,7 @@ If you select a variable width font, the rendering will be ... well ... question
|
||||
}
|
||||
|
||||
// MARK: - NSFontChanging
|
||||
|
||||
extension AppearancePref {
|
||||
func changeFont(_ sender: NSFontManager?) {
|
||||
guard let fontManager = sender else { return }
|
||||
@ -306,8 +307,8 @@ extension AppearancePref {
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
extension AppearancePref {
|
||||
|
||||
extension AppearancePref {
|
||||
@objc func usesColorschemeAction(_ sender: NSButton) {
|
||||
self.emit(.setUsesColorscheme(sender.boolState))
|
||||
}
|
||||
@ -351,7 +352,6 @@ extension AppearancePref {
|
||||
return cgfCharacterspacing
|
||||
}
|
||||
|
||||
|
||||
private func cappedFontSize(_ size: Int) -> CGFloat {
|
||||
let cgfSize = size.cgf
|
||||
|
||||
|
@ -6,7 +6,6 @@
|
||||
import Foundation
|
||||
|
||||
class AppearancePrefReducer: ReducerType {
|
||||
|
||||
typealias StateType = AppState
|
||||
typealias ActionType = AppearancePref.Action
|
||||
|
||||
@ -15,7 +14,6 @@ class AppearancePrefReducer: ReducerType {
|
||||
var appearance = state.mainWindowTemplate.appearance
|
||||
|
||||
switch pair.action {
|
||||
|
||||
case let .setUsesColorscheme(value):
|
||||
appearance.usesTheme = value
|
||||
|
||||
@ -30,10 +28,9 @@ class AppearancePrefReducer: ReducerType {
|
||||
|
||||
case let .setLinespacing(linespacing):
|
||||
appearance.linespacing = linespacing
|
||||
|
||||
|
||||
case let .setCharacterspacing(characterspacing):
|
||||
appearance.characterspacing = characterspacing
|
||||
|
||||
}
|
||||
|
||||
self.modify(state: &state, with: appearance)
|
||||
|
@ -7,7 +7,6 @@ import Cocoa
|
||||
import Sparkle
|
||||
|
||||
class Application: NSApplication {
|
||||
|
||||
override init() {
|
||||
setPressAndHoldSetting()
|
||||
super.init()
|
||||
|
@ -4,21 +4,20 @@
|
||||
*/
|
||||
|
||||
import Cocoa
|
||||
import RxSwift
|
||||
import PureLayout
|
||||
import NvimView
|
||||
import Commons
|
||||
import NvimView
|
||||
import PureLayout
|
||||
import RxSwift
|
||||
|
||||
class BuffersList: NSView,
|
||||
UiComponent,
|
||||
NSTableViewDataSource,
|
||||
NSTableViewDelegate,
|
||||
ThemedView {
|
||||
|
||||
UiComponent,
|
||||
NSTableViewDataSource,
|
||||
NSTableViewDelegate,
|
||||
ThemedView
|
||||
{
|
||||
typealias StateType = MainWindow.State
|
||||
|
||||
enum Action {
|
||||
|
||||
case open(NvimView.Buffer)
|
||||
}
|
||||
|
||||
@ -50,7 +49,8 @@ class BuffersList: NSView,
|
||||
.observeOn(MainScheduler.instance)
|
||||
.subscribe(onNext: { state in
|
||||
if state.viewToBeFocused != nil,
|
||||
case .bufferList = state.viewToBeFocused! {
|
||||
case .bufferList = state.viewToBeFocused!
|
||||
{
|
||||
self.beFirstResponder()
|
||||
}
|
||||
|
||||
@ -59,13 +59,15 @@ class BuffersList: NSView,
|
||||
themeChanged: state.appearance.theme.mark != self.lastThemeMark,
|
||||
usesTheme: state.appearance.usesTheme,
|
||||
forTheme: { self.updateTheme(state.appearance.theme) },
|
||||
forDefaultTheme: { self.updateTheme(Marked(Theme.default)) })
|
||||
forDefaultTheme: { self.updateTheme(Marked(Theme.default)) }
|
||||
)
|
||||
|
||||
self.usesTheme = state.appearance.usesTheme
|
||||
|
||||
if self.buffers == state.buffers
|
||||
&& !themeChanged
|
||||
&& self.showsFileIcon == state.appearance.showsFileIcon {
|
||||
if self.buffers == state.buffers,
|
||||
!themeChanged,
|
||||
self.showsFileIcon == state.appearance.showsFileIcon
|
||||
{
|
||||
return
|
||||
}
|
||||
|
||||
@ -87,7 +89,8 @@ class BuffersList: NSView,
|
||||
|
||||
private var buffers = [NvimView.Buffer]()
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
@available(*, unavailable)
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
@ -109,11 +112,11 @@ class BuffersList: NSView,
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
extension BuffersList {
|
||||
|
||||
@objc func doubleClickAction(_ sender: Any?) {
|
||||
extension BuffersList {
|
||||
@objc func doubleClickAction(_: Any?) {
|
||||
let clickedRow = self.bufferList.clickedRow
|
||||
guard clickedRow >= 0 && clickedRow < self.buffers.count else {
|
||||
guard clickedRow >= 0, clickedRow < self.buffers.count else {
|
||||
return
|
||||
}
|
||||
|
||||
@ -122,31 +125,33 @@ extension BuffersList {
|
||||
}
|
||||
|
||||
// MARK: - NSTableViewDataSource
|
||||
extension BuffersList {
|
||||
|
||||
extension BuffersList {
|
||||
@objc(numberOfRowsInTableView:)
|
||||
func numberOfRows(in tableView: NSTableView) -> Int {
|
||||
return self.buffers.count
|
||||
func numberOfRows(in _: NSTableView) -> Int {
|
||||
self.buffers.count
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - NSTableViewDelegate
|
||||
extension BuffersList {
|
||||
|
||||
extension BuffersList {
|
||||
public func tableView(
|
||||
_ tableView: NSTableView,
|
||||
rowViewForRow row: Int
|
||||
rowViewForRow _: Int
|
||||
) -> NSTableRowView? {
|
||||
return tableView.makeView(
|
||||
tableView.makeView(
|
||||
withIdentifier: NSUserInterfaceItemIdentifier("buffer-row-view"),
|
||||
owner: self
|
||||
) as? ThemedTableRow ?? ThemedTableRow(withIdentifier: "buffer-row-view",
|
||||
themedView: self)
|
||||
) as? ThemedTableRow ?? ThemedTableRow(
|
||||
withIdentifier: "buffer-row-view",
|
||||
themedView: self
|
||||
)
|
||||
}
|
||||
|
||||
func tableView(
|
||||
_ tableView: NSTableView,
|
||||
viewFor tableColumn: NSTableColumn?,
|
||||
viewFor _: NSTableColumn?,
|
||||
row: Int
|
||||
) -> NSView? {
|
||||
let cachedCell = (tableView.makeView(
|
||||
@ -169,13 +174,13 @@ extension BuffersList {
|
||||
}
|
||||
|
||||
func tableView(
|
||||
_ tableView: NSTableView,
|
||||
_: NSTableView,
|
||||
didAdd rowView: NSTableRowView,
|
||||
forRow row: Int
|
||||
forRow _: Int
|
||||
) {
|
||||
guard let cellWidth = (rowView.view(atColumn: 0) as? NSTableCellView)?
|
||||
.fittingSize.width
|
||||
else {
|
||||
else {
|
||||
return
|
||||
}
|
||||
|
||||
@ -194,15 +199,17 @@ extension BuffersList {
|
||||
}
|
||||
|
||||
let pathInfo = url.pathComponents
|
||||
.dropFirst()
|
||||
.dropLast()
|
||||
.reversed()
|
||||
.joined(separator: " / ") + " /"
|
||||
.dropFirst()
|
||||
.dropLast()
|
||||
.reversed()
|
||||
.joined(separator: " / ") + " /"
|
||||
let rowText = NSMutableAttributedString(string: "\(name) — \(pathInfo)")
|
||||
|
||||
rowText.addAttribute(NSAttributedString.Key.foregroundColor,
|
||||
value: self.theme.foreground,
|
||||
range: NSRange(location: 0, length: name.count))
|
||||
rowText.addAttribute(
|
||||
NSAttributedString.Key.foregroundColor,
|
||||
value: self.theme.foreground,
|
||||
range: NSRange(location: 0, length: name.count)
|
||||
)
|
||||
|
||||
rowText.addAttribute(
|
||||
NSAttributedString.Key.foregroundColor,
|
||||
|
@ -6,7 +6,6 @@
|
||||
import Foundation
|
||||
|
||||
class BuffersListReducer: ReducerType {
|
||||
|
||||
typealias StateType = MainWindow.State
|
||||
typealias ActionType = UuidAction<BuffersList.Action>
|
||||
|
||||
@ -14,10 +13,8 @@ class BuffersListReducer: ReducerType {
|
||||
var state = tuple.state
|
||||
|
||||
switch tuple.action.payload {
|
||||
|
||||
case let .open(buffer):
|
||||
state.currentBufferToSet = buffer
|
||||
|
||||
}
|
||||
|
||||
return (state, tuple.action, true)
|
||||
|
@ -8,13 +8,11 @@ import RxSwift
|
||||
|
||||
typealias AnyAction = Any
|
||||
extension ReduxTypes {
|
||||
|
||||
typealias StateType = AppState
|
||||
typealias ActionType = AnyAction
|
||||
}
|
||||
|
||||
class Context: ReduxContext {
|
||||
|
||||
// The following should only be used when Cmd-Q'ing
|
||||
func savePrefs() { self.prefMiddleware.applyPref(from: self.state) }
|
||||
|
||||
@ -24,7 +22,7 @@ class Context: ReduxContext {
|
||||
let markdownPreviewMiddleware = MarkdownPreviewMiddleware()
|
||||
let markdownPreviewReducer = MarkdownPreviewReducer(baseServerUrl: baseServerUrl)
|
||||
let htmlPreviewReducer = HtmlPreviewReducer(baseServerUrl: baseServerUrl)
|
||||
let httpMiddleware: HttpServerMiddleware = HttpServerMiddleware(port: baseServerUrl.port!)
|
||||
let httpMiddleware = HttpServerMiddleware(port: baseServerUrl.port!)
|
||||
let uiRootReducer = UiRootReducer()
|
||||
let openQuicklyReducer = OpenQuicklyReducer()
|
||||
let rpcEpic = RpcAppearanceEpic(emitter: self.actionEmitter)
|
||||
@ -52,7 +50,8 @@ class Context: ReduxContext {
|
||||
self.prefMiddleware.mainWindow.apply,
|
||||
self.prefMiddleware.apply,
|
||||
rpcEpic.apply,
|
||||
])
|
||||
]
|
||||
)
|
||||
.filter { $0.modified }
|
||||
.subscribe(onNext: self.emitAppState)
|
||||
.disposed(by: self.disposeBag)
|
||||
|
@ -3,15 +3,13 @@
|
||||
* See LICENSE
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
import os
|
||||
import Commons
|
||||
import CoreData
|
||||
import Foundation
|
||||
import os
|
||||
|
||||
class CoreDataStack {
|
||||
|
||||
enum Error: Swift.Error {
|
||||
|
||||
case noCacheFolder
|
||||
case pathDoesNotExit
|
||||
case pathNotFolder
|
||||
@ -20,7 +18,6 @@ class CoreDataStack {
|
||||
}
|
||||
|
||||
enum StoreLocation {
|
||||
|
||||
case temp(String)
|
||||
case cache(String)
|
||||
case path(String)
|
||||
@ -45,15 +42,14 @@ class CoreDataStack {
|
||||
let fileManager = FileManager.default
|
||||
let url: URL
|
||||
switch storeLocation {
|
||||
|
||||
case .temp(let folderName):
|
||||
case let .temp(folderName):
|
||||
let parentUrl = fileManager
|
||||
.temporaryDirectory
|
||||
.appendingPathComponent(folderName)
|
||||
try fileManager.createDirectory(at: parentUrl, withIntermediateDirectories: true)
|
||||
url = parentUrl.appendingPathComponent(modelName)
|
||||
|
||||
case .cache(let folderName):
|
||||
case let .cache(folderName):
|
||||
guard let cacheUrl = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first else {
|
||||
throw Error.noCacheFolder
|
||||
}
|
||||
@ -62,14 +58,13 @@ class CoreDataStack {
|
||||
|
||||
url = parentUrl.appendingPathComponent(modelName)
|
||||
|
||||
case .path(let path):
|
||||
case let .path(path):
|
||||
guard fileManager.fileExists(atPath: path) else { throw Error.pathDoesNotExit }
|
||||
|
||||
let parentFolder = URL(fileURLWithPath: path)
|
||||
guard parentFolder.isDir else { throw Error.pathNotFolder }
|
||||
|
||||
url = parentFolder.appendingPathComponent(modelName)
|
||||
|
||||
}
|
||||
|
||||
self.container.persistentStoreDescriptions = [NSPersistentStoreDescription(url: url)]
|
||||
|
@ -6,7 +6,6 @@
|
||||
import Cocoa
|
||||
|
||||
class CssUtils {
|
||||
|
||||
static let cssOverridesTemplate: String = try! String(
|
||||
contentsOf: Resources.cssOverridesTemplateUrl
|
||||
)
|
||||
@ -15,22 +14,36 @@ class CssUtils {
|
||||
self
|
||||
.cssOverridesTemplate
|
||||
.replacingOccurrences(of: "{{ nvim-color }}", with: self.htmlColor(theme.cssColor))
|
||||
.replacingOccurrences(of: "{{ nvim-background-color }}",
|
||||
with: self.htmlColor(theme.cssBackgroundColor))
|
||||
.replacingOccurrences(
|
||||
of: "{{ nvim-background-color }}",
|
||||
with: self.htmlColor(theme.cssBackgroundColor)
|
||||
)
|
||||
.replacingOccurrences(of: "{{ nvim-a }}", with: self.htmlColor(theme.cssA))
|
||||
.replacingOccurrences(of: "{{ nvim-hr-background-color }}",
|
||||
with: self.htmlColor(theme.cssHrBorderBackgroundColor))
|
||||
.replacingOccurrences(of: "{{ nvim-hr-border-bottom-color }}",
|
||||
with: self.htmlColor(theme.cssHrBorderBottomColor))
|
||||
.replacingOccurrences(of: "{{ nvim-blockquote-border-left-color }}",
|
||||
with: self.htmlColor(theme.cssBlockquoteBorderLeftColor))
|
||||
.replacingOccurrences(of: "{{ nvim-blockquote-color }}",
|
||||
with: self.htmlColor(theme.cssBlockquoteColor))
|
||||
.replacingOccurrences(of: "{{ nvim-h2-border-bottom-color }}",
|
||||
with: self.htmlColor(theme.cssH2BorderBottomColor))
|
||||
.replacingOccurrences(
|
||||
of: "{{ nvim-hr-background-color }}",
|
||||
with: self.htmlColor(theme.cssHrBorderBackgroundColor)
|
||||
)
|
||||
.replacingOccurrences(
|
||||
of: "{{ nvim-hr-border-bottom-color }}",
|
||||
with: self.htmlColor(theme.cssHrBorderBottomColor)
|
||||
)
|
||||
.replacingOccurrences(
|
||||
of: "{{ nvim-blockquote-border-left-color }}",
|
||||
with: self.htmlColor(theme.cssBlockquoteBorderLeftColor)
|
||||
)
|
||||
.replacingOccurrences(
|
||||
of: "{{ nvim-blockquote-color }}",
|
||||
with: self.htmlColor(theme.cssBlockquoteColor)
|
||||
)
|
||||
.replacingOccurrences(
|
||||
of: "{{ nvim-h2-border-bottom-color }}",
|
||||
with: self.htmlColor(theme.cssH2BorderBottomColor)
|
||||
)
|
||||
.replacingOccurrences(of: "{{ nvim-h6-color }}", with: self.htmlColor(theme.cssH6Color))
|
||||
.replacingOccurrences(of: "{{ nvim-code-background-color }}",
|
||||
with: self.htmlColor(theme.cssCodeBackgroundColor))
|
||||
.replacingOccurrences(
|
||||
of: "{{ nvim-code-background-color }}",
|
||||
with: self.htmlColor(theme.cssCodeBackgroundColor)
|
||||
)
|
||||
.replacingOccurrences(of: "{{ nvim-code-color }}", with: self.htmlColor(theme.cssCodeColor))
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,6 @@ import Foundation
|
||||
import RxSwift
|
||||
|
||||
class Debouncer<T> {
|
||||
|
||||
let observable: Observable<T>
|
||||
|
||||
init(interval: RxTimeInterval) {
|
||||
|
@ -7,11 +7,9 @@ import Foundation
|
||||
import WebKit
|
||||
|
||||
struct Defs {
|
||||
|
||||
static let loggerSubsystem = Bundle.main.bundleIdentifier!
|
||||
|
||||
struct LoggerCategory {
|
||||
|
||||
enum LoggerCategory {
|
||||
static let general = "general"
|
||||
|
||||
static let ui = "ui"
|
||||
|
@ -4,19 +4,18 @@
|
||||
*/
|
||||
|
||||
import Cocoa
|
||||
import RxSwift
|
||||
import PureLayout
|
||||
import MaterialIcons
|
||||
import Commons
|
||||
import MaterialIcons
|
||||
import PureLayout
|
||||
import RxSwift
|
||||
import Workspace
|
||||
|
||||
class FileBrowser: NSView,
|
||||
UiComponent {
|
||||
|
||||
UiComponent
|
||||
{
|
||||
typealias StateType = MainWindow.State
|
||||
|
||||
enum Action {
|
||||
|
||||
case open(url: URL, mode: MainWindow.OpenMode)
|
||||
case setAsWorkingDirectory(URL)
|
||||
case setShowHidden(Bool)
|
||||
@ -41,8 +40,8 @@ class FileBrowser: NSView,
|
||||
action: #selector(FileBrowser.showHiddenAction),
|
||||
keyEquivalent: ""
|
||||
)
|
||||
showHiddenMenuItem.boolState = state.fileBrowserShowHidden
|
||||
self.menuItems = [showHiddenMenuItem]
|
||||
self.showHiddenMenuItem.boolState = state.fileBrowserShowHidden
|
||||
self.menuItems = [self.showHiddenMenuItem]
|
||||
|
||||
super.init(frame: .zero)
|
||||
|
||||
@ -78,7 +77,8 @@ class FileBrowser: NSView,
|
||||
|
||||
private var cwd: URL
|
||||
|
||||
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||||
@available(*, unavailable)
|
||||
required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||||
|
||||
private func addViews() {
|
||||
let scrollView = NSScrollView.standardScrollView()
|
||||
@ -91,9 +91,7 @@ class FileBrowser: NSView,
|
||||
}
|
||||
|
||||
extension FileBrowser {
|
||||
|
||||
class InnerCustomToolbar: CustomToolBar {
|
||||
|
||||
let goToParentButton = NSButton(forAutoLayout: ())
|
||||
let scrollToSourceButton = NSButton(forAutoLayout: ())
|
||||
let refreshButton = NSButton(forAutoLayout: ())
|
||||
@ -174,30 +172,31 @@ extension FileBrowser {
|
||||
)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||||
@available(*, unavailable)
|
||||
required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
extension FileBrowser {
|
||||
|
||||
extension FileBrowser {
|
||||
@objc func showHiddenAction(_ sender: Any?) {
|
||||
guard let menuItem = sender as? NSMenuItem else { return }
|
||||
|
||||
self.emit(UuidAction(uuid: self.uuid, action: .setShowHidden(!menuItem.boolState)))
|
||||
}
|
||||
|
||||
@objc func goToParentAction(_ sender: Any?) {
|
||||
@objc func goToParentAction(_: Any?) {
|
||||
self.emit(UuidAction(uuid: self.uuid, action: .setAsWorkingDirectory(self.cwd.parent)))
|
||||
}
|
||||
|
||||
@objc func scrollToSourceAction(_ sender: Any?) {
|
||||
@objc func scrollToSourceAction(_: Any?) {
|
||||
guard let url = self.currentBufferUrl else { return }
|
||||
|
||||
self.fileView.select(url)
|
||||
}
|
||||
|
||||
@objc func refreshAction(_ sender: Any?) {
|
||||
@objc func refreshAction(_: Any?) {
|
||||
self.emit(UuidAction(uuid: self.uuid, action: .refresh))
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,6 @@
|
||||
import Foundation
|
||||
|
||||
class FileBrowserReducer: ReducerType {
|
||||
|
||||
typealias StateType = MainWindow.State
|
||||
typealias ActionType = UuidAction<FileBrowser.Action>
|
||||
|
||||
@ -14,7 +13,6 @@ class FileBrowserReducer: ReducerType {
|
||||
var state = tuple.state
|
||||
|
||||
switch tuple.action.payload {
|
||||
|
||||
case let .open(url, mode):
|
||||
state.urlsToOpen[url] = mode
|
||||
state.viewToBeFocused = .neoVimView
|
||||
@ -27,7 +25,6 @@ class FileBrowserReducer: ReducerType {
|
||||
|
||||
case .refresh:
|
||||
state.lastFileSystemUpdate = Marked(state.cwd)
|
||||
|
||||
}
|
||||
|
||||
return (state, tuple.action, true)
|
||||
|
@ -3,22 +3,21 @@
|
||||
* See LICENSE
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import EonilFSEvents
|
||||
import os
|
||||
import Commons
|
||||
import EonilFSEvents
|
||||
import Foundation
|
||||
import os
|
||||
|
||||
class FileMonitor {
|
||||
|
||||
static let fileSystemEventsLatency = 1.0
|
||||
|
||||
private(set) var urlToMonitor = FileUtils.userHomeUrl
|
||||
|
||||
func monitor(url: URL, eventHandler: (@escaping (URL) -> Void)) throws {
|
||||
func monitor(url: URL, eventHandler: @escaping (URL) -> Void) throws {
|
||||
self.stopMonitor()
|
||||
self.urlToMonitor = url
|
||||
self.monitor = try EonilFSEventStream(
|
||||
pathsToWatch: [urlToMonitor.path],
|
||||
pathsToWatch: [self.urlToMonitor.path],
|
||||
sinceWhen: EonilFSEventsEventID.getCurrentEventId(),
|
||||
latency: FileMonitor.fileSystemEventsLatency,
|
||||
flags: [],
|
||||
|
@ -4,18 +4,18 @@
|
||||
*/
|
||||
|
||||
import Cocoa
|
||||
import Commons
|
||||
import MaterialIcons
|
||||
import NvimView
|
||||
import os
|
||||
import PureLayout
|
||||
import RxSwift
|
||||
import MaterialIcons
|
||||
import os
|
||||
import Commons
|
||||
|
||||
class FileOutlineView: NSOutlineView,
|
||||
UiComponent,
|
||||
NSOutlineViewDelegate,
|
||||
ThemedView {
|
||||
|
||||
UiComponent,
|
||||
NSOutlineViewDelegate,
|
||||
ThemedView
|
||||
{
|
||||
typealias StateType = MainWindow.State
|
||||
|
||||
@objc dynamic var content = [Node]()
|
||||
@ -150,7 +150,8 @@ class FileOutlineView: NSOutlineView,
|
||||
self.unbind(.selectionIndexPaths)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||||
@available(*, unavailable)
|
||||
required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||||
|
||||
private func handleFileSystemChanges(_ changedUrl: URL) {
|
||||
DispatchQueue.main.async {
|
||||
@ -206,7 +207,7 @@ class FileOutlineView: NSOutlineView,
|
||||
self.treeController.preservesSelection = true
|
||||
self.treeController.sortDescriptors = [
|
||||
NSSortDescriptor(key: "isLeaf", ascending: true), // Folders first,
|
||||
NSSortDescriptor(key: "displayName", ascending: true) // then, name
|
||||
NSSortDescriptor(key: "displayName", ascending: true), // then, name
|
||||
]
|
||||
|
||||
// The following will create a retain cycle. The superview *must* unbind
|
||||
@ -217,7 +218,7 @@ class FileOutlineView: NSOutlineView,
|
||||
self.bind(.selectionIndexPaths, to: self.treeController, withKeyPath: "selectionIndexPaths")
|
||||
}
|
||||
|
||||
private func changedTreeNode(`for` url: URL) -> NSTreeNode? {
|
||||
private func changedTreeNode(for url: URL) -> NSTreeNode? {
|
||||
if url == self.cwd { return self.treeController.arrangedObjects }
|
||||
|
||||
let cwdCompsCount = self.cwd.pathComponents.count
|
||||
@ -226,12 +227,12 @@ class FileOutlineView: NSOutlineView,
|
||||
|
||||
let rootTreeNode = self.treeController.arrangedObjects
|
||||
let changedTreeNode = comps.reduce(rootTreeNode) { prev, comp in
|
||||
prev.children?.first { child in return child.node?.displayName == comp } ?? prev
|
||||
prev.children?.first { child in child.node?.displayName == comp } ?? prev
|
||||
}
|
||||
|
||||
guard let changeNode = changedTreeNode.node else { return nil }
|
||||
|
||||
guard changeNode.url == url && changeNode.children != nil else { return nil }
|
||||
guard changeNode.url == url, changeNode.children != nil else { return nil }
|
||||
|
||||
return changedTreeNode
|
||||
}
|
||||
@ -256,12 +257,12 @@ class FileOutlineView: NSOutlineView,
|
||||
guard self.treeController.isEditable else { return }
|
||||
|
||||
let indexPathsToRemove = changeTreeNode
|
||||
.children?
|
||||
.filter { child in
|
||||
guard let url = child.node?.url else { return true }
|
||||
return newChildUrls.contains(url) == false
|
||||
}
|
||||
.map { $0.indexPath } ?? []
|
||||
.children?
|
||||
.filter { child in
|
||||
guard let url = child.node?.url else { return true }
|
||||
return newChildUrls.contains(url) == false
|
||||
}
|
||||
.map(\.indexPath) ?? []
|
||||
|
||||
changeTreeNode
|
||||
.children?
|
||||
@ -276,7 +277,7 @@ class FileOutlineView: NSOutlineView,
|
||||
|
||||
private func childUrls(for url: URL) -> [URL] {
|
||||
let urls = FileUtils.directDescendants(of: url).sorted { lhs, rhs in
|
||||
return lhs.lastPathComponent < rhs.lastPathComponent
|
||||
lhs.lastPathComponent < rhs.lastPathComponent
|
||||
}
|
||||
|
||||
if self.isShowHidden { return urls }
|
||||
@ -345,8 +346,8 @@ class FileOutlineView: NSOutlineView,
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
extension FileOutlineView {
|
||||
|
||||
extension FileOutlineView {
|
||||
@IBAction func doubleClickAction(_: Any?) {
|
||||
let clickedTreeNode = self.clickedItem
|
||||
guard let node = self.node(from: clickedTreeNode) else { return }
|
||||
@ -392,9 +393,9 @@ extension FileOutlineView {
|
||||
}
|
||||
|
||||
// MARK: - NSOutlineViewDelegate
|
||||
extension FileOutlineView {
|
||||
|
||||
func outlineView(_ outlineView: NSOutlineView, rowViewForItem item: Any) -> NSTableRowView? {
|
||||
extension FileOutlineView {
|
||||
func outlineView(_: NSOutlineView, rowViewForItem _: Any) -> NSTableRowView? {
|
||||
let view = self.makeView(
|
||||
withIdentifier: NSUserInterfaceItemIdentifier("file-row-view"),
|
||||
owner: self
|
||||
@ -403,7 +404,7 @@ extension FileOutlineView {
|
||||
return view
|
||||
}
|
||||
|
||||
func outlineView(_: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {
|
||||
func outlineView(_: NSOutlineView, viewFor _: NSTableColumn?, item: Any) -> NSView? {
|
||||
guard let node = self.node(from: item) else { return nil }
|
||||
|
||||
let cellView = self.makeView(
|
||||
@ -422,9 +423,9 @@ extension FileOutlineView {
|
||||
return cellView
|
||||
}
|
||||
|
||||
func outlineView(_: NSOutlineView, heightOfRowByItem item: Any) -> CGFloat { 20 }
|
||||
func outlineView(_: NSOutlineView, heightOfRowByItem _: Any) -> CGFloat { 20 }
|
||||
|
||||
func outlineView(_ outlineView: NSOutlineView, shouldExpandItem item: Any) -> Bool {
|
||||
func outlineView(_: NSOutlineView, shouldExpandItem item: Any) -> Bool {
|
||||
guard let node = self.node(from: item) else { return false }
|
||||
|
||||
if node.isChildrenScanned { return true }
|
||||
@ -434,7 +435,7 @@ extension FileOutlineView {
|
||||
return true
|
||||
}
|
||||
|
||||
func outlineView(_ outlineView: NSOutlineView, didAdd rowView: NSTableRowView, forRow row: Int) {
|
||||
func outlineView(_: NSOutlineView, didAdd rowView: NSTableRowView, forRow row: Int) {
|
||||
guard let cellWidth = (rowView.view(atColumn: 0) as? NSTableCellView)?.fittingSize.width else {
|
||||
return
|
||||
}
|
||||
@ -442,7 +443,7 @@ extension FileOutlineView {
|
||||
let level = self.level(forRow: row).cgf
|
||||
let width = level * self.indentationPerLevel + cellWidth + columnWidthRightPadding
|
||||
self.cachedColumnWidth = max(self.cachedColumnWidth, width)
|
||||
self.tableColumns[0].width = cachedColumnWidth
|
||||
self.tableColumns[0].width = self.cachedColumnWidth
|
||||
|
||||
let rv = rowView as? ThemedTableRow
|
||||
guard rv?.themeToken != self.lastThemeMark else { return }
|
||||
@ -455,20 +456,20 @@ extension FileOutlineView {
|
||||
}
|
||||
|
||||
// MARK: - NSUserInterfaceValidations
|
||||
extension FileOutlineView {
|
||||
|
||||
extension FileOutlineView {
|
||||
override func validateUserInterfaceItem(_ item: NSValidatedUserInterfaceItem) -> Bool {
|
||||
guard let clickedNode = self.node(from: self.clickedItem) else { return true }
|
||||
|
||||
if item.action == #selector(setAsWorkingDirectory(_:)) { return clickedNode.url.isDir }
|
||||
if item.action == #selector(self.setAsWorkingDirectory(_:)) { return clickedNode.url.isDir }
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - NSView
|
||||
extension FileOutlineView {
|
||||
|
||||
extension FileOutlineView {
|
||||
override func keyDown(with event: NSEvent) {
|
||||
guard let char = event.charactersIgnoringModifiers?.first else {
|
||||
super.keyDown(with: event)
|
||||
@ -495,8 +496,7 @@ extension FileOutlineView {
|
||||
}
|
||||
|
||||
class Node: NSObject, Comparable {
|
||||
|
||||
static func <(lhs: Node, rhs: Node) -> Bool { lhs.displayName < rhs.displayName }
|
||||
static func < (lhs: Node, rhs: Node) -> Bool { lhs.displayName < rhs.displayName }
|
||||
|
||||
@objc dynamic var url: URL
|
||||
@objc dynamic var isLeaf: Bool
|
||||
@ -521,7 +521,6 @@ class Node: NSObject, Comparable {
|
||||
}
|
||||
|
||||
private extension NSTreeNode {
|
||||
|
||||
var node: Node? { self.representedObject as? Node }
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,5 @@ import Foundation
|
||||
import os
|
||||
|
||||
extension URL {
|
||||
|
||||
var direntType: UInt8 { (self as NSURL).direntType() }
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ import Foundation
|
||||
import os
|
||||
|
||||
class FuzzyMatcherPool {
|
||||
|
||||
let pattern: String
|
||||
|
||||
init(pattern: String, initialPoolSize: Int = 2) {
|
||||
@ -17,7 +16,7 @@ class FuzzyMatcherPool {
|
||||
}
|
||||
|
||||
func request() -> FuzzyMatcher {
|
||||
return self.lock.withLock {
|
||||
self.lock.withLock {
|
||||
if self.matchers.isEmpty {
|
||||
let matcher = FuzzyMatcher(pattern: self.pattern)
|
||||
return matcher
|
||||
@ -45,7 +44,6 @@ class FuzzyMatcherPool {
|
||||
}
|
||||
|
||||
private extension NSLocking {
|
||||
|
||||
func withLock<T>(_ body: () -> T) -> T {
|
||||
self.lock()
|
||||
defer { self.unlock() }
|
||||
|
@ -3,13 +3,12 @@
|
||||
* See LICENSE
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
import os
|
||||
import Commons
|
||||
import CoreData
|
||||
import Foundation
|
||||
import os
|
||||
|
||||
class FuzzySearchService {
|
||||
|
||||
typealias ScoredUrlsCallback = ([ScoredUrl]) -> Void
|
||||
|
||||
var root: URL {
|
||||
@ -101,7 +100,7 @@ class FuzzySearchService {
|
||||
fetchReq.predicate = predicate
|
||||
|
||||
let chunkCount = Int(ceil(Double(count) / Double(coreDataBatchSize)))
|
||||
for chunkIndex in (0..<chunkCount) {
|
||||
for chunkIndex in 0..<chunkCount {
|
||||
if self.shouldStop({ context.reset() }) { return }
|
||||
|
||||
let start = Swift.min(chunkIndex * coreDataBatchSize, count)
|
||||
@ -123,7 +122,7 @@ class FuzzySearchService {
|
||||
private func scanScoreFilesNeedScanning(
|
||||
matcherPool: FuzzyMatcherPool,
|
||||
context: NSManagedObjectContext,
|
||||
callback: ([ScoredUrl]) -> ()
|
||||
callback: ([ScoredUrl]) -> Void
|
||||
) {
|
||||
let req = self.fileFetchRequest("needsScanChildren == TRUE AND direntType == %d", [DT_DIR])
|
||||
do {
|
||||
@ -147,7 +146,7 @@ class FuzzySearchService {
|
||||
matcherPool: FuzzyMatcherPool,
|
||||
folderId: NSManagedObjectID,
|
||||
context: NSManagedObjectContext,
|
||||
callback: ([ScoredUrl]) -> ()
|
||||
callback: ([ScoredUrl]) -> Void
|
||||
) {
|
||||
var saveCounter = 1
|
||||
var counter = 1
|
||||
@ -180,7 +179,9 @@ class FuzzySearchService {
|
||||
|
||||
let childFiles = childUrls
|
||||
.filter { !$0.isPackage }
|
||||
.map { url -> FileItem in self.file(fromUrl: url, pathStart: baton.pathStart, in: context) }
|
||||
.map { url -> FileItem in
|
||||
self.file(fromUrl: url, pathStart: baton.pathStart, in: context)
|
||||
}
|
||||
saveCounter += childFiles.count
|
||||
counter += childFiles.count
|
||||
|
||||
@ -209,9 +210,9 @@ class FuzzySearchService {
|
||||
// We have to re-fetch the Files in stack to get the parent-children relationship right.
|
||||
// Since objectID survives NSManagedObjectContext.reset(), we can re-populate (re-fetch)
|
||||
// stack using the objectIDs.
|
||||
let ids = stack.map { $0.1.objectID }
|
||||
let ids = stack.map(\.1.objectID)
|
||||
stack = Array(zip(
|
||||
stack.map { $0.0 },
|
||||
stack.map(\.0),
|
||||
ids.map { context.object(with: $0) as! FileItem }
|
||||
))
|
||||
}
|
||||
@ -249,7 +250,7 @@ class FuzzySearchService {
|
||||
private func scoreAllRegisteredFiles(
|
||||
matcherPool: FuzzyMatcherPool,
|
||||
context: NSManagedObjectContext,
|
||||
callback: ([ScoredUrl]) -> ()
|
||||
callback: ([ScoredUrl]) -> Void
|
||||
) {
|
||||
let files = context.registeredObjects
|
||||
.compactMap { $0 as? FileItem }
|
||||
@ -338,7 +339,7 @@ class FuzzySearchService {
|
||||
} catch {
|
||||
self.log.error(
|
||||
"Could not fetch File with url \(folderUrl) "
|
||||
+ "or could not save after setting needsScanChildren: \(error)"
|
||||
+ "or could not save after setting needsScanChildren: \(error)"
|
||||
)
|
||||
}
|
||||
|
||||
@ -417,7 +418,7 @@ class FuzzySearchService {
|
||||
}
|
||||
|
||||
func debug() {
|
||||
let req = self.fileFetchRequest("needsScanChildren == TRUE AND direntType == %d", [DT_DIR]);
|
||||
let req = self.fileFetchRequest("needsScanChildren == TRUE AND direntType == %d", [DT_DIR])
|
||||
|
||||
self.queue.async {
|
||||
let moc = self.writeContext
|
||||
|
@ -8,11 +8,9 @@ import PureLayout
|
||||
import RxSwift
|
||||
|
||||
class GeneralPref: PrefPane, UiComponent, NSTextFieldDelegate {
|
||||
|
||||
typealias StateType = AppState
|
||||
|
||||
enum Action {
|
||||
|
||||
case setOpenOnLaunch(Bool)
|
||||
case setAfterLastWindowAction(AppState.AfterLastWindowAction)
|
||||
case setOpenOnReactivation(Bool)
|
||||
@ -20,11 +18,11 @@ class GeneralPref: PrefPane, UiComponent, NSTextFieldDelegate {
|
||||
}
|
||||
|
||||
override var displayName: String {
|
||||
return "General"
|
||||
"General"
|
||||
}
|
||||
|
||||
override var pinToContainer: Bool {
|
||||
return true
|
||||
true
|
||||
}
|
||||
|
||||
required init(source: Observable<StateType>, emitter: ActionEmitter, state: StateType) {
|
||||
@ -39,7 +37,8 @@ class GeneralPref: PrefPane, UiComponent, NSTextFieldDelegate {
|
||||
self.defaultUsesVcsIgnoresCheckbox.boolState = state.openQuickly.defaultUsesVcsIgnores
|
||||
|
||||
self.lastWindowAction = state.afterLastWindowAction
|
||||
self.afterLastWindowPopup.selectItem(at: indexToAfterLastWindowAction.firstIndex(of: state.afterLastWindowAction) ?? 0)
|
||||
self.afterLastWindowPopup
|
||||
.selectItem(at: indexToAfterLastWindowAction.firstIndex(of: state.afterLastWindowAction) ?? 0)
|
||||
|
||||
source
|
||||
.observeOn(MainScheduler.instance)
|
||||
@ -73,7 +72,8 @@ class GeneralPref: PrefPane, UiComponent, NSTextFieldDelegate {
|
||||
|
||||
private let afterLastWindowPopup = NSPopUpButton(forAutoLayout: ())
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
@available(*, unavailable)
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
@ -81,15 +81,21 @@ class GeneralPref: PrefPane, UiComponent, NSTextFieldDelegate {
|
||||
let paneTitle = self.paneTitleTextField(title: "General")
|
||||
|
||||
let openUntitledWindowTitle = self.titleTextField(title: "Open Untitled Window:")
|
||||
self.configureCheckbox(button: self.openWhenLaunchingCheckbox,
|
||||
title: "On launch",
|
||||
action: #selector(GeneralPref.openUntitledWindowWhenLaunchingAction))
|
||||
self.configureCheckbox(button: self.openOnReactivationCheckbox,
|
||||
title: "On re-activation",
|
||||
action: #selector(GeneralPref.openUntitledWindowOnReactivationAction))
|
||||
self.configureCheckbox(button: self.defaultUsesVcsIgnoresCheckbox,
|
||||
title: "Use VCS Ignores",
|
||||
action: #selector(GeneralPref.defaultUsesVcsIgnoresAction))
|
||||
self.configureCheckbox(
|
||||
button: self.openWhenLaunchingCheckbox,
|
||||
title: "On launch",
|
||||
action: #selector(GeneralPref.openUntitledWindowWhenLaunchingAction)
|
||||
)
|
||||
self.configureCheckbox(
|
||||
button: self.openOnReactivationCheckbox,
|
||||
title: "On re-activation",
|
||||
action: #selector(GeneralPref.openUntitledWindowOnReactivationAction)
|
||||
)
|
||||
self.configureCheckbox(
|
||||
button: self.defaultUsesVcsIgnoresCheckbox,
|
||||
title: "Use VCS Ignores",
|
||||
action: #selector(GeneralPref.defaultUsesVcsIgnoresAction)
|
||||
)
|
||||
|
||||
let whenLaunching = self.openWhenLaunchingCheckbox
|
||||
let onReactivation = self.openOnReactivationCheckbox
|
||||
@ -107,12 +113,12 @@ class GeneralPref: PrefPane, UiComponent, NSTextFieldDelegate {
|
||||
let ignoreListTitle = self.titleTextField(title: "Open Quickly:")
|
||||
let ignoreInfo =
|
||||
self.infoTextField(markdown: #"""
|
||||
When checked, the ignore files of VCSs, e.g. `gitignore`, will we used to ignore files.\
|
||||
This checkbox will set the initial value for each VimR window.\
|
||||
You can change this setting for each VimR window in the Open Quickly window.\
|
||||
The behavior should be almost identical to that of
|
||||
[The Silver Searcher](https://github.com/ggreer/the_silver_searcher).
|
||||
"""#)
|
||||
When checked, the ignore files of VCSs, e.g. `gitignore`, will we used to ignore files.\
|
||||
This checkbox will set the initial value for each VimR window.\
|
||||
You can change this setting for each VimR window in the Open Quickly window.\
|
||||
The behavior should be almost identical to that of
|
||||
[The Silver Searcher](https://github.com/ggreer/the_silver_searcher).
|
||||
"""#)
|
||||
|
||||
let cliToolTitle = self.titleTextField(title: "CLI Tool:")
|
||||
let cliToolButton = NSButton(forAutoLayout: ())
|
||||
@ -150,7 +156,11 @@ The behavior should be almost identical to that of
|
||||
|
||||
openUntitledWindowTitle.autoAlignAxis(.baseline, toSameAxisOf: whenLaunching, withOffset: 0)
|
||||
openUntitledWindowTitle.autoPinEdge(.right, to: .right, of: afterLastWindowTitle)
|
||||
openUntitledWindowTitle.autoPinEdge(toSuperviewEdge: .left, withInset: 18, relation: .greaterThanOrEqual)
|
||||
openUntitledWindowTitle.autoPinEdge(
|
||||
toSuperviewEdge: .left,
|
||||
withInset: 18,
|
||||
relation: .greaterThanOrEqual
|
||||
)
|
||||
|
||||
whenLaunching.autoPinEdge(.top, to: .bottom, of: paneTitle, withOffset: 18)
|
||||
whenLaunching.autoPinEdge(.left, to: .right, of: openUntitledWindowTitle, withOffset: 5)
|
||||
@ -158,7 +168,11 @@ The behavior should be almost identical to that of
|
||||
|
||||
onReactivation.autoPinEdge(.top, to: .bottom, of: whenLaunching, withOffset: 5)
|
||||
onReactivation.autoPinEdge(.left, to: .left, of: whenLaunching)
|
||||
onReactivation.autoPinEdge(toSuperviewEdge: .right, withInset: 18, relation: .greaterThanOrEqual)
|
||||
onReactivation.autoPinEdge(
|
||||
toSuperviewEdge: .right,
|
||||
withInset: 18,
|
||||
relation: .greaterThanOrEqual
|
||||
)
|
||||
|
||||
afterLastWindowTitle.autoAlignAxis(.baseline, toSameAxisOf: lastWindow)
|
||||
afterLastWindowTitle.autoPinEdge(toSuperviewEdge: .left, withInset: 18)
|
||||
@ -168,7 +182,11 @@ The behavior should be almost identical to that of
|
||||
|
||||
ignoreListTitle.autoAlignAxis(.baseline, toSameAxisOf: vcsIg)
|
||||
ignoreListTitle.autoPinEdge(.right, to: .right, of: openUntitledWindowTitle)
|
||||
ignoreListTitle.autoPinEdge(toSuperviewEdge: .left, withInset: 18, relation: .greaterThanOrEqual)
|
||||
ignoreListTitle.autoPinEdge(
|
||||
toSuperviewEdge: .left,
|
||||
withInset: 18,
|
||||
relation: .greaterThanOrEqual
|
||||
)
|
||||
|
||||
vcsIg.autoPinEdge(.top, to: .bottom, of: lastWindow, withOffset: 18)
|
||||
vcsIg.autoPinEdge(.left, to: .right, of: ignoreListTitle, withOffset: 5)
|
||||
@ -192,9 +210,9 @@ The behavior should be almost identical to that of
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
extension GeneralPref {
|
||||
|
||||
@objc func copyCliTool(_ sender: NSButton) {
|
||||
extension GeneralPref {
|
||||
@objc func copyCliTool(_: NSButton) {
|
||||
let panel = NSOpenPanel()
|
||||
panel.canChooseFiles = false
|
||||
panel.canChooseDirectories = true
|
||||
@ -205,14 +223,18 @@ extension GeneralPref {
|
||||
}
|
||||
|
||||
guard let vimrUrl = Bundle.main.url(forResource: "vimr", withExtension: nil) else {
|
||||
self.alert(title: "Something Went Wrong.",
|
||||
info: "The CLI tool 'vimr' could not be found. Please re-download VimR and try again.")
|
||||
self.alert(
|
||||
title: "Something Went Wrong.",
|
||||
info: "The CLI tool 'vimr' could not be found. Please re-download VimR and try again."
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
guard let targetUrl = panel.url?.appendingPathComponent("vimr") else {
|
||||
self.alert(title: "Something Went Wrong.",
|
||||
info: "The target directory could not be determined. Please try again with a different directory.")
|
||||
self.alert(
|
||||
title: "Something Went Wrong.",
|
||||
info: "The target directory could not be determined. Please try again with a different directory."
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
@ -228,18 +250,18 @@ extension GeneralPref {
|
||||
self.emit(.setDefaultUsesVcsIgnores(sender.boolState))
|
||||
}
|
||||
|
||||
@objc func openUntitledWindowWhenLaunchingAction(_ sender: NSButton) {
|
||||
@objc func openUntitledWindowWhenLaunchingAction(_: NSButton) {
|
||||
self.emit(.setOpenOnLaunch(self.openWhenLaunchingCheckbox.boolState))
|
||||
}
|
||||
|
||||
@objc func openUntitledWindowOnReactivationAction(_ sender: NSButton) {
|
||||
@objc func openUntitledWindowOnReactivationAction(_: NSButton) {
|
||||
self.emit(.setOpenOnReactivation(self.openOnReactivationCheckbox.boolState))
|
||||
}
|
||||
|
||||
@objc func afterLastWindowAction(_ sender: NSPopUpButton) {
|
||||
let index = sender.indexOfSelectedItem
|
||||
|
||||
guard index >= 0 && index <= 2 else {
|
||||
guard index >= 0, index <= 2 else {
|
||||
return
|
||||
}
|
||||
|
||||
@ -256,4 +278,5 @@ extension GeneralPref {
|
||||
}
|
||||
}
|
||||
|
||||
private let indexToAfterLastWindowAction: [AppState.AfterLastWindowAction] = [.doNothing, .hide, .quit]
|
||||
private let indexToAfterLastWindowAction: [AppState.AfterLastWindowAction] = [.doNothing, .hide,
|
||||
.quit]
|
||||
|
@ -6,7 +6,6 @@
|
||||
import Foundation
|
||||
|
||||
class GeneralPrefReducer: ReducerType {
|
||||
|
||||
typealias StateType = AppState
|
||||
typealias ActionType = GeneralPref.Action
|
||||
|
||||
@ -14,7 +13,6 @@ class GeneralPrefReducer: ReducerType {
|
||||
var state = pair.state
|
||||
|
||||
switch pair.action {
|
||||
|
||||
case let .setOpenOnLaunch(value):
|
||||
state.openNewMainWindowOnLaunch = value
|
||||
|
||||
@ -26,7 +24,6 @@ class GeneralPrefReducer: ReducerType {
|
||||
|
||||
case let .setDefaultUsesVcsIgnores(value):
|
||||
state.openQuickly.defaultUsesVcsIgnores = value
|
||||
|
||||
}
|
||||
|
||||
return (state, pair.action, true)
|
||||
|
@ -3,11 +3,10 @@
|
||||
* See LICENSE
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import Commons
|
||||
import Foundation
|
||||
|
||||
class HtmlPreviewMiddleware: MiddlewareType {
|
||||
|
||||
static func selectFirstHtmlUrl(uuid: UUID) -> URL {
|
||||
FileUtils.tempDir().appendingPathComponent("\(uuid)-select-first.html")
|
||||
}
|
||||
|
@ -4,21 +4,19 @@
|
||||
*/
|
||||
|
||||
import Cocoa
|
||||
import RxSwift
|
||||
import PureLayout
|
||||
import WebKit
|
||||
import EonilFSEvents
|
||||
import MaterialIcons
|
||||
import os
|
||||
import PureLayout
|
||||
import RxSwift
|
||||
import WebKit
|
||||
import Workspace
|
||||
|
||||
private let fileSystemEventsLatency = 1.0
|
||||
private let monitorDispatchQueue = DispatchQueue.global(qos: .userInitiated)
|
||||
|
||||
class HtmlPreviewTool: NSView, UiComponent, WKNavigationDelegate {
|
||||
|
||||
enum Action {
|
||||
|
||||
case selectHtmlFile(URL)
|
||||
}
|
||||
|
||||
@ -70,9 +68,10 @@ class HtmlPreviewTool: NSView, UiComponent, WKNavigationDelegate {
|
||||
sinceWhen: EonilFSEventsEventID.getCurrentEventId(),
|
||||
latency: fileSystemEventsLatency,
|
||||
flags: [.fileEvents],
|
||||
handler: { [weak self] event in
|
||||
handler: { [weak self] _ in
|
||||
self?.reloadWebview(with: serverUrl.payload)
|
||||
})
|
||||
}
|
||||
)
|
||||
self.monitor?.setDispatchQueue(monitorDispatchQueue)
|
||||
try self.monitor?.start()
|
||||
} catch {
|
||||
@ -92,7 +91,7 @@ class HtmlPreviewTool: NSView, UiComponent, WKNavigationDelegate {
|
||||
|
||||
private func reloadWebview(with url: URL) {
|
||||
DispatchQueue.main.async {
|
||||
self.webview.evaluateJavaScript("document.body.scrollTop") { (result, error) in
|
||||
self.webview.evaluateJavaScript("document.body.scrollTop") { result, _ in
|
||||
self.scrollTop = result as? Int ?? 0
|
||||
}
|
||||
}
|
||||
@ -117,12 +116,15 @@ class HtmlPreviewTool: NSView, UiComponent, WKNavigationDelegate {
|
||||
private var monitor: EonilFSEventStream?
|
||||
|
||||
private let disposeBag = DisposeBag()
|
||||
private let log = OSLog(subsystem: Defs.loggerSubsystem,
|
||||
category: Defs.LoggerCategory.ui)
|
||||
private let log = OSLog(
|
||||
subsystem: Defs.loggerSubsystem,
|
||||
category: Defs.LoggerCategory.ui
|
||||
)
|
||||
|
||||
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||||
@available(*, unavailable)
|
||||
required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||||
|
||||
@objc func selectHtmlFile(sender: Any?) {
|
||||
@objc func selectHtmlFile(sender _: Any?) {
|
||||
let panel = NSOpenPanel()
|
||||
panel.canChooseDirectories = false
|
||||
panel.allowsMultipleSelection = false
|
||||
@ -136,15 +138,13 @@ class HtmlPreviewTool: NSView, UiComponent, WKNavigationDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
||||
func webView(_: WKWebView, didFinish _: WKNavigation!) {
|
||||
self.webview.evaluateJavaScript("document.body.scrollTop = \(self.scrollTop)")
|
||||
}
|
||||
}
|
||||
|
||||
extension HtmlPreviewTool {
|
||||
|
||||
class InnerCustomToolbar: CustomToolBar {
|
||||
|
||||
fileprivate weak var htmlPreviewTool: HtmlPreviewTool? {
|
||||
didSet { self.selectHtmlFile.target = self.htmlPreviewTool }
|
||||
}
|
||||
@ -182,6 +182,7 @@ extension HtmlPreviewTool {
|
||||
selectHtmlFile.autoPinEdge(toSuperviewEdge: .right, withInset: InnerToolBar.itemPadding)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||||
@available(*, unavailable)
|
||||
required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||||
}
|
||||
}
|
||||
|
@ -6,11 +6,10 @@
|
||||
import Foundation
|
||||
|
||||
class HtmlPreviewReducer {
|
||||
|
||||
static let basePath = "tools/html-preview"
|
||||
|
||||
static func serverUrl(baseUrl: URL, uuid: UUID) -> URL {
|
||||
baseUrl.appendingPathComponent("\(uuid)/\(basePath)/index.html")
|
||||
baseUrl.appendingPathComponent("\(uuid)/\(self.basePath)/index.html")
|
||||
}
|
||||
|
||||
let mainWindow: MainWindowReducer
|
||||
@ -22,7 +21,6 @@ class HtmlPreviewReducer {
|
||||
}
|
||||
|
||||
class MainWindowReducer: ReducerType {
|
||||
|
||||
typealias StateType = MainWindow.State
|
||||
typealias ActionType = UuidAction<MainWindow.Action>
|
||||
|
||||
@ -32,7 +30,6 @@ class HtmlPreviewReducer {
|
||||
var state = pair.state
|
||||
|
||||
switch pair.action.payload {
|
||||
|
||||
case .setTheme:
|
||||
guard state.htmlPreview.htmlFile == nil else { return pair }
|
||||
state.htmlPreview.server = Marked(
|
||||
@ -41,7 +38,6 @@ class HtmlPreviewReducer {
|
||||
|
||||
default:
|
||||
return pair
|
||||
|
||||
}
|
||||
|
||||
return (state, pair.action, true)
|
||||
@ -51,7 +47,6 @@ class HtmlPreviewReducer {
|
||||
}
|
||||
|
||||
class HtmlPreviewToolReducer: ReducerType {
|
||||
|
||||
typealias StateType = MainWindow.State
|
||||
typealias ActionType = UuidAction<HtmlPreviewTool.Action>
|
||||
|
||||
@ -60,13 +55,11 @@ class HtmlPreviewReducer {
|
||||
func typedReduce(_ pair: ReduceTuple) -> ReduceTuple {
|
||||
var state = pair.state
|
||||
switch pair.action.payload {
|
||||
|
||||
case .selectHtmlFile(let url):
|
||||
case let .selectHtmlFile(url):
|
||||
state.htmlPreview.htmlFile = url
|
||||
state.htmlPreview.server = Marked(
|
||||
HtmlPreviewReducer.serverUrl(baseUrl: self.baseServerUrl, uuid: state.uuid)
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
return (state, pair.action, true)
|
||||
|
@ -4,11 +4,10 @@
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import Swifter
|
||||
import os
|
||||
import Swifter
|
||||
|
||||
class HttpServerMiddleware {
|
||||
|
||||
let htmlPreviewTool: HtmlPreviewToolMiddleware
|
||||
let htmlPreviewMainWindow: HtmlPreviewMainWindowMiddleware
|
||||
let markdownPreview: MarkdownPreviewMiddleware
|
||||
@ -63,15 +62,18 @@ class HttpServerMiddleware {
|
||||
}
|
||||
}
|
||||
|
||||
private let log = OSLog(subsystem: Defs.loggerSubsystem,
|
||||
category: Defs.LoggerCategory.middleware)
|
||||
private let log = OSLog(
|
||||
subsystem: Defs.loggerSubsystem,
|
||||
category: Defs.LoggerCategory.middleware
|
||||
)
|
||||
|
||||
class HtmlPreviewMainWindowMiddleware: MiddlewareType {
|
||||
|
||||
typealias StateType = MainWindow.State
|
||||
typealias ActionType = UuidAction<MainWindow.Action>
|
||||
|
||||
fileprivate init(server: HttpServer, baseUrl: URL, cssUrl: URL, htmlTemplates: HtmlTemplates) {
|
||||
fileprivate init(server: HttpServer, baseUrl: URL, cssUrl: URL,
|
||||
htmlTemplates _: HtmlTemplates)
|
||||
{
|
||||
self.server = server
|
||||
self.baseUrl = baseUrl
|
||||
self.cssUrl = cssUrl
|
||||
@ -108,18 +110,21 @@ class HttpServerMiddleware {
|
||||
private let baseUrl: URL
|
||||
private let cssUrl: URL
|
||||
|
||||
private let log = OSLog(subsystem: Defs.loggerSubsystem,
|
||||
category: Defs.LoggerCategory.middleware)
|
||||
private let log = OSLog(
|
||||
subsystem: Defs.loggerSubsystem,
|
||||
category: Defs.LoggerCategory.middleware
|
||||
)
|
||||
|
||||
private func fullUrl(with path: String) -> URL { self.baseUrl.appendingPathComponent(path) }
|
||||
}
|
||||
|
||||
class HtmlPreviewToolMiddleware: MiddlewareType {
|
||||
|
||||
typealias StateType = MainWindow.State
|
||||
typealias ActionType = UuidAction<HtmlPreviewTool.Action>
|
||||
|
||||
fileprivate init(server: HttpServer, baseUrl: URL, cssUrl: URL, htmlTemplates: HtmlTemplates) {
|
||||
fileprivate init(server: HttpServer, baseUrl: URL, cssUrl: URL,
|
||||
htmlTemplates _: HtmlTemplates)
|
||||
{
|
||||
self.server = server
|
||||
self.baseUrl = baseUrl
|
||||
self.cssUrl = cssUrl
|
||||
@ -152,14 +157,15 @@ class HttpServerMiddleware {
|
||||
private let baseUrl: URL
|
||||
private let cssUrl: URL
|
||||
|
||||
private let log = OSLog(subsystem: Defs.loggerSubsystem,
|
||||
category: Defs.LoggerCategory.middleware)
|
||||
private let log = OSLog(
|
||||
subsystem: Defs.loggerSubsystem,
|
||||
category: Defs.LoggerCategory.middleware
|
||||
)
|
||||
|
||||
private func fullUrl(with path: String) -> URL { self.baseUrl.appendingPathComponent(path) }
|
||||
}
|
||||
|
||||
class MarkdownPreviewMiddleware: MiddlewareType {
|
||||
|
||||
typealias StateType = MainWindow.State
|
||||
typealias ActionType = UuidAction<MainWindow.Action>
|
||||
|
||||
@ -213,8 +219,10 @@ class HttpServerMiddleware {
|
||||
private let cssUrl: URL
|
||||
private let baseCssUrl: URL
|
||||
|
||||
private let log = OSLog(subsystem: Defs.loggerSubsystem,
|
||||
category: Defs.LoggerCategory.middleware)
|
||||
private let log = OSLog(
|
||||
subsystem: Defs.loggerSubsystem,
|
||||
category: Defs.LoggerCategory.middleware
|
||||
)
|
||||
|
||||
private func fullUrl(with path: String) -> URL { self.baseUrl.appendingPathComponent(path) }
|
||||
}
|
||||
|
@ -7,11 +7,11 @@ import Cocoa
|
||||
import PureLayout
|
||||
|
||||
class ImageAndTextTableCell: NSTableCellView {
|
||||
|
||||
private let _textField = NSTextField(forAutoLayout: ())
|
||||
private let _imageView = NSImageView(forAutoLayout: ())
|
||||
|
||||
// MARK: - API
|
||||
|
||||
static let font = NSFont.systemFont(ofSize: 12)
|
||||
static let widthWithoutText = (2 + 16 + 4 + 2).cgf
|
||||
|
||||
@ -86,5 +86,6 @@ class ImageAndTextTableCell: NSTableCellView {
|
||||
return self
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||||
@available(*, unavailable)
|
||||
required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||||
}
|
||||
|
@ -8,21 +8,19 @@ import PureLayout
|
||||
import RxSwift
|
||||
|
||||
class KeysPref: PrefPane, UiComponent, NSTextFieldDelegate {
|
||||
|
||||
typealias StateType = AppState
|
||||
|
||||
enum Action {
|
||||
|
||||
case isLeftOptionMeta(Bool)
|
||||
case isRightOptionMeta(Bool)
|
||||
}
|
||||
|
||||
override var displayName: String {
|
||||
return "Keys"
|
||||
"Keys"
|
||||
}
|
||||
|
||||
override var pinToContainer: Bool {
|
||||
return true
|
||||
true
|
||||
}
|
||||
|
||||
required init(source: Observable<StateType>, emitter: ActionEmitter, state: StateType) {
|
||||
@ -40,7 +38,7 @@ class KeysPref: PrefPane, UiComponent, NSTextFieldDelegate {
|
||||
.observeOn(MainScheduler.instance)
|
||||
.subscribe(onNext: { state in
|
||||
if self.isLeftOptionMeta != state.mainWindowTemplate.isLeftOptionMeta
|
||||
|| self.isRightOptionMeta != state.mainWindowTemplate.isRightOptionMeta
|
||||
|| self.isRightOptionMeta != state.mainWindowTemplate.isRightOptionMeta
|
||||
{
|
||||
self.isLeftOptionMeta = state.mainWindowTemplate.isLeftOptionMeta
|
||||
self.isRightOptionMeta = state.mainWindowTemplate.isRightOptionMeta
|
||||
@ -60,7 +58,8 @@ class KeysPref: PrefPane, UiComponent, NSTextFieldDelegate {
|
||||
private let isLeftOptionMetaCheckbox = NSButton(forAutoLayout: ())
|
||||
private let isRightOptionMetaCheckbox = NSButton(forAutoLayout: ())
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
@available(*, unavailable)
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
@ -73,14 +72,18 @@ class KeysPref: PrefPane, UiComponent, NSTextFieldDelegate {
|
||||
let paneTitle = self.paneTitleTextField(title: "Keys")
|
||||
|
||||
let isLeftOptionMeta = self.isLeftOptionMetaCheckbox
|
||||
self.configureCheckbox(button: isLeftOptionMeta,
|
||||
title: "Use Left Option as Meta",
|
||||
action: #selector(KeysPref.isLeftOptionMetaAction(_:)))
|
||||
self.configureCheckbox(
|
||||
button: isLeftOptionMeta,
|
||||
title: "Use Left Option as Meta",
|
||||
action: #selector(KeysPref.isLeftOptionMetaAction(_:))
|
||||
)
|
||||
|
||||
let isRightOptionMeta = self.isRightOptionMetaCheckbox
|
||||
self.configureCheckbox(button: isRightOptionMeta,
|
||||
title: "Use Right Option as Meta",
|
||||
action: #selector(KeysPref.isRightOptionMetaAction(_:)))
|
||||
self.configureCheckbox(
|
||||
button: isRightOptionMeta,
|
||||
title: "Use Right Option as Meta",
|
||||
action: #selector(KeysPref.isRightOptionMetaAction(_:))
|
||||
)
|
||||
|
||||
let metaInfo = self.infoTextField(markdown: #"""
|
||||
When an Option key is set to Meta, then every input containing the corresponding Option key will\
|
||||
@ -111,8 +114,8 @@ class KeysPref: PrefPane, UiComponent, NSTextFieldDelegate {
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
extension KeysPref {
|
||||
|
||||
extension KeysPref {
|
||||
@objc func isLeftOptionMetaAction(_ sender: NSButton) {
|
||||
self.emit(.isLeftOptionMeta(sender.boolState))
|
||||
}
|
||||
|
@ -6,7 +6,6 @@
|
||||
import Foundation
|
||||
|
||||
class KeysPrefReducer: ReducerType {
|
||||
|
||||
typealias StateType = AppState
|
||||
typealias ActionType = KeysPref.Action
|
||||
|
||||
@ -14,15 +13,13 @@ class KeysPrefReducer: ReducerType {
|
||||
var state = pair.state
|
||||
|
||||
switch pair.action {
|
||||
|
||||
case let .isLeftOptionMeta(value):
|
||||
state.mainWindowTemplate.isLeftOptionMeta = value
|
||||
state.mainWindows.keys.forEach { state.mainWindows[$0]?.isLeftOptionMeta = value }
|
||||
state.mainWindows.keys.forEach { state.mainWindows[$0]?.isLeftOptionMeta = value }
|
||||
|
||||
case let .isRightOptionMeta(value):
|
||||
state.mainWindowTemplate.isRightOptionMeta = value
|
||||
state.mainWindows.keys.forEach { state.mainWindows[$0]?.isRightOptionMeta = value }
|
||||
|
||||
state.mainWindows.keys.forEach { state.mainWindows[$0]?.isRightOptionMeta = value }
|
||||
}
|
||||
|
||||
return (state, pair.action, true)
|
||||
|
@ -4,20 +4,20 @@
|
||||
*/
|
||||
|
||||
import Cocoa
|
||||
import RxSwift
|
||||
import MessagePack
|
||||
import Commons
|
||||
import MessagePack
|
||||
import RxSwift
|
||||
import Workspace
|
||||
|
||||
// MARK: - RpcEvent Actions
|
||||
extension MainWindow {
|
||||
|
||||
extension MainWindow {
|
||||
func rpcEventAction(params rawParams: [MessagePackValue]) {
|
||||
guard rawParams.count > 0 else { return }
|
||||
|
||||
guard let strEvent = rawParams[0].stringValue,
|
||||
let event = RpcEvent(rawValue: "\(RpcEvent.prefix).\(strEvent)")
|
||||
else {
|
||||
else {
|
||||
return
|
||||
}
|
||||
let params = Array(rawParams.suffix(from: 1))
|
||||
@ -64,7 +64,7 @@ extension MainWindow {
|
||||
guard let fontName = params[0].stringValue,
|
||||
let fontSize = params[1].int64Value,
|
||||
let font = NSFont(name: fontName, size: fontSize.cgf)
|
||||
else {
|
||||
else {
|
||||
return
|
||||
}
|
||||
|
||||
@ -79,9 +79,8 @@ extension MainWindow {
|
||||
case .setCharacterspacing:
|
||||
guard params.count == 1 else { return }
|
||||
guard let characterspacing = params[0].floatValue else { return }
|
||||
|
||||
self.emit(self.uuidAction(for: .setCharacterspacing(characterspacing.cgf)))
|
||||
|
||||
self.emit(self.uuidAction(for: .setCharacterspacing(characterspacing.cgf)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,16 +118,16 @@ extension MainWindow {
|
||||
}
|
||||
|
||||
// MARK: - File Menu Item Actions
|
||||
extension MainWindow {
|
||||
|
||||
@IBAction func newTab(_ sender: Any?) {
|
||||
extension MainWindow {
|
||||
@IBAction func newTab(_: Any?) {
|
||||
self.neoVimView
|
||||
.newTab()
|
||||
.subscribe()
|
||||
.disposed(by: self.disposeBag)
|
||||
}
|
||||
|
||||
@IBAction func openDocument(_ sender: Any?) {
|
||||
@IBAction func openDocument(_: Any?) {
|
||||
let panel = NSOpenPanel()
|
||||
panel.canChooseDirectories = true
|
||||
panel.allowsMultipleSelection = true
|
||||
@ -155,16 +154,16 @@ extension MainWindow {
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func openQuickly(_ sender: Any?) {
|
||||
@IBAction func openQuickly(_: Any?) {
|
||||
self.emit(self.uuidAction(for: .openQuickly))
|
||||
}
|
||||
|
||||
@IBAction func closeWindow(_ sender: Any?) {
|
||||
@IBAction func closeWindow(_: Any?) {
|
||||
self.closeWindow = true
|
||||
self.window.performClose(nil)
|
||||
}
|
||||
|
||||
@IBAction func saveDocument(_ sender: Any?) {
|
||||
@IBAction func saveDocument(_: Any?) {
|
||||
self.neoVimView
|
||||
.currentBuffer()
|
||||
.observeOn(MainScheduler.instance)
|
||||
@ -185,7 +184,7 @@ extension MainWindow {
|
||||
.disposed(by: self.disposeBag)
|
||||
}
|
||||
|
||||
@IBAction func saveDocumentAs(_ sender: Any?) {
|
||||
@IBAction func saveDocumentAs(_: Any?) {
|
||||
self.neoVimView
|
||||
.currentBuffer()
|
||||
.observeOn(MainScheduler.instance)
|
||||
@ -194,7 +193,8 @@ extension MainWindow {
|
||||
self.neoVimView
|
||||
.saveCurrentTab(url: url)
|
||||
.andThen(
|
||||
curBuf.isDirty ? self.neoVimView.openInNewTab(urls: [url]) : self.neoVimView.openInCurrentTab(url: url)
|
||||
curBuf.isDirty ? self.neoVimView.openInNewTab(urls: [url]) : self.neoVimView
|
||||
.openInCurrentTab(url: url)
|
||||
)
|
||||
.subscribe()
|
||||
.disposed(by: self.disposeBag)
|
||||
@ -214,7 +214,9 @@ extension MainWindow {
|
||||
let alert = NSAlert()
|
||||
alert.addButton(withTitle: "OK")
|
||||
alert.messageText = "Invalid File Name"
|
||||
alert.informativeText = "The file name you have entered cannot be used. Please use a different name."
|
||||
alert
|
||||
.informativeText =
|
||||
"The file name you have entered cannot be used. Please use a different name."
|
||||
alert.alertStyle = .warning
|
||||
|
||||
alert.runModal()
|
||||
@ -231,36 +233,36 @@ extension MainWindow {
|
||||
}
|
||||
|
||||
// MARK: - Tools Menu Item Actions
|
||||
extension MainWindow {
|
||||
|
||||
@IBAction func toggleAllTools(_ sender: Any?) {
|
||||
extension MainWindow {
|
||||
@IBAction func toggleAllTools(_: Any?) {
|
||||
self.workspace.toggleAllTools()
|
||||
self.focusNvimView(self)
|
||||
|
||||
self.emit(self.uuidAction(for: .toggleAllTools(self.workspace.isAllToolsVisible)))
|
||||
}
|
||||
|
||||
@IBAction func toggleToolButtons(_ sender: Any?) {
|
||||
@IBAction func toggleToolButtons(_: Any?) {
|
||||
self.workspace.toggleToolButtons()
|
||||
self.emit(self.uuidAction(for: .toggleToolButtons(self.workspace.isToolButtonsVisible)))
|
||||
}
|
||||
|
||||
@IBAction func toggleFileBrowser(_ sender: Any?) {
|
||||
@IBAction func toggleFileBrowser(_: Any?) {
|
||||
guard let fileBrowser = self.fileBrowserContainer else { return }
|
||||
self.toggle(tool: fileBrowser, toolType: .fileBrowser)
|
||||
}
|
||||
|
||||
@IBAction func toggleBufferList(_ sender: Any?) {
|
||||
@IBAction func toggleBufferList(_: Any?) {
|
||||
guard let bufferList = self.buffersListContainer else { return }
|
||||
self.toggle(tool: bufferList, toolType: .bufferList)
|
||||
}
|
||||
|
||||
@IBAction func toggleMarkdownPreview(_ sender: Any?) {
|
||||
@IBAction func toggleMarkdownPreview(_: Any?) {
|
||||
guard let markdownPreview = self.previewContainer else { return }
|
||||
self.toggle(tool: markdownPreview, toolType: .markdownPreview)
|
||||
}
|
||||
|
||||
@IBAction func toggleHtmlPreview(_ sender: Any?) {
|
||||
@IBAction func toggleHtmlPreview(_: Any?) {
|
||||
guard let htmlPreview = self.htmlPreviewContainer else { return }
|
||||
self.toggle(tool: htmlPreview, toolType: .htmlPreview)
|
||||
}
|
||||
@ -287,8 +289,8 @@ extension MainWindow {
|
||||
}
|
||||
|
||||
// MARK: - NSUserInterfaceValidationsProtocol
|
||||
extension MainWindow {
|
||||
|
||||
extension MainWindow {
|
||||
func validateUserInterfaceItem(_ item: NSValidatedUserInterfaceItem) -> Bool {
|
||||
let canSave = self.neoVimView.currentBuffer().syncValue()?.type == ""
|
||||
let canSaveAs = canSave
|
||||
@ -303,31 +305,29 @@ extension MainWindow {
|
||||
}
|
||||
|
||||
switch action {
|
||||
|
||||
case #selector(toggleAllTools(_:)), #selector(toggleToolButtons(_:)):
|
||||
case #selector(self.toggleAllTools(_:)), #selector(self.toggleToolButtons(_:)):
|
||||
return canToggleTools
|
||||
|
||||
case #selector(toggleFileBrowser(_:)):
|
||||
case #selector(self.toggleFileBrowser(_:)):
|
||||
return canToggleFileBrowser
|
||||
|
||||
case #selector(focusNvimView(_:)):
|
||||
case #selector(self.focusNvimView(_:)):
|
||||
return canFocusNvimView
|
||||
|
||||
case #selector(openDocument(_:)):
|
||||
case #selector(self.openDocument(_:)):
|
||||
return canOpen
|
||||
|
||||
case #selector(openQuickly(_:)):
|
||||
case #selector(self.openQuickly(_:)):
|
||||
return canOpenQuickly
|
||||
|
||||
case #selector(saveDocument(_:)):
|
||||
case #selector(self.saveDocument(_:)):
|
||||
return canSave
|
||||
|
||||
case #selector(saveDocumentAs(_:)):
|
||||
case #selector(self.saveDocumentAs(_:)):
|
||||
return canSaveAs
|
||||
|
||||
default:
|
||||
return true
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,9 +7,9 @@ import Cocoa
|
||||
import PureLayout
|
||||
|
||||
// MARK: - Custom title
|
||||
extension MainWindow {
|
||||
|
||||
func themeTitlebar(grow: Bool) {
|
||||
extension MainWindow {
|
||||
func themeTitlebar(grow _: Bool) {
|
||||
if self.window.styleMask.contains(.fullScreen) {
|
||||
return
|
||||
}
|
||||
@ -100,7 +100,8 @@ extension MainWindow {
|
||||
self.titleView = title
|
||||
|
||||
if let button = self.window.standardWindowButton(.documentIconButton) {
|
||||
button.removeFromSuperview() // remove the rep icon from the original superview and add it to content view
|
||||
button
|
||||
.removeFromSuperview() // remove the rep icon from the original superview and add it to content view
|
||||
contentView.addSubview(button)
|
||||
button.autoSetDimension(.width, toSize: 16)
|
||||
button.autoSetDimension(.height, toSize: 16)
|
||||
@ -110,11 +111,15 @@ extension MainWindow {
|
||||
// rightView.left = leftView.right + gap
|
||||
// rightView.right = parentView.centerX + (leftView.width + gap + rightView.width) / 2 - 4
|
||||
// The (-4) at the end is an empirical value...
|
||||
contentView.addConstraint(NSLayoutConstraint(item: title, attribute: .left,
|
||||
relatedBy: .equal,
|
||||
toItem: button, attribute: .right,
|
||||
multiplier: 1,
|
||||
constant: repIconToTitleGap))
|
||||
contentView.addConstraint(NSLayoutConstraint(
|
||||
item: title,
|
||||
attribute: .left,
|
||||
relatedBy: .equal,
|
||||
toItem: button,
|
||||
attribute: .right,
|
||||
multiplier: 1,
|
||||
constant: repIconToTitleGap
|
||||
))
|
||||
contentView.addConstraint(
|
||||
// Here we use title.intrinsicContentSize instead of title.frame because title.frame is still zero.
|
||||
NSLayoutConstraint(
|
||||
@ -122,7 +127,8 @@ extension MainWindow {
|
||||
relatedBy: .equal,
|
||||
toItem: contentView, attribute: .centerX,
|
||||
multiplier: 1,
|
||||
constant: -4 + (button.frame.width + repIconToTitleGap + title.intrinsicContentSize.width) / 2
|
||||
constant: -4 +
|
||||
(button.frame.width + repIconToTitleGap + title.intrinsicContentSize.width) / 2
|
||||
)
|
||||
)
|
||||
|
||||
@ -149,4 +155,4 @@ extension MainWindow {
|
||||
}
|
||||
}
|
||||
|
||||
private let repIconToTitleGap = (4.0).cgf
|
||||
private let repIconToTitleGap = 4.0.cgf
|
||||
|
@ -4,14 +4,14 @@
|
||||
*/
|
||||
|
||||
import Cocoa
|
||||
import RxSwift
|
||||
import NvimView
|
||||
import RxPack
|
||||
import RxSwift
|
||||
import Workspace
|
||||
|
||||
// MARK: - NvimViewDelegate
|
||||
extension MainWindow {
|
||||
|
||||
extension MainWindow {
|
||||
// Use only when Cmd-Q'ing
|
||||
func waitTillNvimExits() {
|
||||
self.neoVimView.waitTillNvimExits()
|
||||
@ -35,7 +35,9 @@ extension MainWindow {
|
||||
self.window.toggleFullScreen(nil)
|
||||
}
|
||||
|
||||
guard let cliPipePath = self.cliPipePath, FileManager.default.fileExists(atPath: cliPipePath) else {
|
||||
guard let cliPipePath = self.cliPipePath,
|
||||
FileManager.default.fileExists(atPath: cliPipePath)
|
||||
else {
|
||||
return
|
||||
}
|
||||
|
||||
@ -92,8 +94,10 @@ extension MainWindow {
|
||||
self
|
||||
.updateCssColors()
|
||||
.subscribe(onSuccess: { colors in
|
||||
self.emit(self.uuidAction(
|
||||
for: .setTheme(Theme(from: nvimTheme, additionalColorDict: colors)))
|
||||
self.emit(
|
||||
self.uuidAction(
|
||||
for: .setTheme(Theme(from: nvimTheme, additionalColorDict: colors))
|
||||
)
|
||||
)
|
||||
})
|
||||
.disposed(by: self.disposeBag)
|
||||
@ -103,10 +107,12 @@ extension MainWindow {
|
||||
let alert = NSAlert()
|
||||
alert.addButton(withTitle: "Close")
|
||||
alert.messageText = "Sorry, an error occurred."
|
||||
alert.informativeText = "VimR encountered an error from which it cannot recover. This window will now close.\n"
|
||||
+ reason
|
||||
alert
|
||||
.informativeText =
|
||||
"VimR encountered an error from which it cannot recover. This window will now close.\n"
|
||||
+ reason
|
||||
alert.alertStyle = .critical
|
||||
alert.beginSheetModal(for: self.window) { response in
|
||||
alert.beginSheetModal(for: self.window) { _ in
|
||||
self.windowController.close()
|
||||
}
|
||||
}
|
||||
@ -132,7 +138,7 @@ extension MainWindow {
|
||||
"CursorColumn", // code background and foreground
|
||||
]
|
||||
|
||||
typealias HlResult = Dictionary<String, RxNeovimApi.Value>
|
||||
typealias HlResult = [String: RxNeovimApi.Value]
|
||||
typealias ColorNameHlResultTuple = (colorName: String, hlResult: HlResult)
|
||||
typealias ColorNameObservableTuple = (colorName: String, observable: Observable<HlResult>)
|
||||
|
||||
@ -164,7 +170,6 @@ extension MainWindow {
|
||||
// MARK: - NSWindowDelegate
|
||||
|
||||
extension MainWindow {
|
||||
|
||||
func windowWillEnterFullScreen(_: Notification) {
|
||||
self.unthemeTitlebar(dueFullScreen: true)
|
||||
}
|
||||
@ -175,20 +180,24 @@ extension MainWindow {
|
||||
}
|
||||
}
|
||||
|
||||
func windowDidBecomeMain(_ notification: Notification) {
|
||||
self.emit(self.uuidAction(for: .becomeKey(isFullScreen: self.window.styleMask.contains(.fullScreen))))
|
||||
func windowDidBecomeMain(_: Notification) {
|
||||
self
|
||||
.emit(
|
||||
self
|
||||
.uuidAction(for: .becomeKey(isFullScreen: self.window.styleMask.contains(.fullScreen)))
|
||||
)
|
||||
self.neoVimView.didBecomeMain().subscribe().disposed(by: self.disposeBag)
|
||||
}
|
||||
|
||||
func windowDidResignMain(_ notification: Notification) {
|
||||
func windowDidResignMain(_: Notification) {
|
||||
self.neoVimView.didResignMain().subscribe().disposed(by: self.disposeBag)
|
||||
}
|
||||
|
||||
func windowDidMove(_ notification: Notification) {
|
||||
func windowDidMove(_: Notification) {
|
||||
self.emit(self.uuidAction(for: .frameChanged(to: self.window.frame)))
|
||||
}
|
||||
|
||||
func windowDidResize(_ notification: Notification) {
|
||||
func windowDidResize(_: Notification) {
|
||||
if self.window.styleMask.contains(.fullScreen) {
|
||||
return
|
||||
}
|
||||
@ -199,7 +208,7 @@ extension MainWindow {
|
||||
func windowShouldClose(_: NSWindow) -> Bool {
|
||||
defer { self.closeWindow = false }
|
||||
|
||||
if (self.neoVimView.isBlocked().syncValue() ?? false) {
|
||||
if self.neoVimView.isBlocked().syncValue() ?? false {
|
||||
let alert = NSAlert()
|
||||
alert.messageText = "Nvim is waiting for your input."
|
||||
alert.alertStyle = .informational
|
||||
@ -221,7 +230,7 @@ extension MainWindow {
|
||||
return false
|
||||
}
|
||||
|
||||
guard (self.neoVimView.isCurrentBufferDirty().syncValue() ?? false) else {
|
||||
guard self.neoVimView.isCurrentBufferDirty().syncValue() ?? false else {
|
||||
try? self.neoVimView.closeCurrentTab().wait()
|
||||
return false
|
||||
}
|
||||
@ -249,13 +258,13 @@ extension MainWindow {
|
||||
}
|
||||
|
||||
// MARK: - WorkspaceDelegate
|
||||
extension MainWindow {
|
||||
|
||||
func resizeWillStart(workspace: Workspace, tool: WorkspaceTool?) {
|
||||
extension MainWindow {
|
||||
func resizeWillStart(workspace _: Workspace, tool _: WorkspaceTool?) {
|
||||
self.neoVimView.enterResizeMode()
|
||||
}
|
||||
|
||||
func resizeDidEnd(workspace: Workspace, tool: WorkspaceTool?) {
|
||||
func resizeDidEnd(workspace _: Workspace, tool: WorkspaceTool?) {
|
||||
self.neoVimView.exitResizeMode()
|
||||
|
||||
if let workspaceTool = tool, let toolIdentifier = self.toolIdentifier(for: workspaceTool) {
|
||||
@ -270,13 +279,14 @@ extension MainWindow {
|
||||
}
|
||||
|
||||
func moved(tool: WorkspaceTool) {
|
||||
let tools = self.workspace.orderedTools.compactMap { (tool: WorkspaceTool) -> (Tools, WorkspaceTool)? in
|
||||
guard let toolId = self.toolIdentifier(for: tool) else {
|
||||
return nil
|
||||
}
|
||||
let tools = self.workspace.orderedTools
|
||||
.compactMap { (tool: WorkspaceTool) -> (Tools, WorkspaceTool)? in
|
||||
guard let toolId = self.toolIdentifier(for: tool) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return (toolId, tool)
|
||||
}
|
||||
return (toolId, tool)
|
||||
}
|
||||
|
||||
self.emit(self.uuidAction(for: .setToolsState(tools)))
|
||||
}
|
||||
|
@ -8,9 +8,7 @@ import NvimView
|
||||
import Workspace
|
||||
|
||||
extension MainWindow {
|
||||
|
||||
enum Action {
|
||||
|
||||
case cd(to: URL)
|
||||
case setBufferList([NvimView.Buffer])
|
||||
|
||||
@ -46,7 +44,6 @@ extension MainWindow {
|
||||
}
|
||||
|
||||
enum FocusableView {
|
||||
|
||||
case neoVimView
|
||||
case fileBrowser
|
||||
case bufferList
|
||||
@ -55,13 +52,12 @@ extension MainWindow {
|
||||
}
|
||||
|
||||
enum Tools: String, Codable {
|
||||
|
||||
static let all = Set(
|
||||
[
|
||||
Tools.fileBrowser,
|
||||
Tools.buffersList,
|
||||
Tools.preview,
|
||||
Tools.htmlPreview
|
||||
Tools.htmlPreview,
|
||||
]
|
||||
)
|
||||
|
||||
@ -72,7 +68,6 @@ extension MainWindow {
|
||||
}
|
||||
|
||||
enum OpenMode {
|
||||
|
||||
case `default`
|
||||
case currentTab
|
||||
case newTab
|
||||
|
@ -4,18 +4,18 @@
|
||||
*/
|
||||
|
||||
import Cocoa
|
||||
import RxSwift
|
||||
import NvimView
|
||||
import PureLayout
|
||||
import os
|
||||
import PureLayout
|
||||
import RxSwift
|
||||
import Workspace
|
||||
|
||||
class MainWindow: NSObject,
|
||||
UiComponent,
|
||||
NSWindowDelegate,
|
||||
NSUserInterfaceValidations,
|
||||
WorkspaceDelegate {
|
||||
|
||||
UiComponent,
|
||||
NSWindowDelegate,
|
||||
NSUserInterfaceValidations,
|
||||
WorkspaceDelegate
|
||||
{
|
||||
typealias StateType = State
|
||||
|
||||
let disposeBag = DisposeBag()
|
||||
@ -67,7 +67,8 @@ class MainWindow: NSObject,
|
||||
|
||||
var sourceFileUrls = [URL]()
|
||||
if let sourceFileUrl = Bundle(for: MainWindow.self)
|
||||
.url(forResource: "com.qvacua.VimR", withExtension: "vim") {
|
||||
.url(forResource: "com.qvacua.VimR", withExtension: "vim")
|
||||
{
|
||||
sourceFileUrls.append(sourceFileUrl)
|
||||
}
|
||||
|
||||
@ -97,9 +98,11 @@ class MainWindow: NSObject,
|
||||
}
|
||||
|
||||
if state.activeTools[.htmlPreview] == true {
|
||||
self.htmlPreview = HtmlPreviewTool(source: source,
|
||||
emitter: emitter,
|
||||
state: state)
|
||||
self.htmlPreview = HtmlPreviewTool(
|
||||
source: source,
|
||||
emitter: emitter,
|
||||
state: state
|
||||
)
|
||||
let htmlPreviewConfig = WorkspaceTool.Config(
|
||||
title: "HTML",
|
||||
view: self.htmlPreview!,
|
||||
@ -107,7 +110,7 @@ class MainWindow: NSObject,
|
||||
)
|
||||
self.htmlPreviewContainer = WorkspaceTool(htmlPreviewConfig)
|
||||
self.htmlPreviewContainer!.dimension = state.tools[.htmlPreview]?
|
||||
.dimension ?? 250
|
||||
.dimension ?? 250
|
||||
tools[.htmlPreview] = self.htmlPreviewContainer
|
||||
}
|
||||
|
||||
@ -123,8 +126,8 @@ class MainWindow: NSObject,
|
||||
)
|
||||
self.fileBrowserContainer = WorkspaceTool(fileBrowserConfig)
|
||||
self.fileBrowserContainer!.dimension = state
|
||||
.tools[.fileBrowser]?
|
||||
.dimension ?? 200
|
||||
.tools[.fileBrowser]?
|
||||
.dimension ?? 200
|
||||
tools[.fileBrowser] = self.fileBrowserContainer
|
||||
}
|
||||
|
||||
@ -132,12 +135,14 @@ class MainWindow: NSObject,
|
||||
self.buffersList = BuffersList(
|
||||
source: source, emitter: emitter, state: state
|
||||
)
|
||||
let buffersListConfig = WorkspaceTool.Config(title: "Buffers",
|
||||
view: self.buffersList!)
|
||||
let buffersListConfig = WorkspaceTool.Config(
|
||||
title: "Buffers",
|
||||
view: self.buffersList!
|
||||
)
|
||||
self.buffersListContainer = WorkspaceTool(buffersListConfig)
|
||||
self.buffersListContainer!.dimension = state
|
||||
.tools[.buffersList]?
|
||||
.dimension ?? 200
|
||||
.tools[.buffersList]?
|
||||
.dimension ?? 200
|
||||
tools[.buffersList] = self.buffersListContainer
|
||||
}
|
||||
|
||||
@ -163,11 +168,13 @@ class MainWindow: NSObject,
|
||||
return
|
||||
}
|
||||
|
||||
self.workspace.append(tool: tool,
|
||||
location: state.tools[toolId]?.location ?? .left)
|
||||
self.workspace.append(
|
||||
tool: tool,
|
||||
location: state.tools[toolId]?.location ?? .left
|
||||
)
|
||||
}
|
||||
|
||||
self.tools.forEach { (toolId, toolContainer) in
|
||||
self.tools.forEach { toolId, toolContainer in
|
||||
if state.tools[toolId]?.open == true {
|
||||
toolContainer.toggle()
|
||||
}
|
||||
@ -204,7 +211,7 @@ class MainWindow: NSObject,
|
||||
}
|
||||
|
||||
func uuidAction(for action: Action) -> UuidAction<Action> {
|
||||
return UuidAction(uuid: self.uuid, action: action)
|
||||
UuidAction(uuid: self.uuid, action: action)
|
||||
}
|
||||
|
||||
func show() {
|
||||
@ -213,7 +220,7 @@ class MainWindow: NSObject,
|
||||
|
||||
// The following should only be used when Cmd-Q'ing
|
||||
func quitNeoVimWithoutSaving() -> Completable {
|
||||
return self.neoVimView.quitNeoVimWithoutSaving()
|
||||
self.neoVimView.quitNeoVimWithoutSaving()
|
||||
}
|
||||
|
||||
@IBAction func debug2(_: Any?) {
|
||||
@ -222,10 +229,11 @@ class MainWindow: NSObject,
|
||||
theme.background = .yellow
|
||||
theme.highlightForeground = .orange
|
||||
theme.highlightBackground = .red
|
||||
self.emit(uuidAction(for: .setTheme(theme)))
|
||||
self.emit(self.uuidAction(for: .setTheme(theme)))
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private var currentBuffer: NvimView.Buffer?
|
||||
|
||||
private var goToLineFromCli: Marked<Int>?
|
||||
@ -246,8 +254,10 @@ class MainWindow: NSObject,
|
||||
private var usesTheme = true
|
||||
private var lastThemeMark = Token()
|
||||
|
||||
private let log = OSLog(subsystem: Defs.loggerSubsystem,
|
||||
category: Defs.LoggerCategory.ui)
|
||||
private let log = OSLog(
|
||||
subsystem: Defs.loggerSubsystem,
|
||||
category: Defs.LoggerCategory.ui
|
||||
)
|
||||
|
||||
private func setupScrollAndCursorDebouncers() {
|
||||
Observable
|
||||
@ -265,12 +275,11 @@ class MainWindow: NSObject,
|
||||
.observeOn(MainScheduler.instance)
|
||||
.subscribe(onNext: { [weak self] event in
|
||||
switch event {
|
||||
|
||||
case .neoVimStopped: self?.neoVimStopped()
|
||||
|
||||
case .setTitle(let title): self?.set(title: title)
|
||||
case let .setTitle(title): self?.set(title: title)
|
||||
|
||||
case .setDirtyStatus(let dirty): self?.set(dirtyStatus: dirty)
|
||||
case let .setDirtyStatus(dirty): self?.set(dirtyStatus: dirty)
|
||||
|
||||
case .cwdChanged: self?.cwdChanged()
|
||||
|
||||
@ -278,32 +287,30 @@ class MainWindow: NSObject,
|
||||
|
||||
case .tabChanged: self?.tabChanged()
|
||||
|
||||
case .newCurrentBuffer(let curBuf): self?.newCurrentBuffer(curBuf)
|
||||
case let .newCurrentBuffer(curBuf): self?.newCurrentBuffer(curBuf)
|
||||
|
||||
case .bufferWritten(let buf): self?.bufferWritten(buf)
|
||||
case let .bufferWritten(buf): self?.bufferWritten(buf)
|
||||
|
||||
case .colorschemeChanged(let theme): self?.colorschemeChanged(to: theme)
|
||||
case let .colorschemeChanged(theme): self?.colorschemeChanged(to: theme)
|
||||
|
||||
|
||||
case .ipcBecameInvalid(let reason):
|
||||
case let .ipcBecameInvalid(reason):
|
||||
self?.ipcBecameInvalid(reason: reason)
|
||||
|
||||
case .scroll: self?.scroll()
|
||||
|
||||
case .cursor(let position): self?.cursor(to: position)
|
||||
case let .cursor(position): self?.cursor(to: position)
|
||||
|
||||
case .initVimError: self?.showInitError()
|
||||
|
||||
case .apiError(let error, let msg):
|
||||
case let .apiError(error, msg):
|
||||
self?.log.error("Got api error with msg '\(msg)' and error: \(error)")
|
||||
|
||||
case .rpcEvent(let params): self?.rpcEventAction(params: params)
|
||||
case let .rpcEvent(params): self?.rpcEventAction(params: params)
|
||||
|
||||
case .rpcEventSubscribed: break
|
||||
|
||||
}
|
||||
}, onError: { error in
|
||||
// FIXME call onError
|
||||
// FIXME: call onError
|
||||
self.log.error(error)
|
||||
})
|
||||
.disposed(by: self.disposeBag)
|
||||
@ -318,7 +325,8 @@ class MainWindow: NSObject,
|
||||
}
|
||||
|
||||
if state.viewToBeFocused != nil,
|
||||
case .neoVimView = state.viewToBeFocused! {
|
||||
case .neoVimView = state.viewToBeFocused!
|
||||
{
|
||||
self.window.makeFirstResponder(self.neoVimView)
|
||||
}
|
||||
|
||||
@ -331,11 +339,11 @@ class MainWindow: NSObject,
|
||||
Completable
|
||||
.empty()
|
||||
.andThen {
|
||||
if state.preview.status == .markdown
|
||||
&& state.previewTool.isReverseSearchAutomatically
|
||||
&& state.preview.previewPosition
|
||||
.hasDifferentMark(as: self.previewPosition) {
|
||||
|
||||
if state.preview.status == .markdown,
|
||||
state.previewTool.isReverseSearchAutomatically,
|
||||
state.preview.previewPosition
|
||||
.hasDifferentMark(as: self.previewPosition)
|
||||
{
|
||||
self.previewPosition = state.preview.previewPosition
|
||||
return self.neoVimView.cursorGo(
|
||||
to: state.preview.previewPosition.payload
|
||||
@ -389,7 +397,8 @@ class MainWindow: NSObject,
|
||||
self.unthemeTitlebar(dueFullScreen: false)
|
||||
self.window.backgroundColor = .windowBackgroundColor
|
||||
self.workspace.theme = .default
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
self.usesTheme = state.appearance.usesTheme
|
||||
self.currentBuffer = state.currentBuffer
|
||||
@ -404,7 +413,8 @@ class MainWindow: NSObject,
|
||||
self.neoVimView.isRightOptionMeta = state.isRightOptionMeta
|
||||
|
||||
if self.neoVimView.trackpadScrollResistance
|
||||
!= state.trackpadScrollResistance.cgf {
|
||||
!= state.trackpadScrollResistance.cgf
|
||||
{
|
||||
self.neoVimView.trackpadScrollResistance = CGFloat(
|
||||
state.trackpadScrollResistance
|
||||
)
|
||||
@ -420,9 +430,10 @@ class MainWindow: NSObject,
|
||||
}
|
||||
|
||||
if self.defaultFont != state.appearance.font
|
||||
|| self.linespacing != state.appearance.linespacing
|
||||
|| self.characterspacing != state.appearance.characterspacing
|
||||
|| self.usesLigatures != state.appearance.usesLigatures {
|
||||
|| self.linespacing != state.appearance.linespacing
|
||||
|| self.characterspacing != state.appearance.characterspacing
|
||||
|| self.usesLigatures != state.appearance.usesLigatures
|
||||
{
|
||||
self.defaultFont = state.appearance.font
|
||||
self.linespacing = state.appearance.linespacing
|
||||
self.characterspacing = state.appearance.characterspacing
|
||||
@ -506,10 +517,10 @@ class MainWindow: NSObject,
|
||||
notification.identifier = UUID().uuidString
|
||||
notification.title = "Error during initialization"
|
||||
notification.informativeText =
|
||||
"""
|
||||
There was an error during the initialization of NeoVim.
|
||||
Use :messages to view the error messages.
|
||||
"""
|
||||
"""
|
||||
There was an error during the initialization of NeoVim.
|
||||
Use :messages to view the error messages.
|
||||
"""
|
||||
|
||||
NSUserNotificationCenter.default.deliver(notification)
|
||||
}
|
||||
|
@ -6,7 +6,6 @@
|
||||
import Foundation
|
||||
|
||||
class MainWindowReducer: ReducerType {
|
||||
|
||||
typealias StateType = MainWindow.State
|
||||
typealias ActionType = UuidAction<MainWindow.Action>
|
||||
|
||||
@ -14,11 +13,10 @@ class MainWindowReducer: ReducerType {
|
||||
var state = tuple.state
|
||||
|
||||
switch tuple.action.payload {
|
||||
|
||||
case let .frameChanged(to:frame):
|
||||
case let .frameChanged(to: frame):
|
||||
state.frame = frame
|
||||
|
||||
case let .cd(to:cwd):
|
||||
case let .cd(to: cwd):
|
||||
if state.cwd != cwd {
|
||||
state.cwd = cwd
|
||||
}
|
||||
@ -41,9 +39,11 @@ class MainWindowReducer: ReducerType {
|
||||
state.viewToBeFocused = view
|
||||
|
||||
case let .setState(for: tool, with: workspaceTool):
|
||||
state.tools[tool] = WorkspaceToolState(location: workspaceTool.location,
|
||||
dimension: workspaceTool.dimension,
|
||||
open: workspaceTool.isSelected)
|
||||
state.tools[tool] = WorkspaceToolState(
|
||||
location: workspaceTool.location,
|
||||
dimension: workspaceTool.dimension,
|
||||
open: workspaceTool.isSelected
|
||||
)
|
||||
if workspaceTool.isSelected {
|
||||
state.tools
|
||||
.filter { $0 != tool && $1.location == workspaceTool.location }
|
||||
@ -56,9 +56,11 @@ class MainWindowReducer: ReducerType {
|
||||
let toolId = toolPair.0
|
||||
let tool = toolPair.1
|
||||
|
||||
state.tools[toolId] = WorkspaceToolState(location: tool.location,
|
||||
dimension: tool.dimension,
|
||||
open: tool.isSelected)
|
||||
state.tools[toolId] = WorkspaceToolState(
|
||||
location: tool.location,
|
||||
dimension: tool.dimension,
|
||||
open: tool.isSelected
|
||||
)
|
||||
|
||||
if tool.isSelected {
|
||||
state.tools
|
||||
@ -83,7 +85,6 @@ class MainWindowReducer: ReducerType {
|
||||
|
||||
default:
|
||||
return tuple
|
||||
|
||||
}
|
||||
|
||||
return (state, tuple.action, true)
|
||||
|
@ -3,12 +3,11 @@
|
||||
* See LICENSE
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import Down
|
||||
import Foundation
|
||||
import os
|
||||
|
||||
class MarkdownPreviewMiddleware {
|
||||
|
||||
let markdownTool: MarkdownToolMiddleware
|
||||
let mainWindow: MainWindowMiddleware
|
||||
|
||||
@ -19,7 +18,6 @@ class MarkdownPreviewMiddleware {
|
||||
}
|
||||
|
||||
class PreviewGenerator {
|
||||
|
||||
init() {
|
||||
// We know that the files are there!
|
||||
self.template = try! String(contentsOf: Resources.markdownTemplateUrl)
|
||||
@ -39,10 +37,9 @@ class MarkdownPreviewMiddleware {
|
||||
}
|
||||
|
||||
self.removePreviewHtmlFile(uuid: uuid)
|
||||
guard let htmlUrl = preview.html else {return }
|
||||
guard let htmlUrl = preview.html else { return }
|
||||
|
||||
switch preview.status {
|
||||
|
||||
case .none:
|
||||
self.writePage(html: self.emptyHtml, uuid: uuid, url: htmlUrl)
|
||||
|
||||
@ -63,7 +60,6 @@ class MarkdownPreviewMiddleware {
|
||||
self.log.error("error while rendering \(buffer) to \(htmlUrl): \(error)")
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,15 +97,17 @@ class MarkdownPreviewMiddleware {
|
||||
private let template: String
|
||||
private var previewFiles = [UUID: URL]()
|
||||
|
||||
private let log = OSLog(subsystem: Defs.loggerSubsystem,
|
||||
category: Defs.LoggerCategory.middleware)
|
||||
private let log = OSLog(
|
||||
subsystem: Defs.loggerSubsystem,
|
||||
category: Defs.LoggerCategory.middleware
|
||||
)
|
||||
|
||||
private func render(_ bufferUrl: URL, to htmlUrl: URL) throws {
|
||||
let md = try String(contentsOf: bufferUrl)
|
||||
let down = Down(markdownString: md)
|
||||
let body = try down.toHTML(DownOptions.sourcePos)
|
||||
|
||||
let html = filledTemplate(body: body, title: bufferUrl.lastPathComponent)
|
||||
let html = self.filledTemplate(body: body, title: bufferUrl.lastPathComponent)
|
||||
let htmlFilePath = htmlUrl.path
|
||||
|
||||
try html.write(toFile: htmlFilePath, atomically: true, encoding: .utf8)
|
||||
@ -128,7 +126,6 @@ class MarkdownPreviewMiddleware {
|
||||
}
|
||||
|
||||
class MarkdownToolMiddleware: MiddlewareType {
|
||||
|
||||
typealias StateType = MainWindow.State
|
||||
typealias ActionType = UuidAction<MarkdownTool.Action>
|
||||
|
||||
@ -150,7 +147,6 @@ class MarkdownPreviewMiddleware {
|
||||
}
|
||||
|
||||
class MainWindowMiddleware: MiddlewareType {
|
||||
|
||||
typealias StateType = MainWindow.State
|
||||
typealias ActionType = UuidAction<MainWindow.Action>
|
||||
|
||||
@ -162,14 +158,12 @@ class MarkdownPreviewMiddleware {
|
||||
|
||||
let uuidAction = tuple.action
|
||||
switch uuidAction.payload {
|
||||
|
||||
case .newCurrentBuffer: fallthrough
|
||||
case .bufferWritten: fallthrough
|
||||
case .setTheme:
|
||||
self.generator.apply(result.state, uuid: uuidAction.uuid)
|
||||
|
||||
default: return result
|
||||
|
||||
}
|
||||
|
||||
return result
|
||||
|
@ -4,12 +4,11 @@
|
||||
*/
|
||||
|
||||
import Cocoa
|
||||
import NvimView
|
||||
import Commons
|
||||
import NvimView
|
||||
|
||||
class MarkdownPreviewReducer {
|
||||
|
||||
static private func previewState(
|
||||
private static func previewState(
|
||||
for uuid: UUID,
|
||||
baseUrl: URL,
|
||||
buffer: NvimView.Buffer?,
|
||||
@ -58,7 +57,6 @@ class MarkdownPreviewReducer {
|
||||
}
|
||||
|
||||
class PreviewToolReducer: ReducerType {
|
||||
|
||||
typealias StateType = MainWindow.State
|
||||
typealias ActionType = UuidAction<MarkdownTool.Action>
|
||||
|
||||
@ -66,7 +64,6 @@ class MarkdownPreviewReducer {
|
||||
var state = tuple.state
|
||||
|
||||
switch tuple.action.payload {
|
||||
|
||||
case .refreshNow:
|
||||
state.preview = MarkdownPreviewReducer.previewState(
|
||||
for: tuple.state.uuid,
|
||||
@ -77,20 +74,25 @@ class MarkdownPreviewReducer {
|
||||
)
|
||||
state.preview.lastSearch = .reload
|
||||
|
||||
case let .reverseSearch(to:position):
|
||||
case let .reverseSearch(to: position):
|
||||
state.preview.previewPosition = Marked(position)
|
||||
state.preview.lastSearch = .reverse
|
||||
|
||||
case let .scroll(to:position):
|
||||
case let .scroll(to: position):
|
||||
if state.preview.lastSearch == .reload {
|
||||
state.preview.lastSearch = .none
|
||||
break;
|
||||
break
|
||||
}
|
||||
|
||||
guard state.previewTool.isReverseSearchAutomatically && state.preview.lastSearch != .forward else {
|
||||
guard state.previewTool.isReverseSearchAutomatically,
|
||||
state.preview.lastSearch != .forward
|
||||
else {
|
||||
state.preview.lastSearch = .none
|
||||
state.preview.previewPosition = Marked(mark: state.preview.previewPosition.mark, payload: position)
|
||||
break;
|
||||
state.preview.previewPosition = Marked(
|
||||
mark: state.preview.previewPosition.mark,
|
||||
payload: position
|
||||
)
|
||||
break
|
||||
}
|
||||
|
||||
state.preview.previewPosition = Marked(position)
|
||||
@ -98,7 +100,6 @@ class MarkdownPreviewReducer {
|
||||
|
||||
default:
|
||||
return tuple
|
||||
|
||||
}
|
||||
|
||||
return (state, tuple.action, true)
|
||||
@ -110,7 +111,6 @@ class MarkdownPreviewReducer {
|
||||
}
|
||||
|
||||
class BuffersListReducer: ReducerType {
|
||||
|
||||
typealias StateType = MainWindow.State
|
||||
typealias ActionType = UuidAction<BuffersList.Action>
|
||||
|
||||
@ -118,7 +118,6 @@ class MarkdownPreviewReducer {
|
||||
var state = tuple.state
|
||||
|
||||
switch tuple.action.payload {
|
||||
|
||||
case let .open(buffer):
|
||||
state.preview = MarkdownPreviewReducer.previewState(
|
||||
for: tuple.state.uuid,
|
||||
@ -128,7 +127,6 @@ class MarkdownPreviewReducer {
|
||||
previewPosition: Marked(.beginning)
|
||||
)
|
||||
state.preview.lastSearch = .none
|
||||
|
||||
}
|
||||
|
||||
return (state, tuple.action, true)
|
||||
@ -140,7 +138,6 @@ class MarkdownPreviewReducer {
|
||||
}
|
||||
|
||||
class MainWindowReducer: ReducerType {
|
||||
|
||||
typealias StateType = MainWindow.State
|
||||
typealias ActionType = UuidAction<MainWindow.Action>
|
||||
|
||||
@ -148,7 +145,6 @@ class MarkdownPreviewReducer {
|
||||
var state = tuple.state
|
||||
|
||||
switch tuple.action.payload {
|
||||
|
||||
case let .newCurrentBuffer(buffer):
|
||||
state.preview = MarkdownPreviewReducer.previewState(
|
||||
for: tuple.state.uuid,
|
||||
@ -169,14 +165,15 @@ class MarkdownPreviewReducer {
|
||||
)
|
||||
state.preview.lastSearch = .reload
|
||||
|
||||
case let .setCursor(to:position):
|
||||
case let .setCursor(to: position):
|
||||
if state.preview.lastSearch == .reload {
|
||||
state.preview.lastSearch = .none
|
||||
break
|
||||
}
|
||||
|
||||
guard state.previewTool.isForwardSearchAutomatically,
|
||||
state.preview.lastSearch != .reverse else {
|
||||
state.preview.lastSearch != .reverse
|
||||
else {
|
||||
state.preview.editorPosition = Marked(
|
||||
mark: state.preview.editorPosition.mark,
|
||||
payload: position.payload
|
||||
|
@ -4,16 +4,14 @@
|
||||
*/
|
||||
|
||||
import Cocoa
|
||||
import RxSwift
|
||||
import PureLayout
|
||||
import WebKit
|
||||
import os
|
||||
import NvimView
|
||||
import os
|
||||
import PureLayout
|
||||
import RxSwift
|
||||
import WebKit
|
||||
|
||||
class MarkdownTool: NSView, UiComponent, WKNavigationDelegate {
|
||||
|
||||
enum Action {
|
||||
|
||||
case refreshNow
|
||||
case reverseSearch(to: Position)
|
||||
|
||||
@ -84,7 +82,8 @@ class MarkdownTool: NSView, UiComponent, WKNavigationDelegate {
|
||||
.observeOn(MainScheduler.instance)
|
||||
.subscribe(onNext: { state in
|
||||
if state.viewToBeFocused != nil,
|
||||
case .markdownPreview = state.viewToBeFocused! {
|
||||
case .markdownPreview = state.viewToBeFocused!
|
||||
{
|
||||
self.beFirstResponder()
|
||||
}
|
||||
|
||||
@ -92,9 +91,10 @@ class MarkdownTool: NSView, UiComponent, WKNavigationDelegate {
|
||||
self.automaticReverseMenuItem.boolState = state.previewTool.isReverseSearchAutomatically
|
||||
self.refreshOnWriteMenuItem.boolState = state.previewTool.isRefreshOnWrite
|
||||
|
||||
if state.preview.status == .markdown
|
||||
&& state.previewTool.isForwardSearchAutomatically
|
||||
&& state.preview.editorPosition.hasDifferentMark(as: self.editorPosition) {
|
||||
if state.preview.status == .markdown,
|
||||
state.previewTool.isForwardSearchAutomatically,
|
||||
state.preview.editorPosition.hasDifferentMark(as: self.editorPosition)
|
||||
{
|
||||
self.forwardSearch(position: state.preview.editorPosition.payload)
|
||||
}
|
||||
|
||||
@ -133,18 +133,23 @@ class MarkdownTool: NSView, UiComponent, WKNavigationDelegate {
|
||||
|
||||
private func addViews() {
|
||||
self.webview.navigationDelegate = self
|
||||
self.userContentController.add(webviewMessageHandler, name: "com_vimr_tools_preview_markdown")
|
||||
self.userContentController.add(
|
||||
self.webviewMessageHandler,
|
||||
name: "com_vimr_tools_preview_markdown"
|
||||
)
|
||||
self.webview.configureForAutoLayout()
|
||||
|
||||
self.addSubview(self.webview)
|
||||
self.webview.autoPinEdgesToSuperviewEdges()
|
||||
}
|
||||
|
||||
func webView(_: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
|
||||
func webView(_: WKWebView, didFailProvisionalNavigation _: WKNavigation!,
|
||||
withError error: Error)
|
||||
{
|
||||
self.log.error("ERROR preview component's webview: \(error)")
|
||||
}
|
||||
|
||||
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
||||
func webView(_: WKWebView, didFinish _: WKNavigation!) {
|
||||
self.webview.evaluateJavaScript("document.body.scrollTop = \(self.scrollTop)")
|
||||
}
|
||||
|
||||
@ -165,33 +170,45 @@ class MarkdownTool: NSView, UiComponent, WKNavigationDelegate {
|
||||
private let userContentController = WKUserContentController()
|
||||
private let webviewMessageHandler = WebviewMessageHandler()
|
||||
|
||||
private let automaticForwardMenuItem = NSMenuItem(title: "Automatic Forward Search",
|
||||
action: nil,
|
||||
keyEquivalent: "")
|
||||
private let automaticReverseMenuItem = NSMenuItem(title: "Automatic Reverse Search",
|
||||
action: nil,
|
||||
keyEquivalent: "")
|
||||
private let refreshOnWriteMenuItem = NSMenuItem(title: "Refresh on Write", action: nil, keyEquivalent: "")
|
||||
private let automaticForwardMenuItem = NSMenuItem(
|
||||
title: "Automatic Forward Search",
|
||||
action: nil,
|
||||
keyEquivalent: ""
|
||||
)
|
||||
private let automaticReverseMenuItem = NSMenuItem(
|
||||
title: "Automatic Reverse Search",
|
||||
action: nil,
|
||||
keyEquivalent: ""
|
||||
)
|
||||
private let refreshOnWriteMenuItem = NSMenuItem(
|
||||
title: "Refresh on Write",
|
||||
action: nil,
|
||||
keyEquivalent: ""
|
||||
)
|
||||
|
||||
private let log = OSLog(subsystem: Defs.loggerSubsystem,
|
||||
category: Defs.LoggerCategory.ui)
|
||||
private let log = OSLog(
|
||||
subsystem: Defs.loggerSubsystem,
|
||||
category: Defs.LoggerCategory.ui
|
||||
)
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
@available(*, unavailable)
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
private func forwardSearch(position: Position) {
|
||||
self.webview.evaluateJavaScript("scrollToPosition(\(position.row), \(position.column));") { result, error in
|
||||
if let scrollTop = result as? Int {
|
||||
self.scrollTop = scrollTop
|
||||
self.webview
|
||||
.evaluateJavaScript("scrollToPosition(\(position.row), \(position.column));") { result, _ in
|
||||
if let scrollTop = result as? Int {
|
||||
self.scrollTop = scrollTop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
extension MarkdownTool {
|
||||
|
||||
extension MarkdownTool {
|
||||
@objc func refreshNowAction(_: Any?) {
|
||||
self.emit(UuidAction(uuid: self.uuid, action: .refreshNow))
|
||||
}
|
||||
@ -205,11 +222,13 @@ extension MarkdownTool {
|
||||
}
|
||||
|
||||
@objc func automaticForwardSearchAction(_ sender: NSMenuItem) {
|
||||
self.emit(UuidAction(uuid: self.uuid, action: .setAutomaticForwardSearch(to: !sender.boolState)))
|
||||
self
|
||||
.emit(UuidAction(uuid: self.uuid, action: .setAutomaticForwardSearch(to: !sender.boolState)))
|
||||
}
|
||||
|
||||
@objc func automaticReverseSearchAction(_ sender: NSMenuItem) {
|
||||
self.emit(UuidAction(uuid: self.uuid, action: .setAutomaticReverseSearch(to: !sender.boolState)))
|
||||
self
|
||||
.emit(UuidAction(uuid: self.uuid, action: .setAutomaticReverseSearch(to: !sender.boolState)))
|
||||
}
|
||||
|
||||
@objc func refreshOnWriteAction(_ sender: NSMenuItem) {
|
||||
@ -218,9 +237,8 @@ extension MarkdownTool {
|
||||
}
|
||||
|
||||
private class WebviewMessageHandler: NSObject, WKScriptMessageHandler {
|
||||
|
||||
var source: Observable<(Position, Int)> {
|
||||
return self.subject.asObservable()
|
||||
self.subject.asObservable()
|
||||
}
|
||||
|
||||
deinit {
|
||||
@ -235,7 +253,7 @@ private class WebviewMessageHandler: NSObject, WKScriptMessageHandler {
|
||||
guard let lineBegin = msgBody["lineBegin"],
|
||||
let columnBegin = msgBody["columnBegin"],
|
||||
let scrollTop = msgBody["scrollTop"]
|
||||
else {
|
||||
else {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,6 @@
|
||||
import Foundation
|
||||
|
||||
class MarkdownToolReducer: ReducerType {
|
||||
|
||||
typealias StateType = MainWindow.State
|
||||
typealias ActionType = UuidAction<MarkdownTool.Action>
|
||||
|
||||
@ -18,19 +17,17 @@ class MarkdownToolReducer: ReducerType {
|
||||
var state = tuple.state
|
||||
|
||||
switch tuple.action.payload {
|
||||
|
||||
case let .setAutomaticReverseSearch(to:value):
|
||||
case let .setAutomaticReverseSearch(to: value):
|
||||
state.previewTool.isReverseSearchAutomatically = value
|
||||
|
||||
case let .setAutomaticForwardSearch(to:value):
|
||||
case let .setAutomaticForwardSearch(to: value):
|
||||
state.previewTool.isForwardSearchAutomatically = value
|
||||
|
||||
case let .setRefreshOnWrite(to:value):
|
||||
case let .setRefreshOnWrite(to: value):
|
||||
state.previewTool.isRefreshOnWrite = value
|
||||
|
||||
default:
|
||||
return tuple
|
||||
|
||||
}
|
||||
|
||||
return (state, tuple.action, true)
|
||||
|
@ -6,7 +6,6 @@
|
||||
import Cocoa
|
||||
|
||||
class OpenQuicklyFileViewRow: NSTableRowView {
|
||||
|
||||
override func drawSelection(in dirtyRect: NSRect) {
|
||||
if self.isSelected {
|
||||
NSColor.selectedControlColor.set()
|
||||
|
@ -7,7 +7,6 @@ import Foundation
|
||||
import RxSwift
|
||||
|
||||
class OpenQuicklyReducer: ReducerType {
|
||||
|
||||
typealias StateType = AppState
|
||||
typealias ActionType = OpenQuicklyWindow.Action
|
||||
|
||||
@ -17,7 +16,6 @@ class OpenQuicklyReducer: ReducerType {
|
||||
var appState = pair.state
|
||||
|
||||
switch pair.action {
|
||||
|
||||
case let .setUsesVcsIgnores(usesVcsIgnores):
|
||||
guard let uuid = appState.currentMainWindowUuid else { return pair }
|
||||
appState.mainWindows[uuid]?.usesVcsIgnores = usesVcsIgnores
|
||||
@ -29,21 +27,17 @@ class OpenQuicklyReducer: ReducerType {
|
||||
|
||||
case .close:
|
||||
appState.openQuickly.open = false
|
||||
break
|
||||
|
||||
}
|
||||
|
||||
return (appState, pair.action, true)
|
||||
}
|
||||
|
||||
class MainWindowReducer: ReducerType {
|
||||
|
||||
typealias StateType = AppState
|
||||
typealias ActionType = UuidAction<MainWindow.Action>
|
||||
|
||||
func typedReduce(_ pair: ReduceTuple) -> ReduceTuple {
|
||||
switch pair.action.payload {
|
||||
|
||||
case .openQuickly:
|
||||
var appState = pair.state
|
||||
|
||||
@ -56,7 +50,6 @@ class OpenQuicklyReducer: ReducerType {
|
||||
|
||||
default:
|
||||
return pair
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,28 +4,27 @@
|
||||
*/
|
||||
|
||||
import Cocoa
|
||||
import RxSwift
|
||||
import RxCocoa
|
||||
import PureLayout
|
||||
import os
|
||||
import Commons
|
||||
import os
|
||||
import PureLayout
|
||||
import RxCocoa
|
||||
import RxSwift
|
||||
|
||||
class OpenQuicklyWindow: NSObject,
|
||||
UiComponent,
|
||||
NSWindowDelegate,
|
||||
NSTextFieldDelegate,
|
||||
NSTableViewDelegate {
|
||||
|
||||
UiComponent,
|
||||
NSWindowDelegate,
|
||||
NSTextFieldDelegate,
|
||||
NSTableViewDelegate
|
||||
{
|
||||
typealias StateType = AppState
|
||||
|
||||
enum Action {
|
||||
|
||||
case setUsesVcsIgnores(Bool)
|
||||
case open(URL)
|
||||
case close
|
||||
}
|
||||
|
||||
@objc dynamic private(set) var unsortedScoredUrls = [ScoredUrl]()
|
||||
@objc private(set) dynamic var unsortedScoredUrls = [ScoredUrl]()
|
||||
|
||||
// Call this only when quitting
|
||||
func cleanUp() {
|
||||
@ -62,6 +61,7 @@ class OpenQuicklyWindow: NSObject,
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private let emit: (Action) -> Void
|
||||
private let disposeBag = DisposeBag()
|
||||
|
||||
@ -83,8 +83,10 @@ class OpenQuicklyWindow: NSObject,
|
||||
private let cwdControl = NSPathControl(forAutoLayout: ())
|
||||
private let fileView = NSTableView.standardTableView()
|
||||
|
||||
private let log = OSLog(subsystem: Defs.loggerSubsystem,
|
||||
category: Defs.LoggerCategory.ui)
|
||||
private let log = OSLog(
|
||||
subsystem: Defs.loggerSubsystem,
|
||||
category: Defs.LoggerCategory.ui
|
||||
)
|
||||
|
||||
private var window: NSWindow { self.windowController.window! }
|
||||
|
||||
@ -108,7 +110,7 @@ class OpenQuicklyWindow: NSObject,
|
||||
let windowIsOpen = self.window.isKeyWindow
|
||||
|
||||
// The window is open and the user changed the setting
|
||||
if self.usesVcsIgnores != curWinState.usesVcsIgnores && windowIsOpen {
|
||||
if self.usesVcsIgnores != curWinState.usesVcsIgnores, windowIsOpen {
|
||||
self.usesVcsIgnores = curWinState.usesVcsIgnores
|
||||
self.useVcsIgnoresCheckBox.boolState = curWinState.usesVcsIgnores
|
||||
|
||||
@ -190,7 +192,7 @@ class OpenQuicklyWindow: NSObject,
|
||||
}
|
||||
|
||||
private func endProgress() {
|
||||
DispatchQueue.main.async {self.progressIndicator.stopAnimation(self) }
|
||||
DispatchQueue.main.async { self.progressIndicator.stopAnimation(self) }
|
||||
}
|
||||
|
||||
private func updateRootUrls(state: AppState) {
|
||||
@ -298,10 +300,10 @@ class OpenQuicklyWindow: NSObject,
|
||||
}
|
||||
|
||||
// MARK: - NSTableViewDelegate
|
||||
extension OpenQuicklyWindow {
|
||||
|
||||
func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? {
|
||||
return OpenQuicklyFileViewRow()
|
||||
extension OpenQuicklyWindow {
|
||||
func tableView(_: NSTableView, rowViewForRow _: Int) -> NSTableRowView? {
|
||||
OpenQuicklyFileViewRow()
|
||||
}
|
||||
|
||||
func tableView(_ tableView: NSTableView, viewFor _: NSTableColumn?, row: Int) -> NSView? {
|
||||
@ -313,7 +315,7 @@ extension OpenQuicklyWindow {
|
||||
)?.reset()
|
||||
let cell = cachedCell ?? ImageAndTextTableCell(withIdentifier: "file-view-row")
|
||||
|
||||
guard let sortedUrls = self.scoredUrlsController.arrangedObjects as? Array<ScoredUrl> else {
|
||||
guard let sortedUrls = self.scoredUrlsController.arrangedObjects as? [ScoredUrl] else {
|
||||
self.log.error("Could not convert arranged objects to [ScoredUrl].")
|
||||
return nil
|
||||
}
|
||||
@ -351,27 +353,26 @@ extension OpenQuicklyWindow {
|
||||
}
|
||||
|
||||
// MARK: - NSTextFieldDelegate
|
||||
extension OpenQuicklyWindow {
|
||||
|
||||
extension OpenQuicklyWindow {
|
||||
func control(
|
||||
_ control: NSControl,
|
||||
textView: NSTextView,
|
||||
_: NSControl,
|
||||
textView _: NSTextView,
|
||||
doCommandBy commandSelector: Selector
|
||||
) -> Bool {
|
||||
switch commandSelector {
|
||||
|
||||
case NSSelectorFromString("cancelOperation:"):
|
||||
self.window.performClose(self)
|
||||
return true
|
||||
|
||||
case NSSelectorFromString("insertNewline:"):
|
||||
guard let sortedUrls = self.scoredUrlsController.arrangedObjects as? Array<ScoredUrl> else {
|
||||
guard let sortedUrls = self.scoredUrlsController.arrangedObjects as? [ScoredUrl] else {
|
||||
self.log.error("Could not convert arranged objects to [ScoredUrl].")
|
||||
return true
|
||||
}
|
||||
|
||||
let selectedRow = self.fileView.selectedRow
|
||||
guard selectedRow >= 0 && selectedRow < sortedUrls.count else { return false }
|
||||
guard selectedRow >= 0, selectedRow < sortedUrls.count else { return false }
|
||||
|
||||
self.emit(.open(sortedUrls[selectedRow].url))
|
||||
self.window.performClose(self)
|
||||
@ -387,7 +388,6 @@ extension OpenQuicklyWindow {
|
||||
|
||||
default:
|
||||
return false
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -410,8 +410,8 @@ extension OpenQuicklyWindow {
|
||||
}
|
||||
|
||||
// MARK: - NSWindowDelegate
|
||||
extension OpenQuicklyWindow {
|
||||
|
||||
extension OpenQuicklyWindow {
|
||||
func windowShouldClose(_: NSWindow) -> Bool {
|
||||
self.emit(.close)
|
||||
|
||||
|
@ -8,7 +8,6 @@ import DictionaryCoding
|
||||
import os
|
||||
|
||||
class PrefMiddleware: MiddlewareType {
|
||||
|
||||
typealias StateType = AppState
|
||||
typealias ActionType = AnyAction
|
||||
|
||||
@ -27,7 +26,7 @@ class PrefMiddleware: MiddlewareType {
|
||||
}
|
||||
|
||||
func typedApply(_ reduce: @escaping TypedActionReduceFunction) -> TypedActionReduceFunction {
|
||||
return { tuple in
|
||||
{ tuple in
|
||||
let result = reduce(tuple)
|
||||
|
||||
guard result.modified else {
|
||||
@ -52,7 +51,7 @@ class PrefMiddleware: MiddlewareType {
|
||||
notification.identifier = UUID().uuidString
|
||||
notification.title = "No monospaced font"
|
||||
notification.informativeText = "The font you selected\(newFontNameText) does not seem "
|
||||
+ "to be a monospaced font. The rendering will most likely be broken."
|
||||
+ "to be a monospaced font. The rendering will most likely be broken."
|
||||
NSUserNotificationCenter.default.deliver(notification)
|
||||
}
|
||||
}
|
||||
@ -73,12 +72,11 @@ class PrefMiddleware: MiddlewareType {
|
||||
private var currentFont = NSFont.userFixedPitchFont(ofSize: 13)!
|
||||
|
||||
class MainWindowMiddleware: MiddlewareType {
|
||||
|
||||
typealias StateType = AppState
|
||||
typealias ActionType = UuidAction<MainWindow.Action>
|
||||
|
||||
func typedApply(_ reduce: @escaping TypedActionReduceFunction) -> TypedActionReduceFunction {
|
||||
return { tuple in
|
||||
{ tuple in
|
||||
let result = reduce(tuple)
|
||||
|
||||
guard case .close = tuple.action.payload else {
|
||||
@ -96,8 +94,10 @@ class PrefMiddleware: MiddlewareType {
|
||||
}
|
||||
}
|
||||
|
||||
private let log = OSLog(subsystem: Defs.loggerSubsystem,
|
||||
category: Defs.LoggerCategory.middleware)
|
||||
private let log = OSLog(
|
||||
subsystem: Defs.loggerSubsystem,
|
||||
category: Defs.LoggerCategory.middleware
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,10 +6,9 @@
|
||||
import Cocoa
|
||||
|
||||
class PrefPane: NSView {
|
||||
|
||||
// Return true to place this to the upper left corner when the scroll view is bigger than this view.
|
||||
override var isFlipped: Bool {
|
||||
return true
|
||||
true
|
||||
}
|
||||
|
||||
var displayName: String {
|
||||
@ -17,7 +16,7 @@ class PrefPane: NSView {
|
||||
}
|
||||
|
||||
var pinToContainer: Bool {
|
||||
return false
|
||||
false
|
||||
}
|
||||
|
||||
func paneWillAppear() {
|
||||
@ -30,19 +29,19 @@ class PrefPane: NSView {
|
||||
}
|
||||
|
||||
// MARK: - Control Utils
|
||||
extension PrefPane {
|
||||
|
||||
extension PrefPane {
|
||||
func paneTitleTextField(title: String) -> NSTextField {
|
||||
let field = NSTextField.defaultTitleTextField()
|
||||
field.font = NSFont.boldSystemFont(ofSize: 16)
|
||||
field.alignment = .left;
|
||||
field.alignment = .left
|
||||
field.stringValue = title
|
||||
return field
|
||||
}
|
||||
|
||||
func titleTextField(title: String) -> NSTextField {
|
||||
let field = NSTextField.defaultTitleTextField()
|
||||
field.alignment = .right;
|
||||
field.alignment = .right
|
||||
field.stringValue = title
|
||||
return field
|
||||
}
|
||||
|
@ -7,21 +7,22 @@ import Cocoa
|
||||
import NvimView
|
||||
|
||||
class PrefUtils {
|
||||
|
||||
static func value<T>(from dict: [String: Any], for key: String) -> T? {
|
||||
return dict[key] as? T
|
||||
dict[key] as? T
|
||||
}
|
||||
|
||||
static func value<T>(from dict: [String: Any], for key: String, default defaultValue: T) -> T {
|
||||
return dict[key] as? T ?? defaultValue
|
||||
dict[key] as? T ?? defaultValue
|
||||
}
|
||||
|
||||
static func dict(from dict: [String: Any], for key: String) -> [String: Any]? {
|
||||
return dict[key] as? [String: Any]
|
||||
dict[key] as? [String: Any]
|
||||
}
|
||||
|
||||
static func float(from dict: [String: Any], for key: String, default defaultValue: Float) -> Float {
|
||||
return (dict[key] as? NSNumber)?.floatValue ?? defaultValue
|
||||
static func float(from dict: [String: Any], for key: String,
|
||||
default defaultValue: Float) -> Float
|
||||
{
|
||||
(dict[key] as? NSNumber)?.floatValue ?? defaultValue
|
||||
}
|
||||
|
||||
static func float(from dict: [String: Any], for key: String) -> Float? {
|
||||
@ -41,15 +42,17 @@ class PrefUtils {
|
||||
}
|
||||
|
||||
static func bool(from dict: [String: Any], for key: String, default defaultValue: Bool) -> Bool {
|
||||
return (dict[key] as? NSNumber)?.boolValue ?? defaultValue
|
||||
(dict[key] as? NSNumber)?.boolValue ?? defaultValue
|
||||
}
|
||||
|
||||
static func string(from dict: [String: Any], for key: String) -> String? {
|
||||
return dict[key] as? String
|
||||
dict[key] as? String
|
||||
}
|
||||
|
||||
static func string(from dict: [String: Any], for key: String, default defaultValue: String) -> String {
|
||||
return dict[key] as? String ?? defaultValue
|
||||
static func string(from dict: [String: Any], for key: String,
|
||||
default defaultValue: String) -> String
|
||||
{
|
||||
dict[key] as? String ?? defaultValue
|
||||
}
|
||||
|
||||
static func saneFont(_ fontName: String, fontSize: CGFloat) -> NSFont {
|
||||
@ -66,19 +69,19 @@ class PrefUtils {
|
||||
|
||||
static func saneLinespacing(_ fLinespacing: Float) -> CGFloat {
|
||||
let linespacing = fLinespacing.cgf
|
||||
guard linespacing >= NvimView.minLinespacing && linespacing <= NvimView.maxLinespacing else {
|
||||
guard linespacing >= NvimView.minLinespacing, linespacing <= NvimView.maxLinespacing else {
|
||||
return NvimView.defaultLinespacing
|
||||
}
|
||||
|
||||
return linespacing
|
||||
}
|
||||
|
||||
|
||||
static func saneCharacterspacing(_ fCharacterspacing: Float) -> CGFloat {
|
||||
let characterspacing = fCharacterspacing.cgf
|
||||
guard characterspacing >= 0.0 else {
|
||||
return NvimView.defaultCharacterspacing
|
||||
}
|
||||
|
||||
|
||||
return characterspacing
|
||||
}
|
||||
}
|
||||
|
@ -4,18 +4,17 @@
|
||||
*/
|
||||
|
||||
import Cocoa
|
||||
import RxSwift
|
||||
import PureLayout
|
||||
import RxSwift
|
||||
|
||||
class PrefWindow: NSObject,
|
||||
UiComponent,
|
||||
NSWindowDelegate,
|
||||
NSTableViewDataSource, NSTableViewDelegate {
|
||||
|
||||
UiComponent,
|
||||
NSWindowDelegate,
|
||||
NSTableViewDataSource, NSTableViewDelegate
|
||||
{
|
||||
typealias StateType = AppState
|
||||
|
||||
enum Action {
|
||||
|
||||
case close
|
||||
}
|
||||
|
||||
@ -66,7 +65,7 @@ class PrefWindow: NSObject,
|
||||
|
||||
private let windowController: NSWindowController
|
||||
private var window: NSWindow {
|
||||
return self.windowController.window!
|
||||
self.windowController.window!
|
||||
}
|
||||
|
||||
private let categoryView = NSTableView.standardSourceListTableView()
|
||||
@ -76,7 +75,7 @@ class PrefWindow: NSObject,
|
||||
private let panes: [PrefPane]
|
||||
private var currentPane: PrefPane {
|
||||
get {
|
||||
return self.paneContainer.documentView as! PrefPane
|
||||
self.paneContainer.documentView as! PrefPane
|
||||
}
|
||||
|
||||
set {
|
||||
@ -124,8 +123,8 @@ class PrefWindow: NSObject,
|
||||
}
|
||||
|
||||
// MARK: - NSWindowDelegate
|
||||
extension PrefWindow {
|
||||
|
||||
extension PrefWindow {
|
||||
func windowShouldClose(_: NSWindow) -> Bool {
|
||||
self.emit(.close)
|
||||
|
||||
@ -138,20 +137,24 @@ extension PrefWindow {
|
||||
}
|
||||
|
||||
// MARK: - NSTableViewDataSource
|
||||
extension PrefWindow {
|
||||
|
||||
extension PrefWindow {
|
||||
@objc(numberOfRowsInTableView:) func numberOfRows(in _: NSTableView) -> Int {
|
||||
return self.panes.count
|
||||
self.panes.count
|
||||
}
|
||||
|
||||
@objc(tableView: objectValueForTableColumn:row:) func tableView(_: NSTableView, objectValueFor _: NSTableColumn?, row: Int) -> Any? {
|
||||
return self.panes[row].displayName
|
||||
@objc(tableView: objectValueForTableColumn:row:) func tableView(
|
||||
_: NSTableView,
|
||||
objectValueFor _: NSTableColumn?,
|
||||
row: Int
|
||||
) -> Any? {
|
||||
self.panes[row].displayName
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - NSTableViewDelegate
|
||||
extension PrefWindow {
|
||||
|
||||
extension PrefWindow {
|
||||
func tableViewSelectionDidChange(_: Notification) {
|
||||
let idx = self.categoryView.selectedRow
|
||||
self.panes[idx].paneWillAppear()
|
||||
|
@ -6,7 +6,6 @@
|
||||
import Foundation
|
||||
|
||||
class PrefWindowReducer: ReducerType {
|
||||
|
||||
typealias StateType = AppState
|
||||
typealias ActionType = PrefWindow.Action
|
||||
|
||||
@ -14,10 +13,8 @@ class PrefWindowReducer: ReducerType {
|
||||
var state = pair.state
|
||||
|
||||
switch pair.action {
|
||||
|
||||
case .close:
|
||||
state.preferencesOpen = Marked(false)
|
||||
|
||||
}
|
||||
|
||||
return (state, pair.action, true)
|
||||
|
@ -6,7 +6,6 @@
|
||||
import Foundation
|
||||
|
||||
class Resources {
|
||||
|
||||
static let resourceUrl = Bundle.main.resourceURL!
|
||||
static let previewUrl = resourceUrl.appendingPathComponent("preview")
|
||||
|
||||
|
@ -6,7 +6,6 @@
|
||||
import Foundation
|
||||
|
||||
class RpcAppearanceEpic: EpicType {
|
||||
|
||||
typealias StateType = AppState
|
||||
typealias ActionType = UuidAction<MainWindow.Action>
|
||||
typealias EmitActionType = AppearancePref.Action
|
||||
@ -18,23 +17,21 @@ class RpcAppearanceEpic: EpicType {
|
||||
func typedApply(
|
||||
_ reduce: @escaping TypedActionReduceFunction
|
||||
) -> TypedActionReduceFunction {
|
||||
return { tuple in
|
||||
{ tuple in
|
||||
let result = reduce(tuple)
|
||||
|
||||
switch tuple.action.payload {
|
||||
|
||||
case .setFont(let font):
|
||||
case let .setFont(font):
|
||||
self.emit(.setFont(font))
|
||||
|
||||
case .setLinespacing(let linespacing):
|
||||
case let .setLinespacing(linespacing):
|
||||
self.emit(.setLinespacing(linespacing))
|
||||
|
||||
case .setCharacterspacing(let characterspacing):
|
||||
|
||||
case let .setCharacterspacing(characterspacing):
|
||||
self.emit(.setCharacterspacing(characterspacing))
|
||||
|
||||
default:
|
||||
break
|
||||
|
||||
}
|
||||
|
||||
return result
|
||||
|
@ -6,7 +6,6 @@
|
||||
import Foundation
|
||||
|
||||
enum RpcEvent: String, CaseIterable {
|
||||
|
||||
static let prefix = "com.qvacua.vimr.rpc-events"
|
||||
|
||||
case makeSessionTemporary = "com.qvacua.vimr.rpc-events.make-session-temporary"
|
||||
|
@ -6,8 +6,7 @@
|
||||
import Cocoa
|
||||
|
||||
class ShortcutItem: NSObject, Comparable {
|
||||
|
||||
static func <(lhs: ShortcutItem, rhs: ShortcutItem) -> Bool { lhs.title < rhs.title }
|
||||
static func < (lhs: ShortcutItem, rhs: ShortcutItem) -> Bool { lhs.title < rhs.title }
|
||||
|
||||
@objc dynamic var title: String
|
||||
@objc dynamic var isLeaf: Bool
|
||||
@ -19,11 +18,11 @@ class ShortcutItem: NSObject, Comparable {
|
||||
var isContainer: Bool { !self.isLeaf }
|
||||
|
||||
override var description: String {
|
||||
"<ShortcutItem: \(title), " +
|
||||
"id: '\(self.identifier ?? "")', " +
|
||||
"isLeaf: \(self.isLeaf), " +
|
||||
"childrenCount: \(self.children?.count ?? -1)" +
|
||||
">"
|
||||
"<ShortcutItem: \(self.title), " +
|
||||
"id: '\(self.identifier ?? "")', " +
|
||||
"isLeaf: \(self.isLeaf), " +
|
||||
"childrenCount: \(self.children?.count ?? -1)" +
|
||||
">"
|
||||
}
|
||||
|
||||
let item: NSMenuItem?
|
||||
|
@ -9,10 +9,10 @@ import RxSwift
|
||||
import ShortcutRecorder
|
||||
|
||||
class ShortcutsPref: PrefPane,
|
||||
UiComponent,
|
||||
NSOutlineViewDelegate,
|
||||
RecorderControlDelegate {
|
||||
|
||||
UiComponent,
|
||||
NSOutlineViewDelegate,
|
||||
RecorderControlDelegate
|
||||
{
|
||||
typealias StateType = AppState
|
||||
|
||||
@objc dynamic var content = [ShortcutItem]()
|
||||
@ -21,7 +21,7 @@ class ShortcutsPref: PrefPane,
|
||||
|
||||
override var pinToContainer: Bool { true }
|
||||
|
||||
required init(source: Observable<StateType>, emitter: ActionEmitter, state: StateType) {
|
||||
required init(source _: Observable<StateType>, emitter _: ActionEmitter, state _: StateType) {
|
||||
// We know that the identifier is not empty.
|
||||
let shortcutSuiteName = Bundle.main.bundleIdentifier! + ".menuitems"
|
||||
self.shortcutsUserDefaults = UserDefaults(suiteName: shortcutSuiteName)
|
||||
@ -33,14 +33,15 @@ class ShortcutsPref: PrefPane,
|
||||
super.init(frame: .zero)
|
||||
|
||||
if let version = self.shortcutsUserDefaults?.integer(forKey: defaultsVersionKey),
|
||||
version > defaultsVersion {
|
||||
version > defaultsVersion
|
||||
{
|
||||
let alert = NSAlert()
|
||||
alert.alertStyle = .warning
|
||||
alert.messageText = "Incompatible Defaults for Shortcuts"
|
||||
alert.informativeText = "The stored defaults for shortcuts are not compatible with "
|
||||
+ "this version of VimR. You can delete the stored defaults "
|
||||
+ "by executing 'defaults delete com.qvacua.VimR.menuitems' "
|
||||
+ "in Terminal."
|
||||
+ "this version of VimR. You can delete the stored defaults "
|
||||
+ "by executing 'defaults delete com.qvacua.VimR.menuitems' "
|
||||
+ "in Terminal."
|
||||
alert.runModal()
|
||||
return
|
||||
}
|
||||
@ -59,7 +60,8 @@ class ShortcutsPref: PrefPane,
|
||||
self.shortcutList.expandItem(nil, expandChildren: true)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||||
@available(*, unavailable)
|
||||
required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||||
|
||||
private let shortcutList = NSOutlineView.standardOutlineView()
|
||||
private let shortcutScrollView = NSScrollView.standardScrollView()
|
||||
@ -126,7 +128,7 @@ class ShortcutsPref: PrefPane,
|
||||
|
||||
private func traverseMenuItems(with fn: (String, NSMenuItem) -> Void) {
|
||||
var queue = self.shortcutItemsRoot.children ?? []
|
||||
while (!queue.isEmpty) {
|
||||
while !queue.isEmpty {
|
||||
guard let item = queue.popLast() else { break }
|
||||
if item.isContainer, let children = item.children {
|
||||
queue.append(contentsOf: children)
|
||||
@ -168,18 +170,18 @@ class ShortcutsPref: PrefPane,
|
||||
shortcutItem: ShortcutItem(title: $0.title, isLeaf: false, item: $0)
|
||||
)
|
||||
}
|
||||
while (!queue.isEmpty) {
|
||||
while !queue.isEmpty {
|
||||
guard let entry = queue.popLast() else { break }
|
||||
|
||||
if !entry.shortcutItem.isLeaf
|
||||
|| entry.shortcutItem.identifier?.hasPrefix("com.qvacua.vimr.menuitems.") == true {
|
||||
|
||||
|| entry.shortcutItem.identifier?.hasPrefix("com.qvacua.vimr.menuitems.") == true
|
||||
{
|
||||
entry.parent.children?.append(entry.shortcutItem)
|
||||
}
|
||||
|
||||
if entry.shortcutItem.isContainer,
|
||||
let childMenuItems = entry.shortcutItem.item?.submenu?.items {
|
||||
|
||||
let childMenuItems = entry.shortcutItem.item?.submenu?.items
|
||||
{
|
||||
let shortcutChildItems = childMenuItems
|
||||
.filter { !$0.title.isEmpty }
|
||||
.map { menuItem in
|
||||
@ -233,9 +235,9 @@ class ShortcutsPref: PrefPane,
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
extension ShortcutsPref {
|
||||
|
||||
@objc func resetToDefault(_ sender: NSButton) {
|
||||
extension ShortcutsPref {
|
||||
@objc func resetToDefault(_: NSButton) {
|
||||
guard let window = self.window else { return }
|
||||
|
||||
let alert = NSAlert()
|
||||
@ -260,8 +262,8 @@ extension ShortcutsPref {
|
||||
}
|
||||
|
||||
// MARK: - NSOutlineViewDelegate
|
||||
extension ShortcutsPref {
|
||||
|
||||
extension ShortcutsPref {
|
||||
private func isUppercase(_ str: String) -> Bool {
|
||||
for c in str.unicodeScalars {
|
||||
if !CharacterSet.uppercaseLetters.contains(c) { return false }
|
||||
@ -270,7 +272,7 @@ extension ShortcutsPref {
|
||||
return true
|
||||
}
|
||||
|
||||
func outlineView(_ outlineView: NSOutlineView, rowViewForItem item: Any) -> NSTableRowView? {
|
||||
func outlineView(_: NSOutlineView, rowViewForItem _: Any) -> NSTableRowView? {
|
||||
let view = self.shortcutList.makeView(
|
||||
withIdentifier: NSUserInterfaceItemIdentifier("shortcut-row-view"),
|
||||
owner: self
|
||||
@ -279,7 +281,7 @@ extension ShortcutsPref {
|
||||
return view
|
||||
}
|
||||
|
||||
func outlineView(_: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {
|
||||
func outlineView(_: NSOutlineView, viewFor _: NSTableColumn?, item: Any) -> NSView? {
|
||||
let cellView = self.shortcutList.makeView(
|
||||
withIdentifier: NSUserInterfaceItemIdentifier("shortcut-cell-view"),
|
||||
owner: self
|
||||
@ -306,7 +308,7 @@ extension ShortcutsPref {
|
||||
return cellView
|
||||
}
|
||||
|
||||
func outlineView(_: NSOutlineView, heightOfRowByItem item: Any) -> CGFloat { 28 }
|
||||
func outlineView(_: NSOutlineView, heightOfRowByItem _: Any) -> CGFloat { 28 }
|
||||
|
||||
private func areShortcutsEqual(_ identifier: String) -> Bool {
|
||||
guard let dataFromDefaults = self.shortcutsDefaultsController.value(
|
||||
@ -324,9 +326,9 @@ extension ShortcutsPref {
|
||||
}
|
||||
|
||||
// MARK: - SRRecorderControlDelegate
|
||||
extension ShortcutsPref {
|
||||
|
||||
func recorderControlDidEndRecording(_ sender: RecorderControl) {
|
||||
extension ShortcutsPref {
|
||||
func recorderControlDidEndRecording(_: RecorderControl) {
|
||||
self.treeController.rearrangeObjects()
|
||||
}
|
||||
}
|
||||
@ -335,7 +337,6 @@ private let defaultsVersionKey = "version"
|
||||
private let defaultsVersion = 337
|
||||
|
||||
private class DataToKeyEquivalentTransformer: ValueTransformer {
|
||||
|
||||
override func transformedValue(_ value: Any?) -> Any? {
|
||||
guard let shortcut = ValueTransformer
|
||||
.keyedUnarchiveFromDataTransformer
|
||||
@ -346,7 +347,6 @@ private class DataToKeyEquivalentTransformer: ValueTransformer {
|
||||
}
|
||||
|
||||
private class DataToKeyEquivalentModifierMaskTransformer: ValueTransformer {
|
||||
|
||||
override func transformedValue(_ value: Any?) -> Any? {
|
||||
guard let shortcut = ValueTransformer
|
||||
.keyedUnarchiveFromDataTransformer
|
||||
|
@ -7,19 +7,18 @@ import Cocoa
|
||||
import ShortcutRecorder
|
||||
|
||||
class ShortcutTableRow: NSTableRowView {
|
||||
|
||||
init(withIdentifier identifier: String) {
|
||||
super.init(frame: .zero)
|
||||
self.identifier = NSUserInterfaceItemIdentifier(identifier)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
@available(*, unavailable)
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
|
||||
class ShortcutTableCell: NSTableCellView {
|
||||
|
||||
static let font = NSFont.systemFont(ofSize: 13)
|
||||
static let boldFont = NSFont.boldSystemFont(ofSize: 13)
|
||||
|
||||
@ -117,6 +116,6 @@ class ShortcutTableCell: NSTableCellView {
|
||||
private let shortcutRecorder = RecorderControl(forAutoLayout: ())
|
||||
private let _textField = NSTextField(forAutoLayout: ())
|
||||
|
||||
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||||
@available(*, unavailable)
|
||||
required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||||
}
|
||||
|
||||
|
@ -4,24 +4,21 @@
|
||||
*/
|
||||
|
||||
import Cocoa
|
||||
import Commons
|
||||
import NvimView
|
||||
import RxSwift
|
||||
import Commons
|
||||
import Workspace
|
||||
|
||||
struct AppState: Codable {
|
||||
|
||||
enum AfterLastWindowAction: String, Codable {
|
||||
|
||||
case doNothing = "do-nothing"
|
||||
case hide = "hide"
|
||||
case quit = "quit"
|
||||
case hide
|
||||
case quit
|
||||
}
|
||||
|
||||
static let `default` = AppState()
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
|
||||
case openNewMainWindowOnLaunch = "open-new-window-when-launching"
|
||||
case openNewMainWindowOnReactivation = "open-new-window-on-reactivation"
|
||||
case afterLastWindowAction = "after-last-window-action"
|
||||
@ -56,33 +53,44 @@ struct AppState: Codable {
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
self.openNewMainWindowOnLaunch = try container.decode(forKey: .openNewMainWindowOnLaunch,
|
||||
default: AppState.default.openNewMainWindowOnLaunch)
|
||||
self.openNewMainWindowOnReactivation = try container.decode(
|
||||
forKey: .openNewMainWindowOnReactivation, default: AppState.default.openNewMainWindowOnReactivation
|
||||
self.openNewMainWindowOnLaunch = try container.decode(
|
||||
forKey: .openNewMainWindowOnLaunch,
|
||||
default: AppState.default
|
||||
.openNewMainWindowOnLaunch
|
||||
)
|
||||
self.openNewMainWindowOnReactivation = try container.decode(
|
||||
forKey: .openNewMainWindowOnReactivation,
|
||||
default: AppState.default.openNewMainWindowOnReactivation
|
||||
)
|
||||
self.afterLastWindowAction = try container.decode(
|
||||
forKey: .afterLastWindowAction,
|
||||
default: .doNothing
|
||||
)
|
||||
self.useSnapshotUpdate = try container.decode(
|
||||
forKey: .useSnapshotUpdate,
|
||||
default: AppState.default.useSnapshotUpdate
|
||||
)
|
||||
self.afterLastWindowAction = try container.decode(forKey: .afterLastWindowAction, default: .doNothing)
|
||||
self.useSnapshotUpdate = try container.decode(forKey: .useSnapshotUpdate,
|
||||
default: AppState.default.useSnapshotUpdate)
|
||||
|
||||
self.openQuickly = try container.decode(forKey: .openQuickly, default: OpenQuicklyWindow.State.default)
|
||||
self.mainWindowTemplate = try container.decode(forKey: .mainWindowTemplate, default: MainWindow.State.default)
|
||||
self.openQuickly = try container.decode(
|
||||
forKey: .openQuickly,
|
||||
default: OpenQuicklyWindow.State.default
|
||||
)
|
||||
self.mainWindowTemplate = try container.decode(
|
||||
forKey: .mainWindowTemplate,
|
||||
default: MainWindow.State.default
|
||||
)
|
||||
}
|
||||
|
||||
// Use generated encode(to:)
|
||||
|
||||
private init() {
|
||||
}
|
||||
private init() {}
|
||||
}
|
||||
|
||||
extension OpenQuicklyWindow {
|
||||
|
||||
struct State: Codable {
|
||||
|
||||
static let `default` = State()
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
|
||||
case defaultUsesVcsIgnore = "default-uses-vcs-ignores"
|
||||
}
|
||||
|
||||
@ -103,17 +111,14 @@ extension OpenQuicklyWindow {
|
||||
try container.encode(self.defaultUsesVcsIgnores, forKey: .defaultUsesVcsIgnore)
|
||||
}
|
||||
|
||||
private init() {
|
||||
}
|
||||
private init() {}
|
||||
}
|
||||
}
|
||||
|
||||
struct PreviewState {
|
||||
|
||||
static let `default` = PreviewState()
|
||||
|
||||
enum Status {
|
||||
|
||||
case none
|
||||
case notSaved
|
||||
case error
|
||||
@ -121,7 +126,6 @@ struct PreviewState {
|
||||
}
|
||||
|
||||
enum SearchAction {
|
||||
|
||||
case none
|
||||
case forward
|
||||
case reverse
|
||||
@ -160,7 +164,6 @@ struct PreviewState {
|
||||
}
|
||||
|
||||
struct HtmlPreviewState {
|
||||
|
||||
static let `default` = HtmlPreviewState()
|
||||
|
||||
var htmlFile: URL?
|
||||
@ -168,11 +171,9 @@ struct HtmlPreviewState {
|
||||
}
|
||||
|
||||
struct AppearanceState: Codable {
|
||||
|
||||
static let `default` = AppearanceState()
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
|
||||
case usesTheme = "uses-theme"
|
||||
case showsFileIcon = "shows-file-icon"
|
||||
case editorFontName = "editor-font-name"
|
||||
@ -196,15 +197,23 @@ struct AppearanceState: Codable {
|
||||
|
||||
if let fontName = try container.decodeIfPresent(String.self, forKey: .editorFontName),
|
||||
let fontSize = try container.decodeIfPresent(Float.self, forKey: .editorFontSize),
|
||||
let font = NSFont(name: fontName, size: fontSize.cgf) {
|
||||
let font = NSFont(name: fontName, size: fontSize.cgf)
|
||||
{
|
||||
self.font = font
|
||||
} else {
|
||||
self.font = NvimView.defaultFont
|
||||
}
|
||||
|
||||
self.linespacing = (try container.decodeIfPresent(Float.self, forKey: .editorLinespacing) ?? 1.0).cgf
|
||||
self.characterspacing = (try container.decodeIfPresent(Float.self, forKey: .editorCharacterspacing) ?? 1.0).cgf
|
||||
self.usesLigatures = try container.decodeIfPresent(Bool.self, forKey: .editorUsesLigatures) ?? true
|
||||
self
|
||||
.linespacing = (try container.decodeIfPresent(Float.self, forKey: .editorLinespacing) ?? 1.0)
|
||||
.cgf
|
||||
self
|
||||
.characterspacing = (
|
||||
try container
|
||||
.decodeIfPresent(Float.self, forKey: .editorCharacterspacing) ?? 1.0
|
||||
).cgf
|
||||
self.usesLigatures = try container
|
||||
.decodeIfPresent(Bool.self, forKey: .editorUsesLigatures) ?? true
|
||||
|
||||
self.usesTheme = try container.decodeIfPresent(Bool.self, forKey: .usesTheme) ?? true
|
||||
self.showsFileIcon = try container.decodeIfPresent(Bool.self, forKey: .showsFileIcon) ?? true
|
||||
@ -222,14 +231,11 @@ struct AppearanceState: Codable {
|
||||
try container.encode(self.usesLigatures, forKey: .editorUsesLigatures)
|
||||
}
|
||||
|
||||
private init() {
|
||||
}
|
||||
private init() {}
|
||||
}
|
||||
|
||||
extension MainWindow {
|
||||
|
||||
struct State: Codable {
|
||||
|
||||
static let `default` = State(isAllToolsVisible: true, isToolButtonsVisible: true)
|
||||
|
||||
static let defaultTools: [MainWindow.Tools: WorkspaceToolState] = [
|
||||
@ -239,7 +245,8 @@ extension MainWindow {
|
||||
.htmlPreview: WorkspaceToolState(location: .right, dimension: 500, open: false),
|
||||
]
|
||||
|
||||
static let orderedDefault: [MainWindow.Tools] = [.fileBrowser, .buffersList, .preview, .htmlPreview]
|
||||
static let orderedDefault: [MainWindow.Tools] = [.fileBrowser, .buffersList, .preview,
|
||||
.htmlPreview]
|
||||
|
||||
var isAllToolsVisible = true
|
||||
var isToolButtonsVisible = true
|
||||
@ -303,12 +310,11 @@ extension MainWindow {
|
||||
}
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
|
||||
case allToolsVisible = "is-all-tools-visible"
|
||||
case toolButtonsVisible = "is-tool-buttons-visible"
|
||||
case orderedTools = "ordered-tools"
|
||||
case activeTools = "active-tools"
|
||||
case frame = "frame"
|
||||
case frame
|
||||
|
||||
case isLeftOptionMeta = "is-left-option-meta"
|
||||
case isRightOptionMeta = "is-right-option-meta"
|
||||
@ -319,7 +325,7 @@ extension MainWindow {
|
||||
case drawsParallel = "draws-parallel"
|
||||
case isShowHidden = "is-show-hidden"
|
||||
|
||||
case appearance = "appearance"
|
||||
case appearance
|
||||
case workspaceTools = "workspace-tool"
|
||||
case previewTool = "preview-tool"
|
||||
}
|
||||
@ -327,34 +333,58 @@ extension MainWindow {
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
self.isLeftOptionMeta = try container.decode(forKey: .isLeftOptionMeta, default: State.default.isLeftOptionMeta)
|
||||
self.isRightOptionMeta = try container.decode(forKey: .isRightOptionMeta,
|
||||
default: State.default.isRightOptionMeta)
|
||||
self.useInteractiveZsh = try container.decode(forKey: .useInteractiveZsh,
|
||||
default: State.default.useInteractiveZsh)
|
||||
self.trackpadScrollResistance = try container.decode(forKey: .trackpadScrollResistance,
|
||||
default: State.default.trackpadScrollResistance)
|
||||
self.useLiveResize = try container.decode(forKey: .useLiveResize, default: State.default.useLiveResize)
|
||||
self.drawsParallel = try container.decode(forKey: .drawsParallel, default: State.default.drawsParallel)
|
||||
self.isLeftOptionMeta = try container.decode(
|
||||
forKey: .isLeftOptionMeta,
|
||||
default: State.default.isLeftOptionMeta
|
||||
)
|
||||
self.isRightOptionMeta = try container.decode(
|
||||
forKey: .isRightOptionMeta,
|
||||
default: State.default.isRightOptionMeta
|
||||
)
|
||||
self.useInteractiveZsh = try container.decode(
|
||||
forKey: .useInteractiveZsh,
|
||||
default: State.default.useInteractiveZsh
|
||||
)
|
||||
self.trackpadScrollResistance = try container.decode(
|
||||
forKey: .trackpadScrollResistance,
|
||||
default: State.default
|
||||
.trackpadScrollResistance
|
||||
)
|
||||
self.useLiveResize = try container.decode(
|
||||
forKey: .useLiveResize,
|
||||
default: State.default.useLiveResize
|
||||
)
|
||||
self.drawsParallel = try container.decode(
|
||||
forKey: .drawsParallel,
|
||||
default: State.default.drawsParallel
|
||||
)
|
||||
if let frameRawValue = try container.decodeIfPresent(String.self, forKey: .frame) {
|
||||
self.frame = NSRectFromString(frameRawValue)
|
||||
} else {
|
||||
self.frame = CGRect(x: 100, y: 100, width: 600, height: 400)
|
||||
}
|
||||
|
||||
self.isAllToolsVisible = try container.decode(forKey: .allToolsVisible, default: State.default.isAllToolsVisible)
|
||||
self.isToolButtonsVisible = try container.decode(forKey: .toolButtonsVisible,
|
||||
default: State.default.isToolButtonsVisible)
|
||||
self.isAllToolsVisible = try container.decode(
|
||||
forKey: .allToolsVisible,
|
||||
default: State.default.isAllToolsVisible
|
||||
)
|
||||
self.isToolButtonsVisible = try container.decode(
|
||||
forKey: .toolButtonsVisible,
|
||||
default: State.default.isToolButtonsVisible
|
||||
)
|
||||
|
||||
self.appearance = try container.decode(forKey: .appearance, default: State.default.appearance)
|
||||
|
||||
self.orderedTools = try container.decode(forKey: .orderedTools, default: State.default.orderedTools)
|
||||
self.orderedTools = try container.decode(
|
||||
forKey: .orderedTools,
|
||||
default: State.default.orderedTools
|
||||
)
|
||||
let missingOrderedTools = MainWindow.Tools.all.subtracting(self.orderedTools)
|
||||
self.orderedTools.append(contentsOf: missingOrderedTools)
|
||||
|
||||
// See [1]
|
||||
let rawActiveTools: [String: Bool] = try container.decode(forKey: .activeTools, default: [:])
|
||||
self.activeTools = rawActiveTools.flatMapToDict { (key, value) in
|
||||
self.activeTools = rawActiveTools.flatMapToDict { key, value in
|
||||
guard let toolId = MainWindow.Tools(rawValue: key) else {
|
||||
return nil
|
||||
}
|
||||
@ -364,8 +394,9 @@ extension MainWindow {
|
||||
let missingActiveTools = MainWindow.Tools.all.subtracting(self.activeTools.keys)
|
||||
missingActiveTools.forEach { self.activeTools[$0] = true }
|
||||
|
||||
let rawTools: [String: WorkspaceToolState] = try container.decode(forKey: .workspaceTools, default: [:])
|
||||
self.tools = rawTools.flatMapToDict { (key, value) in
|
||||
let rawTools: [String: WorkspaceToolState] = try container
|
||||
.decode(forKey: .workspaceTools, default: [:])
|
||||
self.tools = rawTools.flatMapToDict { key, value in
|
||||
guard let tool = MainWindow.Tools(rawValue: key) else {
|
||||
return nil
|
||||
}
|
||||
@ -377,9 +408,15 @@ extension MainWindow {
|
||||
self.tools[missingTool] = MainWindow.State.defaultTools[missingTool]!
|
||||
}
|
||||
|
||||
self.previewTool = try container.decode(forKey: .previewTool, default: State.default.previewTool)
|
||||
self.fileBrowserShowHidden = try container.decode(forKey: .isShowHidden,
|
||||
default: State.default.fileBrowserShowHidden)
|
||||
self.previewTool = try container.decode(
|
||||
forKey: .previewTool,
|
||||
default: State.default.previewTool
|
||||
)
|
||||
self.fileBrowserShowHidden = try container.decode(
|
||||
forKey: .isShowHidden,
|
||||
default: State.default
|
||||
.fileBrowserShowHidden
|
||||
)
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
@ -397,10 +434,14 @@ extension MainWindow {
|
||||
try container.encode(self.fileBrowserShowHidden, forKey: .isShowHidden)
|
||||
|
||||
// See [1]
|
||||
try container.encode(Dictionary(uniqueKeysWithValues: self.tools.map { k, v in (k.rawValue, v) }),
|
||||
forKey: .workspaceTools)
|
||||
try container.encode(Dictionary(uniqueKeysWithValues: self.activeTools.map { k, v in (k.rawValue, v) }),
|
||||
forKey: .activeTools)
|
||||
try container.encode(
|
||||
Dictionary(uniqueKeysWithValues: self.tools.map { k, v in (k.rawValue, v) }),
|
||||
forKey: .workspaceTools
|
||||
)
|
||||
try container.encode(
|
||||
Dictionary(uniqueKeysWithValues: self.activeTools.map { k, v in (k.rawValue, v) }),
|
||||
forKey: .activeTools
|
||||
)
|
||||
|
||||
try container.encode(self.appearance, forKey: .appearance)
|
||||
try container.encode(self.orderedTools, forKey: .orderedTools)
|
||||
@ -410,14 +451,12 @@ extension MainWindow {
|
||||
}
|
||||
|
||||
struct WorkspaceToolState: Codable {
|
||||
|
||||
static let `default` = WorkspaceToolState()
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
|
||||
case location = "location"
|
||||
case `open` = "is-visible"
|
||||
case dimension = "dimension"
|
||||
case location
|
||||
case open = "is-visible"
|
||||
case dimension
|
||||
}
|
||||
|
||||
var location = WorkspaceBarLocation.left
|
||||
@ -443,8 +482,7 @@ struct WorkspaceToolState: Codable {
|
||||
|
||||
// Use generated encode(to:)
|
||||
|
||||
private init() {
|
||||
}
|
||||
private init() {}
|
||||
|
||||
init(location: WorkspaceBarLocation, dimension: CGFloat, open: Bool) {
|
||||
self.location = location
|
||||
@ -454,13 +492,10 @@ struct WorkspaceToolState: Codable {
|
||||
}
|
||||
|
||||
extension MarkdownTool {
|
||||
|
||||
struct State: Codable {
|
||||
|
||||
static let `default` = State()
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
|
||||
case forwardSearchAutomatically = "is-forward-search-automatically"
|
||||
case reverseSearchAutomatically = "is-reverse-search-automatically"
|
||||
case refreshOnWrite = "is-refresh-on-write"
|
||||
@ -470,17 +505,25 @@ extension MarkdownTool {
|
||||
var isReverseSearchAutomatically = false
|
||||
var isRefreshOnWrite = true
|
||||
|
||||
private init() {
|
||||
}
|
||||
private init() {}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
self.isForwardSearchAutomatically = try container.decode(forKey: .forwardSearchAutomatically,
|
||||
default: State.default.isForwardSearchAutomatically)
|
||||
self.isReverseSearchAutomatically = try container.decode(forKey: .reverseSearchAutomatically,
|
||||
default: State.default.isReverseSearchAutomatically)
|
||||
self.isRefreshOnWrite = try container.decode(forKey: .refreshOnWrite, default: State.default.isRefreshOnWrite)
|
||||
self.isForwardSearchAutomatically = try container.decode(
|
||||
forKey: .forwardSearchAutomatically,
|
||||
default: State.default
|
||||
.isForwardSearchAutomatically
|
||||
)
|
||||
self.isReverseSearchAutomatically = try container.decode(
|
||||
forKey: .reverseSearchAutomatically,
|
||||
default: State.default
|
||||
.isReverseSearchAutomatically
|
||||
)
|
||||
self.isRefreshOnWrite = try container.decode(
|
||||
forKey: .refreshOnWrite,
|
||||
default: State.default.isRefreshOnWrite
|
||||
)
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
@ -493,10 +536,9 @@ extension MarkdownTool {
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate extension KeyedDecodingContainer where K: CodingKey {
|
||||
|
||||
func decode<T: Decodable>(forKey key: K, `default`: T) throws -> T {
|
||||
return try self.decodeIfPresent(T.self, forKey: key) ?? `default`
|
||||
private extension KeyedDecodingContainer where K: CodingKey {
|
||||
func decode<T: Decodable>(forKey key: K, default: T) throws -> T {
|
||||
try self.decodeIfPresent(T.self, forKey: key) ?? `default`
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,23 +4,23 @@
|
||||
*/
|
||||
|
||||
import Cocoa
|
||||
import NvimView
|
||||
import Commons
|
||||
import NvimView
|
||||
|
||||
func changeTheme(themePrefChanged: Bool, themeChanged: Bool, usesTheme: Bool,
|
||||
forTheme: () -> Void, forDefaultTheme: () -> Void) -> Bool {
|
||||
|
||||
if themePrefChanged && usesTheme {
|
||||
forTheme: () -> Void, forDefaultTheme: () -> Void) -> Bool
|
||||
{
|
||||
if themePrefChanged, usesTheme {
|
||||
forTheme()
|
||||
return true
|
||||
}
|
||||
|
||||
if themePrefChanged && !usesTheme {
|
||||
if themePrefChanged, !usesTheme {
|
||||
forDefaultTheme()
|
||||
return true
|
||||
}
|
||||
|
||||
if !themePrefChanged && themeChanged && usesTheme {
|
||||
if !themePrefChanged, themeChanged, usesTheme {
|
||||
forTheme()
|
||||
return true
|
||||
}
|
||||
@ -29,7 +29,6 @@ func changeTheme(themePrefChanged: Bool, themeChanged: Bool, usesTheme: Bool,
|
||||
}
|
||||
|
||||
struct Theme: CustomStringConvertible {
|
||||
|
||||
static let `default` = Theme()
|
||||
|
||||
var foreground = NSColor.textColor
|
||||
@ -53,11 +52,11 @@ struct Theme: CustomStringConvertible {
|
||||
var cssCodeBackgroundColor = NSColor(hex: "1b1f23")!
|
||||
|
||||
public var description: String {
|
||||
return "Theme<" +
|
||||
"fg: \(self.foreground.hex), bg: \(self.background.hex), " +
|
||||
"hl-fg: \(self.highlightForeground.hex), hl-bg: \(self.highlightBackground.hex)" +
|
||||
"dir-fg: \(self.directoryForeground.hex)" +
|
||||
">"
|
||||
"Theme<" +
|
||||
"fg: \(self.foreground.hex), bg: \(self.background.hex), " +
|
||||
"hl-fg: \(self.highlightForeground.hex), hl-bg: \(self.highlightBackground.hex)" +
|
||||
"dir-fg: \(self.directoryForeground.hex)" +
|
||||
">"
|
||||
}
|
||||
|
||||
init() {}
|
||||
|
@ -7,14 +7,12 @@ import Cocoa
|
||||
import NvimView
|
||||
import PureLayout
|
||||
|
||||
protocol ThemedView: class {
|
||||
|
||||
protocol ThemedView: AnyObject {
|
||||
var theme: Theme { get }
|
||||
var lastThemeMark: Token { get }
|
||||
}
|
||||
|
||||
class ThemedTableRow: NSTableRowView {
|
||||
|
||||
weak var triangleView: NSButton?
|
||||
var themeToken: Token
|
||||
|
||||
@ -34,11 +32,11 @@ class ThemedTableRow: NSTableRowView {
|
||||
}
|
||||
}
|
||||
|
||||
open override func drawBackground(in dirtyRect: NSRect) {
|
||||
override open func drawBackground(in dirtyRect: NSRect) {
|
||||
if let cell = self.view(atColumn: 0) as? ThemedTableCell {
|
||||
if cell.isDir {
|
||||
cell.textField?.textColor
|
||||
= self.themedView?.theme.directoryForeground ?? Theme.default.directoryForeground
|
||||
= self.themedView?.theme.directoryForeground ?? Theme.default.directoryForeground
|
||||
} else {
|
||||
cell.textField?.textColor = self.themedView?.theme.foreground ?? Theme.default.foreground
|
||||
}
|
||||
@ -51,7 +49,7 @@ class ThemedTableRow: NSTableRowView {
|
||||
override func drawSelection(in dirtyRect: NSRect) {
|
||||
if let cell = self.view(atColumn: 0) as? ThemedTableCell {
|
||||
cell.textField?.textColor
|
||||
= self.themedView?.theme.highlightForeground ?? Theme.default.highlightForeground
|
||||
= self.themedView?.theme.highlightForeground ?? Theme.default.highlightForeground
|
||||
}
|
||||
|
||||
self.themedView?.theme.highlightBackground.set()
|
||||
@ -60,12 +58,13 @@ class ThemedTableRow: NSTableRowView {
|
||||
|
||||
private weak var themedView: ThemedView?
|
||||
|
||||
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||||
@available(*, unavailable)
|
||||
required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||||
}
|
||||
|
||||
class ThemedTableCell: NSTableCellView {
|
||||
|
||||
// MARK: - API
|
||||
|
||||
static let font = NSFont.systemFont(ofSize: 12)
|
||||
static let widthWithoutText = (2 + 16 + 4 + 2).cgf
|
||||
|
||||
@ -166,5 +165,6 @@ class ThemedTableCell: NSTableCellView {
|
||||
private let _textField = NSTextField(forAutoLayout: ())
|
||||
private let _imageView = NSImageView(forAutoLayout: ())
|
||||
|
||||
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||||
@available(*, unavailable)
|
||||
required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||||
}
|
||||
|
@ -8,20 +8,18 @@ import PureLayout
|
||||
import RxSwift
|
||||
|
||||
class ToolsPref: PrefPane, UiComponent {
|
||||
|
||||
typealias StateType = AppState
|
||||
|
||||
enum Action {
|
||||
|
||||
case setActiveTools([MainWindow.Tools: Bool])
|
||||
}
|
||||
|
||||
override var displayName: String {
|
||||
return "Tools"
|
||||
"Tools"
|
||||
}
|
||||
|
||||
override var pinToContainer: Bool {
|
||||
return true
|
||||
true
|
||||
}
|
||||
|
||||
required init(source: Observable<StateType>, emitter: ActionEmitter, state: StateType) {
|
||||
@ -36,7 +34,7 @@ class ToolsPref: PrefPane, UiComponent {
|
||||
|
||||
source
|
||||
.observeOn(MainScheduler.instance)
|
||||
.subscribe(onNext: { state in
|
||||
.subscribe(onNext: { _ in
|
||||
|
||||
self.updateViews()
|
||||
})
|
||||
@ -53,7 +51,8 @@ class ToolsPref: PrefPane, UiComponent {
|
||||
private let previewCheckbox = NSButton(forAutoLayout: ())
|
||||
private let htmlCheckbox = NSButton(forAutoLayout: ())
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
@available(*, unavailable)
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
@ -69,24 +68,32 @@ class ToolsPref: PrefPane, UiComponent {
|
||||
|
||||
let fileBrowser = self.fileBrowserCheckbox
|
||||
fileBrowser.target = self
|
||||
self.configureCheckbox(button: fileBrowser,
|
||||
title: "File Browser",
|
||||
action: #selector(ToolsPref.fileBrowserAction(_:)))
|
||||
self.configureCheckbox(
|
||||
button: fileBrowser,
|
||||
title: "File Browser",
|
||||
action: #selector(ToolsPref.fileBrowserAction(_:))
|
||||
)
|
||||
let openedFilesList = self.openedFilesListCheckbox
|
||||
openedFilesList.target = self
|
||||
self.configureCheckbox(button: openedFilesList,
|
||||
title: "Buffers",
|
||||
action: #selector(ToolsPref.openedFilesListAction(_:)))
|
||||
self.configureCheckbox(
|
||||
button: openedFilesList,
|
||||
title: "Buffers",
|
||||
action: #selector(ToolsPref.openedFilesListAction(_:))
|
||||
)
|
||||
let preview = self.previewCheckbox
|
||||
preview.target = self
|
||||
self.configureCheckbox(button: preview,
|
||||
title: "Markdown Preview",
|
||||
action: #selector(ToolsPref.previewAction(_:)))
|
||||
self.configureCheckbox(
|
||||
button: preview,
|
||||
title: "Markdown Preview",
|
||||
action: #selector(ToolsPref.previewAction(_:))
|
||||
)
|
||||
let html = self.htmlCheckbox
|
||||
html.target = self
|
||||
self.configureCheckbox(button: html,
|
||||
title: "HTML Preview",
|
||||
action: #selector(ToolsPref.htmlPreviewAction(_:)))
|
||||
self.configureCheckbox(
|
||||
button: html,
|
||||
title: "HTML Preview",
|
||||
action: #selector(ToolsPref.htmlPreviewAction(_:))
|
||||
)
|
||||
|
||||
let info = self.infoTextField(
|
||||
markdown: "You can turn off tools you don't need. The effect takes place when new windows are opened."
|
||||
@ -123,24 +130,24 @@ class ToolsPref: PrefPane, UiComponent {
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
extension ToolsPref {
|
||||
|
||||
@IBAction func fileBrowserAction(_ sender: Any?) {
|
||||
extension ToolsPref {
|
||||
@IBAction func fileBrowserAction(_: Any?) {
|
||||
self.tools[.fileBrowser] = self.fileBrowserCheckbox.boolState
|
||||
self.emit(.setActiveTools(self.tools))
|
||||
}
|
||||
|
||||
@IBAction func openedFilesListAction(_ sender: Any?) {
|
||||
@IBAction func openedFilesListAction(_: Any?) {
|
||||
self.tools[.buffersList] = self.openedFilesListCheckbox.boolState
|
||||
self.emit(.setActiveTools(self.tools))
|
||||
}
|
||||
|
||||
@IBAction func previewAction(_ sender: Any?) {
|
||||
@IBAction func previewAction(_: Any?) {
|
||||
self.tools[.preview] = self.previewCheckbox.boolState
|
||||
self.emit(.setActiveTools(self.tools))
|
||||
}
|
||||
|
||||
@IBAction func htmlPreviewAction(_ sender: Any?) {
|
||||
@IBAction func htmlPreviewAction(_: Any?) {
|
||||
self.tools[.htmlPreview] = self.htmlCheckbox.boolState
|
||||
self.emit(.setActiveTools(self.tools))
|
||||
}
|
||||
|
@ -6,7 +6,6 @@
|
||||
import Foundation
|
||||
|
||||
class ToolsPrefReducer: ReducerType {
|
||||
|
||||
typealias StateType = AppState
|
||||
typealias ActionType = ToolsPref.Action
|
||||
|
||||
@ -14,10 +13,8 @@ class ToolsPrefReducer: ReducerType {
|
||||
var state = pair.state
|
||||
|
||||
switch pair.action {
|
||||
|
||||
case let .setActiveTools(tools):
|
||||
state.mainWindowTemplate.activeTools = tools
|
||||
|
||||
}
|
||||
|
||||
return (state, pair.action, true)
|
||||
|
@ -7,24 +7,21 @@ import Foundation
|
||||
import RxSwift
|
||||
|
||||
struct StateActionPair<S, A> {
|
||||
|
||||
var state: S
|
||||
var action: A
|
||||
var modified: Bool
|
||||
}
|
||||
|
||||
protocol UuidTagged {
|
||||
|
||||
var uuid: UUID { get }
|
||||
}
|
||||
|
||||
class UuidAction<A>: UuidTagged, CustomStringConvertible {
|
||||
|
||||
let uuid: UUID
|
||||
let payload: A
|
||||
|
||||
var description: String {
|
||||
return "UuidAction(uuid: \(uuid), payload: \(String(reflecting: payload)))"
|
||||
"UuidAction(uuid: \(self.uuid), payload: \(String(reflecting: self.payload)))"
|
||||
}
|
||||
|
||||
init(uuid: UUID, action: A) {
|
||||
@ -34,12 +31,11 @@ class UuidAction<A>: UuidTagged, CustomStringConvertible {
|
||||
}
|
||||
|
||||
class UuidState<S>: UuidTagged, CustomStringConvertible {
|
||||
|
||||
let uuid: UUID
|
||||
let payload: S
|
||||
|
||||
var description: String {
|
||||
return "UuidState(uuid: \(uuid), payload: \(String(reflecting: payload)))"
|
||||
"UuidState(uuid: \(self.uuid), payload: \(String(reflecting: self.payload)))"
|
||||
}
|
||||
|
||||
init(uuid: UUID, state: S) {
|
||||
@ -49,27 +45,25 @@ class UuidState<S>: UuidTagged, CustomStringConvertible {
|
||||
}
|
||||
|
||||
class Token: Hashable, CustomStringConvertible {
|
||||
|
||||
func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(ObjectIdentifier(self))
|
||||
}
|
||||
|
||||
var description: String {
|
||||
return ObjectIdentifier(self).debugDescription
|
||||
ObjectIdentifier(self).debugDescription
|
||||
}
|
||||
|
||||
static func == (left: Token, right: Token) -> Bool {
|
||||
return left === right
|
||||
left === right
|
||||
}
|
||||
}
|
||||
|
||||
class Marked<T>: CustomStringConvertible {
|
||||
|
||||
let mark: Token
|
||||
let payload: T
|
||||
|
||||
var description: String {
|
||||
return "Marked<\(mark) -> \(self.payload)>"
|
||||
"Marked<\(mark) -> \(self.payload)>"
|
||||
}
|
||||
|
||||
convenience init(_ payload: T) {
|
||||
@ -82,21 +76,18 @@ class Marked<T>: CustomStringConvertible {
|
||||
}
|
||||
|
||||
func hasDifferentMark(as other: Marked<T>) -> Bool {
|
||||
return self.mark != other.mark
|
||||
self.mark != other.mark
|
||||
}
|
||||
}
|
||||
|
||||
class UiComponentTemplate: UiComponent {
|
||||
|
||||
typealias StateType = State
|
||||
|
||||
struct State {
|
||||
|
||||
var someField: String
|
||||
}
|
||||
|
||||
enum Action {
|
||||
|
||||
case doSth
|
||||
}
|
||||
|
||||
@ -115,7 +106,7 @@ class UiComponentTemplate: UiComponent {
|
||||
source
|
||||
.observeOn(MainScheduler.instance)
|
||||
.subscribe(
|
||||
onNext: { state in
|
||||
onNext: { _ in
|
||||
Swift.print("Hello, \(self.someField)")
|
||||
}
|
||||
)
|
||||
|
@ -7,11 +7,9 @@ import Cocoa
|
||||
import RxSwift
|
||||
|
||||
class UiRoot: UiComponent {
|
||||
|
||||
typealias StateType = AppState
|
||||
|
||||
enum Action {
|
||||
|
||||
case quit
|
||||
}
|
||||
|
||||
@ -56,11 +54,9 @@ class UiRoot: UiComponent {
|
||||
guard self.mainWindows.isEmpty else { return }
|
||||
|
||||
switch state.afterLastWindowAction {
|
||||
|
||||
case .doNothing: return
|
||||
case .hide: NSApp.hide(self)
|
||||
case .quit: self.emit(.quit)
|
||||
|
||||
}
|
||||
})
|
||||
.disposed(by: self.disposeBag)
|
||||
@ -108,9 +104,11 @@ class UiRoot: UiComponent {
|
||||
.completableSubject()
|
||||
|
||||
self.subjectForMainWindows[state.uuid] = subject
|
||||
return MainWindow(source: subject.asObservable(),
|
||||
emitter: self.emitter,
|
||||
state: state)
|
||||
return MainWindow(
|
||||
source: subject.asObservable(),
|
||||
emitter: self.emitter,
|
||||
state: state
|
||||
)
|
||||
}
|
||||
|
||||
private func removeMainWindow(with uuid: UUID) {
|
||||
|
@ -6,7 +6,6 @@
|
||||
import Foundation
|
||||
|
||||
class UiRootReducer: ReducerType {
|
||||
|
||||
typealias StateType = AppState
|
||||
typealias ActionType = UiRoot.Action
|
||||
|
||||
@ -16,17 +15,14 @@ class UiRootReducer: ReducerType {
|
||||
var appState = tuple.state
|
||||
|
||||
switch tuple.action {
|
||||
|
||||
case .quit:
|
||||
appState.quit = true
|
||||
|
||||
}
|
||||
|
||||
return (appState, tuple.action, true)
|
||||
}
|
||||
|
||||
class MainWindowReducer: ReducerType {
|
||||
|
||||
typealias StateType = AppState
|
||||
typealias ActionType = UuidAction<MainWindow.Action>
|
||||
|
||||
@ -35,7 +31,6 @@ class UiRootReducer: ReducerType {
|
||||
let uuid = tuple.action.uuid
|
||||
|
||||
switch tuple.action.payload {
|
||||
|
||||
case let .becomeKey(isFullScreen):
|
||||
appState.currentMainWindowUuid = uuid
|
||||
|
||||
@ -49,7 +44,7 @@ class UiRootReducer: ReducerType {
|
||||
isFullScreen: isFullScreen
|
||||
)
|
||||
|
||||
case let .frameChanged(to:frame):
|
||||
case let .frameChanged(to: frame):
|
||||
if appState.mainWindows[uuid]?.isTemporarySession == true {
|
||||
break
|
||||
}
|
||||
@ -63,7 +58,7 @@ class UiRootReducer: ReducerType {
|
||||
break
|
||||
}
|
||||
|
||||
appState.mainWindowTemplate.orderedTools = tools.map { $0.0 }
|
||||
appState.mainWindowTemplate.orderedTools = tools.map(\.0)
|
||||
|
||||
case let .toggleAllTools(value):
|
||||
if appState.mainWindows[uuid]?.isTemporarySession == true {
|
||||
@ -90,8 +85,8 @@ class UiRootReducer: ReducerType {
|
||||
}
|
||||
|
||||
if appState.currentMainWindowUuid == uuid,
|
||||
let mainWindowToClose = appState.mainWindows[uuid] {
|
||||
|
||||
let mainWindowToClose = appState.mainWindows[uuid]
|
||||
{
|
||||
appState.currentMainWindowUuid = nil
|
||||
appState.mainWindowTemplate = self.mainWindowTemplate(
|
||||
from: appState.mainWindowTemplate,
|
||||
@ -107,7 +102,6 @@ class UiRootReducer: ReducerType {
|
||||
|
||||
default:
|
||||
return tuple
|
||||
|
||||
}
|
||||
|
||||
return (appState, tuple.action, true)
|
||||
@ -115,8 +109,8 @@ class UiRootReducer: ReducerType {
|
||||
|
||||
private func mainWindowTemplate(from old: MainWindow.State,
|
||||
new: MainWindow.State,
|
||||
isFullScreen: Bool) -> MainWindow.State {
|
||||
|
||||
isFullScreen: Bool) -> MainWindow.State
|
||||
{
|
||||
var result = old
|
||||
|
||||
if !isFullScreen {
|
||||
|
Loading…
Reference in New Issue
Block a user