1
1
mirror of https://github.com/qvacua/vimr.git synced 2024-12-25 06:43:24 +03:00

Format VimR folder with swiftformat

This commit is contained in:
Tae Won Ha 2020-11-19 21:14:36 +01:00
parent ed05f433c9
commit ac5d0ca055
No known key found for this signature in database
GPG Key ID: E40743465B5B8B44
63 changed files with 1013 additions and 935 deletions

View File

@ -6,7 +6,6 @@
import Foundation import Foundation
class AdvancedPrefReducer: ReducerType { class AdvancedPrefReducer: ReducerType {
typealias StateType = AppState typealias StateType = AppState
typealias ActionType = AdvancedPref.Action typealias ActionType = AdvancedPref.Action
@ -14,7 +13,6 @@ class AdvancedPrefReducer: ReducerType {
var state = pair.state var state = pair.state
switch pair.action { switch pair.action {
case let .setUseLiveResize(value): case let .setUseLiveResize(value):
state.mainWindowTemplate.useLiveResize = value state.mainWindowTemplate.useLiveResize = value
state.mainWindows.keys.forEach { state.mainWindows[$0]?.useLiveResize = value } state.mainWindows.keys.forEach { state.mainWindows[$0]?.useLiveResize = value }

View File

@ -8,11 +8,9 @@ import PureLayout
import RxSwift import RxSwift
class AdvancedPref: PrefPane, UiComponent, NSTextFieldDelegate { class AdvancedPref: PrefPane, UiComponent, NSTextFieldDelegate {
typealias StateType = AppState typealias StateType = AppState
enum Action { enum Action {
case setUseInteractiveZsh(Bool) case setUseInteractiveZsh(Bool)
case setUseSnapshotUpdate(Bool) case setUseSnapshotUpdate(Bool)
case setTrackpadScrollResistance(Double) case setTrackpadScrollResistance(Double)
@ -21,11 +19,11 @@ class AdvancedPref: PrefPane, UiComponent, NSTextFieldDelegate {
} }
override var displayName: String { override var displayName: String {
return "Advanced" "Advanced"
} }
override var pinToContainer: Bool { override var pinToContainer: Bool {
return true true
} }
required init(source: Observable<StateType>, emitter: ActionEmitter, state: StateType) { required init(source: Observable<StateType>, emitter: ActionEmitter, state: StateType) {
@ -46,10 +44,10 @@ class AdvancedPref: PrefPane, UiComponent, NSTextFieldDelegate {
.observeOn(MainScheduler.instance) .observeOn(MainScheduler.instance)
.subscribe(onNext: { state in .subscribe(onNext: { state in
if self.useInteractiveZsh != state.mainWindowTemplate.useInteractiveZsh if self.useInteractiveZsh != state.mainWindowTemplate.useInteractiveZsh
|| self.useSnapshotUpdate != state.useSnapshotUpdate || self.useSnapshotUpdate != state.useSnapshotUpdate
|| self.useLiveResize != state.mainWindowTemplate.useLiveResize || self.useLiveResize != state.mainWindowTemplate.useLiveResize
|| self.drawsParallel != state.mainWindowTemplate.drawsParallel { || self.drawsParallel != state.mainWindowTemplate.drawsParallel
{
self.useInteractiveZsh = state.mainWindowTemplate.useInteractiveZsh self.useInteractiveZsh = state.mainWindowTemplate.useInteractiveZsh
self.useSnapshotUpdate = state.useSnapshotUpdate self.useSnapshotUpdate = state.useSnapshotUpdate
self.useLiveResize = state.mainWindowTemplate.useLiveResize self.useLiveResize = state.mainWindowTemplate.useLiveResize
@ -76,7 +74,8 @@ class AdvancedPref: PrefPane, UiComponent, NSTextFieldDelegate {
private let drawsParallelCheckbox = NSButton(forAutoLayout: ()) private let drawsParallelCheckbox = NSButton(forAutoLayout: ())
private let sensitivitySlider = NSSlider(forAutoLayout: ()) private let sensitivitySlider = NSSlider(forAutoLayout: ())
required init?(coder: NSCoder) { @available(*, unavailable)
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
@ -93,47 +92,55 @@ class AdvancedPref: PrefPane, UiComponent, NSTextFieldDelegate {
let paneTitle = self.paneTitleTextField(title: "Advanced") let paneTitle = self.paneTitleTextField(title: "Advanced")
let useInteractiveZsh = self.useInteractiveZshCheckbox let useInteractiveZsh = self.useInteractiveZshCheckbox
self.configureCheckbox(button: useInteractiveZsh, self.configureCheckbox(
title: "Use interactive mode for zsh", button: useInteractiveZsh,
action: #selector(AdvancedPref.useInteractiveZshAction(_:))) title: "Use interactive mode for zsh",
action: #selector(AdvancedPref.useInteractiveZshAction(_:))
)
let useInteractiveZshInfo = self.infoTextField(markdown: #""" let useInteractiveZshInfo = self.infoTextField(markdown: #"""
If your login shell is `zsh`, when checked, the `-i` option will be used to launch `zsh`.\ 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.\ 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.\ It may be a good idea to put the `PATH`-settings in `.zshenv` and let this unchecked.\
*Use with caution.* *Use with caution.*
"""#) """#)
let useSnapshotUpdate = self.useSnapshotUpdateCheckbox let useSnapshotUpdate = self.useSnapshotUpdateCheckbox
self.configureCheckbox(button: self.useSnapshotUpdateCheckbox, self.configureCheckbox(
title: "Use Snapshot Update Channel", button: self.useSnapshotUpdateCheckbox,
action: #selector(AdvancedPref.useSnapshotUpdateChannelAction(_:))) title: "Use Snapshot Update Channel",
action: #selector(AdvancedPref.useSnapshotUpdateChannelAction(_:))
)
let useSnapshotUpdateInfo = self.infoTextField(markdown: #""" let useSnapshotUpdateInfo = self.infoTextField(markdown: #"""
If you are adventurous, check this. You'll be test driving the newest snapshot builds\ If you are adventurous, check this. You'll be test driving the newest snapshot builds\
of VimR in no time! of VimR in no time!
"""#) """#)
let useLiveResize = self.useLiveResizeCheckbox let useLiveResize = self.useLiveResizeCheckbox
self.configureCheckbox(button: useLiveResize, self.configureCheckbox(
title: "Use Live Window Resizing", button: useLiveResize,
action: #selector(AdvancedPref.useLiveResizeAction(_:))) title: "Use Live Window Resizing",
action: #selector(AdvancedPref.useLiveResizeAction(_:))
)
let useLiveResizeInfo = self.infoTextField(markdown: #""" let useLiveResizeInfo = self.infoTextField(markdown: #"""
The Live Resizing is yet experimental. You may experience some 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). If you do, please report them at [GitHub](https://github.com/qvacua/vimr/issues).
"""#) """#)
let drawsParallelBox = self.drawsParallelCheckbox let drawsParallelBox = self.drawsParallelCheckbox
self.configureCheckbox(button: drawsParallelBox, self.configureCheckbox(
title: "Use Concurrent Rendering", button: drawsParallelBox,
action: #selector(AdvancedPref.drawParallelAction(_:))) title: "Use Concurrent Rendering",
action: #selector(AdvancedPref.drawParallelAction(_:))
)
let drawsParallelInfo = self.infoTextField(markdown: #""" let drawsParallelInfo = self.infoTextField(markdown: #"""
VimR can compute the glyphs concurrently. This may result in faster rendering,\ 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.\ depending on the situation. It will definitely result in higher CPU usage, e.g.\
when scrolling very fast. when scrolling very fast.
"""#) """#)
let sensitivityTitle = self.titleTextField(title: "Scroll Sensitivity:") let sensitivityTitle = self.titleTextField(title: "Scroll Sensitivity:")
let sensitivity = self.sensitivitySlider let sensitivity = self.sensitivitySlider
@ -142,9 +149,9 @@ when scrolling very fast.
sensitivity.target = self sensitivity.target = self
sensitivity.action = #selector(sensitivitySliderAction) sensitivity.action = #selector(sensitivitySliderAction)
let sensitivityInfo = self.infoTextField(markdown: #""" let sensitivityInfo = self.infoTextField(markdown: #"""
Trackpad scroll sensitivity is yet experimental. You may experience some issues.\ 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). 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. // We set the value of the NSSlider only at the beginning.
self.sensitivitySlider.doubleValue = self.sensitivity 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(useInteractiveZsh)
self.addSubview(useInteractiveZshInfo) self.addSubview(useInteractiveZshInfo)
self.addSubview(sensitivityTitle) self.addSubview(sensitivityTitle)
self.addSubview(sensitivitySlider) self.addSubview(self.sensitivitySlider)
self.addSubview(sensitivityInfo) self.addSubview(sensitivityInfo)
self.addSubview(useLiveResize) self.addSubview(useLiveResize)
self.addSubview(useLiveResizeInfo) 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.autoAlignAxis(.baseline, toSameAxisOf: sensitivityTitle)
sensitivity.autoPinEdge(.left, to: .right, of: sensitivityTitle, withOffset: 5) 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) sensitivityInfo.autoPinEdge(.left, to: .right, of: sensitivityTitle, withOffset: 5)
useLiveResize.autoPinEdge(.top, to: .bottom, of: sensitivityInfo, withOffset: 18) 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 // MARK: - Actions
extension AdvancedPref {
extension AdvancedPref {
@objc func useLiveResizeAction(_ sender: NSButton) { @objc func useLiveResizeAction(_ sender: NSButton) {
self.emit(.setUseLiveResize(sender.boolState)) self.emit(.setUseLiveResize(sender.boolState))
} }

View File

@ -4,21 +4,19 @@
*/ */
import Cocoa import Cocoa
import RxSwift
import PureLayout
import Sparkle
import DictionaryCoding
import os
import Commons import Commons
import CommonsObjC import CommonsObjC
import DictionaryCoding
import os
import PureLayout
import RxSwift
import Sparkle
let debugMenuItemIdentifier = NSUserInterfaceItemIdentifier("debug-menu-item") let debugMenuItemIdentifier = NSUserInterfaceItemIdentifier("debug-menu-item")
@NSApplicationMain @NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDelegate { class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDelegate {
struct OpenConfig { struct OpenConfig {
var urls: [URL] var urls: [URL]
var cwd: URL var cwd: URL
@ -29,7 +27,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
} }
enum Action { enum Action {
case newMainWindow(config: OpenConfig) case newMainWindow(config: OpenConfig)
case openInKeyWindow(config: OpenConfig) case openInKeyWindow(config: OpenConfig)
@ -42,9 +39,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
var initialAppState: AppState var initialAppState: AppState
let dictDecoder = DictionaryDecoder() let dictDecoder = DictionaryDecoder()
if let stateDict = UserDefaults.standard.value(forKey: PrefMiddleware.compatibleVersion) as? [String: Any], if let stateDict = UserDefaults.standard
let state = try? dictDecoder.decode(AppState.self, from: stateDict) { .value(forKey: PrefMiddleware.compatibleVersion) as? [String: Any],
let state = try? dictDecoder.decode(AppState.self, from: stateDict)
{
initialAppState = state initialAppState = state
} else { } else {
initialAppState = .default initialAppState = .default
@ -113,8 +111,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
private var launching = true private var launching = true
private let log = OSLog(subsystem: Defs.loggerSubsystem, private let log = OSLog(subsystem: Defs.loggerSubsystem, category: Defs.LoggerCategory.general)
category: Defs.LoggerCategory.general)
private func setSparkleUrl(_ snapshot: Bool) { private func setSparkleUrl(_ snapshot: Bool) {
if snapshot { if snapshot {
@ -130,27 +127,29 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
} }
// MARK: - NSApplicationDelegate // MARK: - NSApplicationDelegate
extension AppDelegate {
extension AppDelegate {
func applicationWillFinishLaunching(_: Notification) { func applicationWillFinishLaunching(_: Notification) {
self.launching = true self.launching = true
let appleEventManager = NSAppleEventManager.shared() let appleEventManager = NSAppleEventManager.shared()
appleEventManager.setEventHandler(self, appleEventManager.setEventHandler(
andSelector: #selector(AppDelegate.handle(getUrlEvent:replyEvent:)), self,
forEventClass: UInt32(kInternetEventClass), andSelector: #selector(AppDelegate.handle(getUrlEvent:replyEvent:)),
andEventID: UInt32(kAEGetURL)) forEventClass: UInt32(kInternetEventClass),
andEventID: UInt32(kAEGetURL)
)
} }
func applicationDidFinishLaunching(_: Notification) { func applicationDidFinishLaunching(_: Notification) {
self.launching = false self.launching = false
#if DEBUG #if DEBUG
NSApp.mainMenu?.items.first { $0.identifier == debugMenuItemIdentifier }?.isHidden = false NSApp.mainMenu?.items.first { $0.identifier == debugMenuItemIdentifier }?.isHidden = false
#endif #endif
} }
func applicationOpenUntitledFile(_ sender: NSApplication) -> Bool { func applicationOpenUntitledFile(_: NSApplication) -> Bool {
if self.launching { if self.launching {
if self.openNewMainWindowOnLaunch { if self.openNewMainWindowOnLaunch {
self.newDocument(self) self.newDocument(self)
@ -166,7 +165,7 @@ extension AppDelegate {
return false return false
} }
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply { func applicationShouldTerminate(_: NSApplication) -> NSApplication.TerminateReply {
self.context.savePrefs() self.context.savePrefs()
guard self.hasMainWindows else { guard self.hasMainWindows else {
@ -210,7 +209,8 @@ extension AppDelegate {
func application(_ sender: NSApplication, openFiles filenames: [String]) { func application(_ sender: NSApplication, openFiles filenames: [String]) {
let urls = filenames.map { URL(fileURLWithPath: $0) } let urls = filenames.map { URL(fileURLWithPath: $0) }
let config = OpenConfig( 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)) self.emit(.newMainWindow(config: config))
@ -226,37 +226,32 @@ extension AppDelegate {
} }
// MARK: - AppleScript // MARK: - AppleScript
extension AppDelegate { 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 url = URL(string: urlString) else { return }
guard let urlString = event.paramDescriptor(forKeyword: UInt32(keyDirectObject))?.stringValue else {
return
}
guard let url = URL(string: urlString) else { guard url.scheme == "vimr" else { return }
return
}
guard url.scheme == "vimr" else { guard let rawAction = url.host else { return }
return
}
guard let rawAction = url.host else { guard let action = VimRUrlAction(rawValue: rawAction) else { return }
return
}
guard let action = VimRUrlAction(rawValue: rawAction) else {
return
}
let rawParams = url.query?.components(separatedBy: "&") ?? [] 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() let alert = NSAlert()
alert.alertStyle = .informational alert.alertStyle = .informational
alert.messageText = "Outdated Command Line Tool?" alert.messageText = "Outdated Command Line Tool?"
alert.informativeText = "It seems that the installed vimr command line tool is outdated." + 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() alert.runModal()
return return
@ -275,7 +270,7 @@ extension AppDelegate {
let envDict: [String: String]? let envDict: [String: String]?
if let envPath = queryParam(envPathPrefix, from: rawParams, transforming: identity).first { 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) { if FileManager.default.fileExists(atPath: envPath) {
do { do {
try FileManager.default.removeItem(atPath: envPath) try FileManager.default.removeItem(atPath: envPath)
@ -287,45 +282,79 @@ extension AppDelegate {
envDict = nil envDict = nil
} }
let line = queryParam(linePrefix, from: rawParams, transforming: { Int($0) }).compactMap { $0 }.first let line = self.queryParam(linePrefix, from: rawParams, transforming: { Int($0) })
let urls = queryParam(filePrefix, from: rawParams, transforming: { URL(fileURLWithPath: $0) }) .compactMap { $0 }.first
let cwd = queryParam(cwdPrefix, let urls = self.queryParam(
from: rawParams, filePrefix,
transforming: { URL(fileURLWithPath: $0) }).first ?? FileUtils.userHomeUrl from: rawParams,
let wait = queryParam(waitPrefix, from: rawParams, transforming: { $0 == "true" ? true : false }).first ?? false 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 { if wait == false { _ = Darwin.close(Darwin.open(pipePath, O_WRONLY)) }
_ = Darwin.close(Darwin.open(pipePath, O_WRONLY))
}
// If we don't do this, the window is active, but not in front. // If we don't do this, the window is active, but not in front.
NSApp.activate(ignoringOtherApps: true) NSApp.activate(ignoringOtherApps: true)
switch action { switch action {
case .activate, .newWindow: 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)) self.emit(.newMainWindow(config: config))
case .open: 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)) self.emit(.openInKeyWindow(config: config))
case .separateWindows: case .separateWindows:
urls.forEach { 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)) self.emit(.newMainWindow(config: config))
} }
case .nvim: case .nvim:
let config = OpenConfig(urls: urls, let config = OpenConfig(
cwd: cwd, urls: urls,
cliPipePath: pipePath, cwd: cwd,
nvimArgs: queryParam(nvimArgsPrefix, from: rawParams, transforming: identity), cliPipePath: pipePath,
envDict: envDict, nvimArgs: queryParam(
line: line) nvimArgsPrefix,
from: rawParams,
transforming: identity
),
envDict: envDict,
line: line
)
self.emit(.newMainWindow(config: config)) self.emit(.newMainWindow(config: config))
} }
} }
@ -345,9 +374,9 @@ extension AppDelegate {
private func queryParam<T>(_ prefix: String, private func queryParam<T>(_ prefix: String,
from rawParams: [String], from rawParams: [String],
transforming transform: (String) -> T) -> [T] { transforming transform: (String) -> T) -> [T]
{
return rawParams rawParams
.filter { $0.hasPrefix(prefix) } .filter { $0.hasPrefix(prefix) }
.compactMap { $0.without(prefix: prefix).removingPercentEncoding } .compactMap { $0.without(prefix: prefix).removingPercentEncoding }
.map(transform) .map(transform)
@ -355,26 +384,22 @@ extension AppDelegate {
} }
// MARK: - IBActions // MARK: - IBActions
extension AppDelegate {
extension AppDelegate {
@IBAction func checkForUpdates(_ sender: Any?) { @IBAction func checkForUpdates(_ sender: Any?) {
updater.checkForUpdates(sender) updater.checkForUpdates(sender)
} }
@IBAction func newDocument(_ sender: Any?) { @IBAction func newDocument(_: Any?) {
let config = OpenConfig( let config = OpenConfig(
urls: [], cwd: FileUtils.userHomeUrl, cliPipePath: nil, nvimArgs: nil, envDict: nil, line: nil urls: [], cwd: FileUtils.userHomeUrl, cliPipePath: nil, nvimArgs: nil, envDict: nil, line: nil
) )
self.emit(.newMainWindow(config: config)) self.emit(.newMainWindow(config: config))
} }
@IBAction func openInNewWindow(_ sender: Any?) { @IBAction func openInNewWindow(_ sender: Any?) { self.openDocument(sender) }
self.openDocument(sender)
}
@IBAction func showPrefWindow(_ sender: Any?) { @IBAction func showPrefWindow(_: Any?) { self.emit(.preferences) }
self.emit(.preferences)
}
// Invoked when no main window is open. // Invoked when no main window is open.
@IBAction func openDocument(_: Any?) { @IBAction func openDocument(_: Any?) {
@ -382,9 +407,7 @@ extension AppDelegate {
panel.canChooseDirectories = true panel.canChooseDirectories = true
panel.allowsMultipleSelection = true panel.allowsMultipleSelection = true
panel.begin { result in panel.begin { result in
guard result == .OK else { guard result == .OK else { return }
return
}
let urls = panel.urls let urls = panel.urls
let commonParentUrl = FileUtils.commonParent(of: urls) let commonParentUrl = FileUtils.commonParent(of: urls)
@ -398,20 +421,21 @@ extension AppDelegate {
} }
// MARK: - NSUserNotificationCenterDelegate // MARK: - NSUserNotificationCenterDelegate
extension AppDelegate {
public func userNotificationCenter(_ center: NSUserNotificationCenter, shouldPresent _: NSUserNotification) -> Bool { extension AppDelegate {
return true func userNotificationCenter(
} _: NSUserNotificationCenter,
shouldPresent _: NSUserNotification
) -> Bool { true }
} }
// Keep the rawValues in sync with Action in the `vimr` Python script. // Keep the rawValues in sync with Action in the `vimr` Python script.
private enum VimRUrlAction: String { private enum VimRUrlAction: String {
case activate = "activate" case activate
case open = "open" case open
case newWindow = "open-in-new-window" case newWindow = "open-in-new-window"
case separateWindows = "open-in-separate-windows" case separateWindows = "open-in-separate-windows"
case nvim = "nvim" case nvim
} }
private let updater = SUUpdater() private let updater = SUUpdater()

View File

@ -6,7 +6,6 @@
import Foundation import Foundation
class AppDelegateReducer: ReducerType { class AppDelegateReducer: ReducerType {
typealias StateType = AppState typealias StateType = AppState
typealias ActionType = AppDelegate.Action typealias ActionType = AppDelegate.Action
@ -18,7 +17,6 @@ class AppDelegateReducer: ReducerType {
var state = pair.state var state = pair.state
switch pair.action { switch pair.action {
case let .newMainWindow(config): case let .newMainWindow(config):
let mainWindow = self.newMainWindow(with: state, config: config) let mainWindow = self.newMainWindow(with: state, config: config)
state.mainWindows[mainWindow.uuid] = mainWindow state.mainWindows[mainWindow.uuid] = mainWindow
@ -30,7 +28,7 @@ class AppDelegateReducer: ReducerType {
break 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 state.mainWindows[uuid]?.cwd = config.cwd
if let line = config.line { if let line = config.line {
state.mainWindows[uuid]?.goToLineFromCli = Marked(line) state.mainWindows[uuid]?.goToLineFromCli = Marked(line)
@ -38,7 +36,6 @@ class AppDelegateReducer: ReducerType {
case .preferences: case .preferences:
state.preferencesOpen = Marked(true) state.preferencesOpen = Marked(true)
} }
return (state, pair.action, true) return (state, pair.action, true)
@ -46,8 +43,9 @@ class AppDelegateReducer: ReducerType {
private let baseServerUrl: URL 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 var mainWindow = state.mainWindowTemplate
mainWindow.uuid = UUID() mainWindow.uuid = UUID()
@ -77,7 +75,7 @@ class AppDelegateReducer: ReducerType {
} }
private func frame(relativeTo refFrame: CGRect) -> CGRect { private func frame(relativeTo refFrame: CGRect) -> CGRect {
return refFrame.offsetBy(dx: cascadeX, dy: -cascadeY) refFrame.offsetBy(dx: cascadeX, dy: -cascadeY)
} }
} }

View File

@ -6,15 +6,11 @@
import Cocoa import Cocoa
import Down import Down
extension NSView { extension NSView {
@objc var isFirstResponder: Bool { self.window?.firstResponder == self } @objc var isFirstResponder: Bool { self.window?.firstResponder == self }
} }
extension NSAttributedString { extension NSAttributedString {
static func infoLabel(markdown: String) -> NSAttributedString { static func infoLabel(markdown: String) -> NSAttributedString {
let down = Down(markdownString: markdown) let down = Down(markdownString: markdown)
guard let result = try? down.toAttributedString(styler: downStyler) else { guard let result = try? down.toAttributedString(styler: downStyler) else {
@ -26,7 +22,6 @@ extension NSAttributedString {
} }
extension NSTableView { extension NSTableView {
static func standardTableView() -> NSTableView { static func standardTableView() -> NSTableView {
let tableView = NSTableView(frame: CGRect.zero) let tableView = NSTableView(frame: CGRect.zero)
@ -53,7 +48,6 @@ extension NSTableView {
} }
extension NSOutlineView { extension NSOutlineView {
static func standardOutlineView() -> NSOutlineView { static func standardOutlineView() -> NSOutlineView {
let outlineView = NSOutlineView(frame: CGRect.zero) let outlineView = NSOutlineView(frame: CGRect.zero)
NSOutlineView.configure(toStandard: outlineView) NSOutlineView.configure(toStandard: outlineView)
@ -99,18 +93,16 @@ extension NSOutlineView {
} }
extension NSTextField { extension NSTextField {
static func defaultTitleTextField() -> NSTextField { static func defaultTitleTextField() -> NSTextField {
let field = NSTextField(forAutoLayout: ()) let field = NSTextField(forAutoLayout: ())
field.backgroundColor = NSColor.clear; field.backgroundColor = NSColor.clear
field.isEditable = false; field.isEditable = false
field.isBordered = false; field.isBordered = false
return field return field
} }
} }
extension NSScrollView { extension NSScrollView {
static func standardScrollView() -> NSScrollView { static func standardScrollView() -> NSScrollView {
let scrollView = NSScrollView(frame: CGRect.zero) let scrollView = NSScrollView(frame: CGRect.zero)
@ -124,8 +116,7 @@ extension NSScrollView {
} }
} }
private class AttributedStringMarkdownStyler { private enum AttributedStringMarkdownStyler {
static func new() -> Styler { static func new() -> Styler {
let fonts = StaticFontCollection( let fonts = StaticFontCollection(
body: NSFont.systemFont(ofSize: NSFont.smallSystemFontSize), body: NSFont.systemFont(ofSize: NSFont.smallSystemFontSize),
@ -138,7 +129,6 @@ private class AttributedStringMarkdownStyler {
} }
private struct ParagraphStyles: ParagraphStyleCollection { private struct ParagraphStyles: ParagraphStyleCollection {
let heading1: NSParagraphStyle let heading1: NSParagraphStyle
let heading2: NSParagraphStyle let heading2: NSParagraphStyle
let heading3: NSParagraphStyle let heading3: NSParagraphStyle
@ -171,7 +161,6 @@ private struct ParagraphStyles: ParagraphStyleCollection {
} }
} }
private let fontCollection = StaticFontCollection( private let fontCollection = StaticFontCollection(
body: NSFont.systemFont(ofSize: NSFont.smallSystemFontSize), body: NSFont.systemFont(ofSize: NSFont.smallSystemFontSize),
code: NSFont.userFixedPitchFont(ofSize: NSFont.smallSystemFontSize)! code: NSFont.userFixedPitchFont(ofSize: NSFont.smallSystemFontSize)!

View File

@ -4,16 +4,14 @@
*/ */
import Cocoa import Cocoa
import NvimView
import PureLayout import PureLayout
import RxSwift import RxSwift
import NvimView
class AppearancePref: PrefPane, NSComboBoxDelegate, NSControlTextEditingDelegate, NSFontChanging { class AppearancePref: PrefPane, NSComboBoxDelegate, NSControlTextEditingDelegate, NSFontChanging {
typealias StateType = AppState typealias StateType = AppState
enum Action { enum Action {
case setUsesColorscheme(Bool) case setUsesColorscheme(Bool)
case setShowsFileIcon(Bool) case setShowsFileIcon(Bool)
case setUsesLigatures(Bool) case setUsesLigatures(Bool)
@ -58,12 +56,12 @@ class AppearancePref: PrefPane, NSComboBoxDelegate, NSControlTextEditingDelegate
let appearance = state.mainWindowTemplate.appearance let appearance = state.mainWindowTemplate.appearance
guard self.font != appearance.font guard self.font != appearance.font
|| self.linespacing != appearance.linespacing || self.linespacing != appearance.linespacing
|| self.characterspacing != appearance.characterspacing || self.characterspacing != appearance.characterspacing
|| self.usesLigatures != appearance.usesLigatures || self.usesLigatures != appearance.usesLigatures
|| self.usesColorscheme != appearance.usesTheme || self.usesColorscheme != appearance.usesTheme
|| self.showsFileIcon != appearance.showsFileIcon || self.showsFileIcon != appearance.showsFileIcon
else { return } else { return }
self.font = appearance.font self.font = appearance.font
self.linespacing = appearance.linespacing self.linespacing = appearance.linespacing
@ -96,15 +94,16 @@ class AppearancePref: PrefPane, NSComboBoxDelegate, NSControlTextEditingDelegate
private let previewArea = NSTextView(frame: .zero) private let previewArea = NSTextView(frame: .zero)
private let exampleText = #""" private let exampleText = #"""
abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ ABCDEFGHIJKLMNOPQRSTUVWXYZ
0123456789 -~ - ~ 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() { private func addViews() {
let paneTitle = self.paneTitleTextField(title: "Appearance") let paneTitle = self.paneTitleTextField(title: "Appearance")
@ -117,9 +116,9 @@ ABCDEFGHIJKLMNOPQRSTUVWXYZ
) )
let useColorschemeInfo = self.infoTextField(markdown: #""" let useColorschemeInfo = self.infoTextField(markdown: #"""
If checked, the colors of the selected `colorscheme` will be used to render tools,\ If checked, the colors of the selected `colorscheme` will be used to render tools,\
for example the file browser. for example the file browser.
"""#) """#)
let fileIcon = self.fileIconCheckbox let fileIcon = self.fileIconCheckbox
self.configureCheckbox( self.configureCheckbox(
@ -129,9 +128,9 @@ for example the file browser.
) )
let fileIconInfo = self.infoTextField(markdown: #""" let fileIconInfo = self.infoTextField(markdown: #"""
In case the selected `colorscheme` does not play well with the file icons\ 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 the file browser and the buffer list, you can turn them off.
"""#) """#)
let fontTitle = self.titleTextField(title: "Default Font:") let fontTitle = self.titleTextField(title: "Default Font:")
let fontPanelButton = self.fontPanelButton 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(_:)) fontPanelButton.action = #selector(AppearancePref.showFontPanel(_:))
let fontInfo = self.infoTextField(markdown: #""" let fontInfo = self.infoTextField(markdown: #"""
The font panel will show variable width fonts, but VimR does not support them.\ 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. If you select a variable width font, the rendering will be ... well ... questionable.
"""#) """#)
let linespacingTitle = self.titleTextField(title: "Line Spacing:") let linespacingTitle = self.titleTextField(title: "Line Spacing:")
let linespacingField = self.linespacingField let linespacingField = self.linespacingField
@ -278,8 +277,9 @@ If you select a variable width font, the rendering will be ... well ... question
} }
private func updateViews() { private func updateViews() {
sharedFontPanel.setPanelFont(font, isMultiple: false) sharedFontPanel.setPanelFont(self.font, isMultiple: false)
self.fontPanelButton.title = font.displayName.map { "\($0) \(font.pointSize)" } ?? "Show fonts..." self.fontPanelButton.title = self.font.displayName
.map { "\($0) \(font.pointSize)" } ?? "Show fonts..."
self.linespacingField.stringValue = String(format: "%.2f", self.linespacing) self.linespacingField.stringValue = String(format: "%.2f", self.linespacing)
self.characterspacingField.stringValue = String(format: "%.2f", self.characterspacing) self.characterspacingField.stringValue = String(format: "%.2f", self.characterspacing)
self.ligatureCheckbox.boolState = self.usesLigatures self.ligatureCheckbox.boolState = self.usesLigatures
@ -296,6 +296,7 @@ If you select a variable width font, the rendering will be ... well ... question
} }
// MARK: - NSFontChanging // MARK: - NSFontChanging
extension AppearancePref { extension AppearancePref {
func changeFont(_ sender: NSFontManager?) { func changeFont(_ sender: NSFontManager?) {
guard let fontManager = sender else { return } guard let fontManager = sender else { return }
@ -306,8 +307,8 @@ extension AppearancePref {
} }
// MARK: - Actions // MARK: - Actions
extension AppearancePref {
extension AppearancePref {
@objc func usesColorschemeAction(_ sender: NSButton) { @objc func usesColorschemeAction(_ sender: NSButton) {
self.emit(.setUsesColorscheme(sender.boolState)) self.emit(.setUsesColorscheme(sender.boolState))
} }
@ -351,7 +352,6 @@ extension AppearancePref {
return cgfCharacterspacing return cgfCharacterspacing
} }
private func cappedFontSize(_ size: Int) -> CGFloat { private func cappedFontSize(_ size: Int) -> CGFloat {
let cgfSize = size.cgf let cgfSize = size.cgf

View File

@ -6,7 +6,6 @@
import Foundation import Foundation
class AppearancePrefReducer: ReducerType { class AppearancePrefReducer: ReducerType {
typealias StateType = AppState typealias StateType = AppState
typealias ActionType = AppearancePref.Action typealias ActionType = AppearancePref.Action
@ -15,7 +14,6 @@ class AppearancePrefReducer: ReducerType {
var appearance = state.mainWindowTemplate.appearance var appearance = state.mainWindowTemplate.appearance
switch pair.action { switch pair.action {
case let .setUsesColorscheme(value): case let .setUsesColorscheme(value):
appearance.usesTheme = value appearance.usesTheme = value
@ -30,10 +28,9 @@ class AppearancePrefReducer: ReducerType {
case let .setLinespacing(linespacing): case let .setLinespacing(linespacing):
appearance.linespacing = linespacing appearance.linespacing = linespacing
case let .setCharacterspacing(characterspacing): case let .setCharacterspacing(characterspacing):
appearance.characterspacing = characterspacing appearance.characterspacing = characterspacing
} }
self.modify(state: &state, with: appearance) self.modify(state: &state, with: appearance)

View File

@ -7,7 +7,6 @@ import Cocoa
import Sparkle import Sparkle
class Application: NSApplication { class Application: NSApplication {
override init() { override init() {
setPressAndHoldSetting() setPressAndHoldSetting()
super.init() super.init()

View File

@ -4,21 +4,20 @@
*/ */
import Cocoa import Cocoa
import RxSwift
import PureLayout
import NvimView
import Commons import Commons
import NvimView
import PureLayout
import RxSwift
class BuffersList: NSView, class BuffersList: NSView,
UiComponent, UiComponent,
NSTableViewDataSource, NSTableViewDataSource,
NSTableViewDelegate, NSTableViewDelegate,
ThemedView { ThemedView
{
typealias StateType = MainWindow.State typealias StateType = MainWindow.State
enum Action { enum Action {
case open(NvimView.Buffer) case open(NvimView.Buffer)
} }
@ -50,7 +49,8 @@ class BuffersList: NSView,
.observeOn(MainScheduler.instance) .observeOn(MainScheduler.instance)
.subscribe(onNext: { state in .subscribe(onNext: { state in
if state.viewToBeFocused != nil, if state.viewToBeFocused != nil,
case .bufferList = state.viewToBeFocused! { case .bufferList = state.viewToBeFocused!
{
self.beFirstResponder() self.beFirstResponder()
} }
@ -59,13 +59,15 @@ class BuffersList: NSView,
themeChanged: state.appearance.theme.mark != self.lastThemeMark, themeChanged: state.appearance.theme.mark != self.lastThemeMark,
usesTheme: state.appearance.usesTheme, usesTheme: state.appearance.usesTheme,
forTheme: { self.updateTheme(state.appearance.theme) }, forTheme: { self.updateTheme(state.appearance.theme) },
forDefaultTheme: { self.updateTheme(Marked(Theme.default)) }) forDefaultTheme: { self.updateTheme(Marked(Theme.default)) }
)
self.usesTheme = state.appearance.usesTheme self.usesTheme = state.appearance.usesTheme
if self.buffers == state.buffers if self.buffers == state.buffers,
&& !themeChanged !themeChanged,
&& self.showsFileIcon == state.appearance.showsFileIcon { self.showsFileIcon == state.appearance.showsFileIcon
{
return return
} }
@ -87,7 +89,8 @@ class BuffersList: NSView,
private var buffers = [NvimView.Buffer]() private var buffers = [NvimView.Buffer]()
required init?(coder: NSCoder) { @available(*, unavailable)
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
@ -109,11 +112,11 @@ class BuffersList: NSView,
} }
// MARK: - Actions // MARK: - Actions
extension BuffersList {
@objc func doubleClickAction(_ sender: Any?) { extension BuffersList {
@objc func doubleClickAction(_: Any?) {
let clickedRow = self.bufferList.clickedRow let clickedRow = self.bufferList.clickedRow
guard clickedRow >= 0 && clickedRow < self.buffers.count else { guard clickedRow >= 0, clickedRow < self.buffers.count else {
return return
} }
@ -122,31 +125,33 @@ extension BuffersList {
} }
// MARK: - NSTableViewDataSource // MARK: - NSTableViewDataSource
extension BuffersList {
extension BuffersList {
@objc(numberOfRowsInTableView:) @objc(numberOfRowsInTableView:)
func numberOfRows(in tableView: NSTableView) -> Int { func numberOfRows(in _: NSTableView) -> Int {
return self.buffers.count self.buffers.count
} }
} }
// MARK: - NSTableViewDelegate // MARK: - NSTableViewDelegate
extension BuffersList {
extension BuffersList {
public func tableView( public func tableView(
_ tableView: NSTableView, _ tableView: NSTableView,
rowViewForRow row: Int rowViewForRow _: Int
) -> NSTableRowView? { ) -> NSTableRowView? {
return tableView.makeView( tableView.makeView(
withIdentifier: NSUserInterfaceItemIdentifier("buffer-row-view"), withIdentifier: NSUserInterfaceItemIdentifier("buffer-row-view"),
owner: self owner: self
) as? ThemedTableRow ?? ThemedTableRow(withIdentifier: "buffer-row-view", ) as? ThemedTableRow ?? ThemedTableRow(
themedView: self) withIdentifier: "buffer-row-view",
themedView: self
)
} }
func tableView( func tableView(
_ tableView: NSTableView, _ tableView: NSTableView,
viewFor tableColumn: NSTableColumn?, viewFor _: NSTableColumn?,
row: Int row: Int
) -> NSView? { ) -> NSView? {
let cachedCell = (tableView.makeView( let cachedCell = (tableView.makeView(
@ -169,13 +174,13 @@ extension BuffersList {
} }
func tableView( func tableView(
_ tableView: NSTableView, _: NSTableView,
didAdd rowView: NSTableRowView, didAdd rowView: NSTableRowView,
forRow row: Int forRow _: Int
) { ) {
guard let cellWidth = (rowView.view(atColumn: 0) as? NSTableCellView)? guard let cellWidth = (rowView.view(atColumn: 0) as? NSTableCellView)?
.fittingSize.width .fittingSize.width
else { else {
return return
} }
@ -194,15 +199,17 @@ extension BuffersList {
} }
let pathInfo = url.pathComponents let pathInfo = url.pathComponents
.dropFirst() .dropFirst()
.dropLast() .dropLast()
.reversed() .reversed()
.joined(separator: " / ") + " /" .joined(separator: " / ") + " /"
let rowText = NSMutableAttributedString(string: "\(name)\(pathInfo)") let rowText = NSMutableAttributedString(string: "\(name)\(pathInfo)")
rowText.addAttribute(NSAttributedString.Key.foregroundColor, rowText.addAttribute(
value: self.theme.foreground, NSAttributedString.Key.foregroundColor,
range: NSRange(location: 0, length: name.count)) value: self.theme.foreground,
range: NSRange(location: 0, length: name.count)
)
rowText.addAttribute( rowText.addAttribute(
NSAttributedString.Key.foregroundColor, NSAttributedString.Key.foregroundColor,

View File

@ -6,7 +6,6 @@
import Foundation import Foundation
class BuffersListReducer: ReducerType { class BuffersListReducer: ReducerType {
typealias StateType = MainWindow.State typealias StateType = MainWindow.State
typealias ActionType = UuidAction<BuffersList.Action> typealias ActionType = UuidAction<BuffersList.Action>
@ -14,10 +13,8 @@ class BuffersListReducer: ReducerType {
var state = tuple.state var state = tuple.state
switch tuple.action.payload { switch tuple.action.payload {
case let .open(buffer): case let .open(buffer):
state.currentBufferToSet = buffer state.currentBufferToSet = buffer
} }
return (state, tuple.action, true) return (state, tuple.action, true)

View File

@ -8,13 +8,11 @@ import RxSwift
typealias AnyAction = Any typealias AnyAction = Any
extension ReduxTypes { extension ReduxTypes {
typealias StateType = AppState typealias StateType = AppState
typealias ActionType = AnyAction typealias ActionType = AnyAction
} }
class Context: ReduxContext { class Context: ReduxContext {
// The following should only be used when Cmd-Q'ing // The following should only be used when Cmd-Q'ing
func savePrefs() { self.prefMiddleware.applyPref(from: self.state) } func savePrefs() { self.prefMiddleware.applyPref(from: self.state) }
@ -24,7 +22,7 @@ class Context: ReduxContext {
let markdownPreviewMiddleware = MarkdownPreviewMiddleware() let markdownPreviewMiddleware = MarkdownPreviewMiddleware()
let markdownPreviewReducer = MarkdownPreviewReducer(baseServerUrl: baseServerUrl) let markdownPreviewReducer = MarkdownPreviewReducer(baseServerUrl: baseServerUrl)
let htmlPreviewReducer = HtmlPreviewReducer(baseServerUrl: baseServerUrl) let htmlPreviewReducer = HtmlPreviewReducer(baseServerUrl: baseServerUrl)
let httpMiddleware: HttpServerMiddleware = HttpServerMiddleware(port: baseServerUrl.port!) let httpMiddleware = HttpServerMiddleware(port: baseServerUrl.port!)
let uiRootReducer = UiRootReducer() let uiRootReducer = UiRootReducer()
let openQuicklyReducer = OpenQuicklyReducer() let openQuicklyReducer = OpenQuicklyReducer()
let rpcEpic = RpcAppearanceEpic(emitter: self.actionEmitter) let rpcEpic = RpcAppearanceEpic(emitter: self.actionEmitter)
@ -52,7 +50,8 @@ class Context: ReduxContext {
self.prefMiddleware.mainWindow.apply, self.prefMiddleware.mainWindow.apply,
self.prefMiddleware.apply, self.prefMiddleware.apply,
rpcEpic.apply, rpcEpic.apply,
]) ]
)
.filter { $0.modified } .filter { $0.modified }
.subscribe(onNext: self.emitAppState) .subscribe(onNext: self.emitAppState)
.disposed(by: self.disposeBag) .disposed(by: self.disposeBag)

View File

@ -3,15 +3,13 @@
* See LICENSE * See LICENSE
*/ */
import Foundation
import CoreData
import os
import Commons import Commons
import CoreData
import Foundation
import os
class CoreDataStack { class CoreDataStack {
enum Error: Swift.Error { enum Error: Swift.Error {
case noCacheFolder case noCacheFolder
case pathDoesNotExit case pathDoesNotExit
case pathNotFolder case pathNotFolder
@ -20,7 +18,6 @@ class CoreDataStack {
} }
enum StoreLocation { enum StoreLocation {
case temp(String) case temp(String)
case cache(String) case cache(String)
case path(String) case path(String)
@ -45,15 +42,14 @@ class CoreDataStack {
let fileManager = FileManager.default let fileManager = FileManager.default
let url: URL let url: URL
switch storeLocation { switch storeLocation {
case let .temp(folderName):
case .temp(let folderName):
let parentUrl = fileManager let parentUrl = fileManager
.temporaryDirectory .temporaryDirectory
.appendingPathComponent(folderName) .appendingPathComponent(folderName)
try fileManager.createDirectory(at: parentUrl, withIntermediateDirectories: true) try fileManager.createDirectory(at: parentUrl, withIntermediateDirectories: true)
url = parentUrl.appendingPathComponent(modelName) url = parentUrl.appendingPathComponent(modelName)
case .cache(let folderName): case let .cache(folderName):
guard let cacheUrl = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first else { guard let cacheUrl = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first else {
throw Error.noCacheFolder throw Error.noCacheFolder
} }
@ -62,14 +58,13 @@ class CoreDataStack {
url = parentUrl.appendingPathComponent(modelName) url = parentUrl.appendingPathComponent(modelName)
case .path(let path): case let .path(path):
guard fileManager.fileExists(atPath: path) else { throw Error.pathDoesNotExit } guard fileManager.fileExists(atPath: path) else { throw Error.pathDoesNotExit }
let parentFolder = URL(fileURLWithPath: path) let parentFolder = URL(fileURLWithPath: path)
guard parentFolder.isDir else { throw Error.pathNotFolder } guard parentFolder.isDir else { throw Error.pathNotFolder }
url = parentFolder.appendingPathComponent(modelName) url = parentFolder.appendingPathComponent(modelName)
} }
self.container.persistentStoreDescriptions = [NSPersistentStoreDescription(url: url)] self.container.persistentStoreDescriptions = [NSPersistentStoreDescription(url: url)]

View File

@ -6,7 +6,6 @@
import Cocoa import Cocoa
class CssUtils { class CssUtils {
static let cssOverridesTemplate: String = try! String( static let cssOverridesTemplate: String = try! String(
contentsOf: Resources.cssOverridesTemplateUrl contentsOf: Resources.cssOverridesTemplateUrl
) )
@ -15,22 +14,36 @@ class CssUtils {
self self
.cssOverridesTemplate .cssOverridesTemplate
.replacingOccurrences(of: "{{ nvim-color }}", with: self.htmlColor(theme.cssColor)) .replacingOccurrences(of: "{{ nvim-color }}", with: self.htmlColor(theme.cssColor))
.replacingOccurrences(of: "{{ nvim-background-color }}", .replacingOccurrences(
with: self.htmlColor(theme.cssBackgroundColor)) of: "{{ nvim-background-color }}",
with: self.htmlColor(theme.cssBackgroundColor)
)
.replacingOccurrences(of: "{{ nvim-a }}", with: self.htmlColor(theme.cssA)) .replacingOccurrences(of: "{{ nvim-a }}", with: self.htmlColor(theme.cssA))
.replacingOccurrences(of: "{{ nvim-hr-background-color }}", .replacingOccurrences(
with: self.htmlColor(theme.cssHrBorderBackgroundColor)) of: "{{ nvim-hr-background-color }}",
.replacingOccurrences(of: "{{ nvim-hr-border-bottom-color }}", with: self.htmlColor(theme.cssHrBorderBackgroundColor)
with: self.htmlColor(theme.cssHrBorderBottomColor)) )
.replacingOccurrences(of: "{{ nvim-blockquote-border-left-color }}", .replacingOccurrences(
with: self.htmlColor(theme.cssBlockquoteBorderLeftColor)) of: "{{ nvim-hr-border-bottom-color }}",
.replacingOccurrences(of: "{{ nvim-blockquote-color }}", with: self.htmlColor(theme.cssHrBorderBottomColor)
with: self.htmlColor(theme.cssBlockquoteColor)) )
.replacingOccurrences(of: "{{ nvim-h2-border-bottom-color }}", .replacingOccurrences(
with: self.htmlColor(theme.cssH2BorderBottomColor)) 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-h6-color }}", with: self.htmlColor(theme.cssH6Color))
.replacingOccurrences(of: "{{ nvim-code-background-color }}", .replacingOccurrences(
with: self.htmlColor(theme.cssCodeBackgroundColor)) of: "{{ nvim-code-background-color }}",
with: self.htmlColor(theme.cssCodeBackgroundColor)
)
.replacingOccurrences(of: "{{ nvim-code-color }}", with: self.htmlColor(theme.cssCodeColor)) .replacingOccurrences(of: "{{ nvim-code-color }}", with: self.htmlColor(theme.cssCodeColor))
} }

View File

@ -7,7 +7,6 @@ import Foundation
import RxSwift import RxSwift
class Debouncer<T> { class Debouncer<T> {
let observable: Observable<T> let observable: Observable<T>
init(interval: RxTimeInterval) { init(interval: RxTimeInterval) {

View File

@ -7,11 +7,9 @@ import Foundation
import WebKit import WebKit
struct Defs { struct Defs {
static let loggerSubsystem = Bundle.main.bundleIdentifier! static let loggerSubsystem = Bundle.main.bundleIdentifier!
struct LoggerCategory { enum LoggerCategory {
static let general = "general" static let general = "general"
static let ui = "ui" static let ui = "ui"

View File

@ -4,19 +4,18 @@
*/ */
import Cocoa import Cocoa
import RxSwift
import PureLayout
import MaterialIcons
import Commons import Commons
import MaterialIcons
import PureLayout
import RxSwift
import Workspace import Workspace
class FileBrowser: NSView, class FileBrowser: NSView,
UiComponent { UiComponent
{
typealias StateType = MainWindow.State typealias StateType = MainWindow.State
enum Action { enum Action {
case open(url: URL, mode: MainWindow.OpenMode) case open(url: URL, mode: MainWindow.OpenMode)
case setAsWorkingDirectory(URL) case setAsWorkingDirectory(URL)
case setShowHidden(Bool) case setShowHidden(Bool)
@ -41,8 +40,8 @@ class FileBrowser: NSView,
action: #selector(FileBrowser.showHiddenAction), action: #selector(FileBrowser.showHiddenAction),
keyEquivalent: "" keyEquivalent: ""
) )
showHiddenMenuItem.boolState = state.fileBrowserShowHidden self.showHiddenMenuItem.boolState = state.fileBrowserShowHidden
self.menuItems = [showHiddenMenuItem] self.menuItems = [self.showHiddenMenuItem]
super.init(frame: .zero) super.init(frame: .zero)
@ -78,7 +77,8 @@ class FileBrowser: NSView,
private var cwd: URL 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() { private func addViews() {
let scrollView = NSScrollView.standardScrollView() let scrollView = NSScrollView.standardScrollView()
@ -91,9 +91,7 @@ class FileBrowser: NSView,
} }
extension FileBrowser { extension FileBrowser {
class InnerCustomToolbar: CustomToolBar { class InnerCustomToolbar: CustomToolBar {
let goToParentButton = NSButton(forAutoLayout: ()) let goToParentButton = NSButton(forAutoLayout: ())
let scrollToSourceButton = NSButton(forAutoLayout: ()) let scrollToSourceButton = NSButton(forAutoLayout: ())
let refreshButton = 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 // MARK: - Actions
extension FileBrowser {
extension FileBrowser {
@objc func showHiddenAction(_ sender: Any?) { @objc func showHiddenAction(_ sender: Any?) {
guard let menuItem = sender as? NSMenuItem else { return } guard let menuItem = sender as? NSMenuItem else { return }
self.emit(UuidAction(uuid: self.uuid, action: .setShowHidden(!menuItem.boolState))) 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))) 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 } guard let url = self.currentBufferUrl else { return }
self.fileView.select(url) self.fileView.select(url)
} }
@objc func refreshAction(_ sender: Any?) { @objc func refreshAction(_: Any?) {
self.emit(UuidAction(uuid: self.uuid, action: .refresh)) self.emit(UuidAction(uuid: self.uuid, action: .refresh))
} }
} }

View File

@ -6,7 +6,6 @@
import Foundation import Foundation
class FileBrowserReducer: ReducerType { class FileBrowserReducer: ReducerType {
typealias StateType = MainWindow.State typealias StateType = MainWindow.State
typealias ActionType = UuidAction<FileBrowser.Action> typealias ActionType = UuidAction<FileBrowser.Action>
@ -14,7 +13,6 @@ class FileBrowserReducer: ReducerType {
var state = tuple.state var state = tuple.state
switch tuple.action.payload { switch tuple.action.payload {
case let .open(url, mode): case let .open(url, mode):
state.urlsToOpen[url] = mode state.urlsToOpen[url] = mode
state.viewToBeFocused = .neoVimView state.viewToBeFocused = .neoVimView
@ -27,7 +25,6 @@ class FileBrowserReducer: ReducerType {
case .refresh: case .refresh:
state.lastFileSystemUpdate = Marked(state.cwd) state.lastFileSystemUpdate = Marked(state.cwd)
} }
return (state, tuple.action, true) return (state, tuple.action, true)

View File

@ -3,22 +3,21 @@
* See LICENSE * See LICENSE
*/ */
import Foundation
import EonilFSEvents
import os
import Commons import Commons
import EonilFSEvents
import Foundation
import os
class FileMonitor { class FileMonitor {
static let fileSystemEventsLatency = 1.0 static let fileSystemEventsLatency = 1.0
private(set) var urlToMonitor = FileUtils.userHomeUrl 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.stopMonitor()
self.urlToMonitor = url self.urlToMonitor = url
self.monitor = try EonilFSEventStream( self.monitor = try EonilFSEventStream(
pathsToWatch: [urlToMonitor.path], pathsToWatch: [self.urlToMonitor.path],
sinceWhen: EonilFSEventsEventID.getCurrentEventId(), sinceWhen: EonilFSEventsEventID.getCurrentEventId(),
latency: FileMonitor.fileSystemEventsLatency, latency: FileMonitor.fileSystemEventsLatency,
flags: [], flags: [],

View File

@ -4,18 +4,18 @@
*/ */
import Cocoa import Cocoa
import Commons
import MaterialIcons
import NvimView import NvimView
import os
import PureLayout import PureLayout
import RxSwift import RxSwift
import MaterialIcons
import os
import Commons
class FileOutlineView: NSOutlineView, class FileOutlineView: NSOutlineView,
UiComponent, UiComponent,
NSOutlineViewDelegate, NSOutlineViewDelegate,
ThemedView { ThemedView
{
typealias StateType = MainWindow.State typealias StateType = MainWindow.State
@objc dynamic var content = [Node]() @objc dynamic var content = [Node]()
@ -150,7 +150,8 @@ class FileOutlineView: NSOutlineView,
self.unbind(.selectionIndexPaths) 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) { private func handleFileSystemChanges(_ changedUrl: URL) {
DispatchQueue.main.async { DispatchQueue.main.async {
@ -206,7 +207,7 @@ class FileOutlineView: NSOutlineView,
self.treeController.preservesSelection = true self.treeController.preservesSelection = true
self.treeController.sortDescriptors = [ self.treeController.sortDescriptors = [
NSSortDescriptor(key: "isLeaf", ascending: true), // Folders first, 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 // 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") 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 } if url == self.cwd { return self.treeController.arrangedObjects }
let cwdCompsCount = self.cwd.pathComponents.count let cwdCompsCount = self.cwd.pathComponents.count
@ -226,12 +227,12 @@ class FileOutlineView: NSOutlineView,
let rootTreeNode = self.treeController.arrangedObjects let rootTreeNode = self.treeController.arrangedObjects
let changedTreeNode = comps.reduce(rootTreeNode) { prev, comp in 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 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 return changedTreeNode
} }
@ -256,12 +257,12 @@ class FileOutlineView: NSOutlineView,
guard self.treeController.isEditable else { return } guard self.treeController.isEditable else { return }
let indexPathsToRemove = changeTreeNode let indexPathsToRemove = changeTreeNode
.children? .children?
.filter { child in .filter { child in
guard let url = child.node?.url else { return true } guard let url = child.node?.url else { return true }
return newChildUrls.contains(url) == false return newChildUrls.contains(url) == false
} }
.map { $0.indexPath } ?? [] .map(\.indexPath) ?? []
changeTreeNode changeTreeNode
.children? .children?
@ -276,7 +277,7 @@ class FileOutlineView: NSOutlineView,
private func childUrls(for url: URL) -> [URL] { private func childUrls(for url: URL) -> [URL] {
let urls = FileUtils.directDescendants(of: url).sorted { lhs, rhs in let urls = FileUtils.directDescendants(of: url).sorted { lhs, rhs in
return lhs.lastPathComponent < rhs.lastPathComponent lhs.lastPathComponent < rhs.lastPathComponent
} }
if self.isShowHidden { return urls } if self.isShowHidden { return urls }
@ -345,8 +346,8 @@ class FileOutlineView: NSOutlineView,
} }
// MARK: - Actions // MARK: - Actions
extension FileOutlineView {
extension FileOutlineView {
@IBAction func doubleClickAction(_: Any?) { @IBAction func doubleClickAction(_: Any?) {
let clickedTreeNode = self.clickedItem let clickedTreeNode = self.clickedItem
guard let node = self.node(from: clickedTreeNode) else { return } guard let node = self.node(from: clickedTreeNode) else { return }
@ -392,9 +393,9 @@ extension FileOutlineView {
} }
// MARK: - NSOutlineViewDelegate // MARK: - NSOutlineViewDelegate
extension FileOutlineView {
func outlineView(_ outlineView: NSOutlineView, rowViewForItem item: Any) -> NSTableRowView? { extension FileOutlineView {
func outlineView(_: NSOutlineView, rowViewForItem _: Any) -> NSTableRowView? {
let view = self.makeView( let view = self.makeView(
withIdentifier: NSUserInterfaceItemIdentifier("file-row-view"), withIdentifier: NSUserInterfaceItemIdentifier("file-row-view"),
owner: self owner: self
@ -403,7 +404,7 @@ extension FileOutlineView {
return view 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 } guard let node = self.node(from: item) else { return nil }
let cellView = self.makeView( let cellView = self.makeView(
@ -422,9 +423,9 @@ extension FileOutlineView {
return cellView 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 } guard let node = self.node(from: item) else { return false }
if node.isChildrenScanned { return true } if node.isChildrenScanned { return true }
@ -434,7 +435,7 @@ extension FileOutlineView {
return true 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 { guard let cellWidth = (rowView.view(atColumn: 0) as? NSTableCellView)?.fittingSize.width else {
return return
} }
@ -442,7 +443,7 @@ extension FileOutlineView {
let level = self.level(forRow: row).cgf let level = self.level(forRow: row).cgf
let width = level * self.indentationPerLevel + cellWidth + columnWidthRightPadding let width = level * self.indentationPerLevel + cellWidth + columnWidthRightPadding
self.cachedColumnWidth = max(self.cachedColumnWidth, width) self.cachedColumnWidth = max(self.cachedColumnWidth, width)
self.tableColumns[0].width = cachedColumnWidth self.tableColumns[0].width = self.cachedColumnWidth
let rv = rowView as? ThemedTableRow let rv = rowView as? ThemedTableRow
guard rv?.themeToken != self.lastThemeMark else { return } guard rv?.themeToken != self.lastThemeMark else { return }
@ -455,20 +456,20 @@ extension FileOutlineView {
} }
// MARK: - NSUserInterfaceValidations // MARK: - NSUserInterfaceValidations
extension FileOutlineView {
extension FileOutlineView {
override func validateUserInterfaceItem(_ item: NSValidatedUserInterfaceItem) -> Bool { override func validateUserInterfaceItem(_ item: NSValidatedUserInterfaceItem) -> Bool {
guard let clickedNode = self.node(from: self.clickedItem) else { return true } 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 return true
} }
} }
// MARK: - NSView // MARK: - NSView
extension FileOutlineView {
extension FileOutlineView {
override func keyDown(with event: NSEvent) { override func keyDown(with event: NSEvent) {
guard let char = event.charactersIgnoringModifiers?.first else { guard let char = event.charactersIgnoringModifiers?.first else {
super.keyDown(with: event) super.keyDown(with: event)
@ -495,8 +496,7 @@ extension FileOutlineView {
} }
class Node: NSObject, Comparable { 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 url: URL
@objc dynamic var isLeaf: Bool @objc dynamic var isLeaf: Bool
@ -521,7 +521,6 @@ class Node: NSObject, Comparable {
} }
private extension NSTreeNode { private extension NSTreeNode {
var node: Node? { self.representedObject as? Node } var node: Node? { self.representedObject as? Node }
} }

View File

@ -7,6 +7,5 @@ import Foundation
import os import os
extension URL { extension URL {
var direntType: UInt8 { (self as NSURL).direntType() } var direntType: UInt8 { (self as NSURL).direntType() }
} }

View File

@ -7,7 +7,6 @@ import Foundation
import os import os
class FuzzyMatcherPool { class FuzzyMatcherPool {
let pattern: String let pattern: String
init(pattern: String, initialPoolSize: Int = 2) { init(pattern: String, initialPoolSize: Int = 2) {
@ -17,7 +16,7 @@ class FuzzyMatcherPool {
} }
func request() -> FuzzyMatcher { func request() -> FuzzyMatcher {
return self.lock.withLock { self.lock.withLock {
if self.matchers.isEmpty { if self.matchers.isEmpty {
let matcher = FuzzyMatcher(pattern: self.pattern) let matcher = FuzzyMatcher(pattern: self.pattern)
return matcher return matcher
@ -45,7 +44,6 @@ class FuzzyMatcherPool {
} }
private extension NSLocking { private extension NSLocking {
func withLock<T>(_ body: () -> T) -> T { func withLock<T>(_ body: () -> T) -> T {
self.lock() self.lock()
defer { self.unlock() } defer { self.unlock() }

View File

@ -3,13 +3,12 @@
* See LICENSE * See LICENSE
*/ */
import Foundation
import CoreData
import os
import Commons import Commons
import CoreData
import Foundation
import os
class FuzzySearchService { class FuzzySearchService {
typealias ScoredUrlsCallback = ([ScoredUrl]) -> Void typealias ScoredUrlsCallback = ([ScoredUrl]) -> Void
var root: URL { var root: URL {
@ -101,7 +100,7 @@ class FuzzySearchService {
fetchReq.predicate = predicate fetchReq.predicate = predicate
let chunkCount = Int(ceil(Double(count) / Double(coreDataBatchSize))) let chunkCount = Int(ceil(Double(count) / Double(coreDataBatchSize)))
for chunkIndex in (0..<chunkCount) { for chunkIndex in 0..<chunkCount {
if self.shouldStop({ context.reset() }) { return } if self.shouldStop({ context.reset() }) { return }
let start = Swift.min(chunkIndex * coreDataBatchSize, count) let start = Swift.min(chunkIndex * coreDataBatchSize, count)
@ -123,7 +122,7 @@ class FuzzySearchService {
private func scanScoreFilesNeedScanning( private func scanScoreFilesNeedScanning(
matcherPool: FuzzyMatcherPool, matcherPool: FuzzyMatcherPool,
context: NSManagedObjectContext, context: NSManagedObjectContext,
callback: ([ScoredUrl]) -> () callback: ([ScoredUrl]) -> Void
) { ) {
let req = self.fileFetchRequest("needsScanChildren == TRUE AND direntType == %d", [DT_DIR]) let req = self.fileFetchRequest("needsScanChildren == TRUE AND direntType == %d", [DT_DIR])
do { do {
@ -147,7 +146,7 @@ class FuzzySearchService {
matcherPool: FuzzyMatcherPool, matcherPool: FuzzyMatcherPool,
folderId: NSManagedObjectID, folderId: NSManagedObjectID,
context: NSManagedObjectContext, context: NSManagedObjectContext,
callback: ([ScoredUrl]) -> () callback: ([ScoredUrl]) -> Void
) { ) {
var saveCounter = 1 var saveCounter = 1
var counter = 1 var counter = 1
@ -180,7 +179,9 @@ class FuzzySearchService {
let childFiles = childUrls let childFiles = childUrls
.filter { !$0.isPackage } .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 saveCounter += childFiles.count
counter += 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. // 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) // Since objectID survives NSManagedObjectContext.reset(), we can re-populate (re-fetch)
// stack using the objectIDs. // stack using the objectIDs.
let ids = stack.map { $0.1.objectID } let ids = stack.map(\.1.objectID)
stack = Array(zip( stack = Array(zip(
stack.map { $0.0 }, stack.map(\.0),
ids.map { context.object(with: $0) as! FileItem } ids.map { context.object(with: $0) as! FileItem }
)) ))
} }
@ -249,7 +250,7 @@ class FuzzySearchService {
private func scoreAllRegisteredFiles( private func scoreAllRegisteredFiles(
matcherPool: FuzzyMatcherPool, matcherPool: FuzzyMatcherPool,
context: NSManagedObjectContext, context: NSManagedObjectContext,
callback: ([ScoredUrl]) -> () callback: ([ScoredUrl]) -> Void
) { ) {
let files = context.registeredObjects let files = context.registeredObjects
.compactMap { $0 as? FileItem } .compactMap { $0 as? FileItem }
@ -338,7 +339,7 @@ class FuzzySearchService {
} catch { } catch {
self.log.error( self.log.error(
"Could not fetch File with url \(folderUrl) " "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() { 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 { self.queue.async {
let moc = self.writeContext let moc = self.writeContext

View File

@ -8,11 +8,9 @@ import PureLayout
import RxSwift import RxSwift
class GeneralPref: PrefPane, UiComponent, NSTextFieldDelegate { class GeneralPref: PrefPane, UiComponent, NSTextFieldDelegate {
typealias StateType = AppState typealias StateType = AppState
enum Action { enum Action {
case setOpenOnLaunch(Bool) case setOpenOnLaunch(Bool)
case setAfterLastWindowAction(AppState.AfterLastWindowAction) case setAfterLastWindowAction(AppState.AfterLastWindowAction)
case setOpenOnReactivation(Bool) case setOpenOnReactivation(Bool)
@ -20,11 +18,11 @@ class GeneralPref: PrefPane, UiComponent, NSTextFieldDelegate {
} }
override var displayName: String { override var displayName: String {
return "General" "General"
} }
override var pinToContainer: Bool { override var pinToContainer: Bool {
return true true
} }
required init(source: Observable<StateType>, emitter: ActionEmitter, state: StateType) { 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.defaultUsesVcsIgnoresCheckbox.boolState = state.openQuickly.defaultUsesVcsIgnores
self.lastWindowAction = state.afterLastWindowAction 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 source
.observeOn(MainScheduler.instance) .observeOn(MainScheduler.instance)
@ -73,7 +72,8 @@ class GeneralPref: PrefPane, UiComponent, NSTextFieldDelegate {
private let afterLastWindowPopup = NSPopUpButton(forAutoLayout: ()) private let afterLastWindowPopup = NSPopUpButton(forAutoLayout: ())
required init?(coder: NSCoder) { @available(*, unavailable)
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
@ -81,15 +81,21 @@ class GeneralPref: PrefPane, UiComponent, NSTextFieldDelegate {
let paneTitle = self.paneTitleTextField(title: "General") let paneTitle = self.paneTitleTextField(title: "General")
let openUntitledWindowTitle = self.titleTextField(title: "Open Untitled Window:") let openUntitledWindowTitle = self.titleTextField(title: "Open Untitled Window:")
self.configureCheckbox(button: self.openWhenLaunchingCheckbox, self.configureCheckbox(
title: "On launch", button: self.openWhenLaunchingCheckbox,
action: #selector(GeneralPref.openUntitledWindowWhenLaunchingAction)) title: "On launch",
self.configureCheckbox(button: self.openOnReactivationCheckbox, action: #selector(GeneralPref.openUntitledWindowWhenLaunchingAction)
title: "On re-activation", )
action: #selector(GeneralPref.openUntitledWindowOnReactivationAction)) self.configureCheckbox(
self.configureCheckbox(button: self.defaultUsesVcsIgnoresCheckbox, button: self.openOnReactivationCheckbox,
title: "Use VCS Ignores", title: "On re-activation",
action: #selector(GeneralPref.defaultUsesVcsIgnoresAction)) action: #selector(GeneralPref.openUntitledWindowOnReactivationAction)
)
self.configureCheckbox(
button: self.defaultUsesVcsIgnoresCheckbox,
title: "Use VCS Ignores",
action: #selector(GeneralPref.defaultUsesVcsIgnoresAction)
)
let whenLaunching = self.openWhenLaunchingCheckbox let whenLaunching = self.openWhenLaunchingCheckbox
let onReactivation = self.openOnReactivationCheckbox let onReactivation = self.openOnReactivationCheckbox
@ -107,12 +113,12 @@ class GeneralPref: PrefPane, UiComponent, NSTextFieldDelegate {
let ignoreListTitle = self.titleTextField(title: "Open Quickly:") let ignoreListTitle = self.titleTextField(title: "Open Quickly:")
let ignoreInfo = let ignoreInfo =
self.infoTextField(markdown: #""" self.infoTextField(markdown: #"""
When checked, the ignore files of VCSs, e.g. `gitignore`, will we used to ignore files.\ 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.\ 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.\ You can change this setting for each VimR window in the Open Quickly window.\
The behavior should be almost identical to that of The behavior should be almost identical to that of
[The Silver Searcher](https://github.com/ggreer/the_silver_searcher). [The Silver Searcher](https://github.com/ggreer/the_silver_searcher).
"""#) """#)
let cliToolTitle = self.titleTextField(title: "CLI Tool:") let cliToolTitle = self.titleTextField(title: "CLI Tool:")
let cliToolButton = NSButton(forAutoLayout: ()) 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.autoAlignAxis(.baseline, toSameAxisOf: whenLaunching, withOffset: 0)
openUntitledWindowTitle.autoPinEdge(.right, to: .right, of: afterLastWindowTitle) 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(.top, to: .bottom, of: paneTitle, withOffset: 18)
whenLaunching.autoPinEdge(.left, to: .right, of: openUntitledWindowTitle, withOffset: 5) 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(.top, to: .bottom, of: whenLaunching, withOffset: 5)
onReactivation.autoPinEdge(.left, to: .left, of: whenLaunching) 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.autoAlignAxis(.baseline, toSameAxisOf: lastWindow)
afterLastWindowTitle.autoPinEdge(toSuperviewEdge: .left, withInset: 18) 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.autoAlignAxis(.baseline, toSameAxisOf: vcsIg)
ignoreListTitle.autoPinEdge(.right, to: .right, of: openUntitledWindowTitle) 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(.top, to: .bottom, of: lastWindow, withOffset: 18)
vcsIg.autoPinEdge(.left, to: .right, of: ignoreListTitle, withOffset: 5) vcsIg.autoPinEdge(.left, to: .right, of: ignoreListTitle, withOffset: 5)
@ -192,9 +210,9 @@ The behavior should be almost identical to that of
} }
// MARK: - Actions // MARK: - Actions
extension GeneralPref {
@objc func copyCliTool(_ sender: NSButton) { extension GeneralPref {
@objc func copyCliTool(_: NSButton) {
let panel = NSOpenPanel() let panel = NSOpenPanel()
panel.canChooseFiles = false panel.canChooseFiles = false
panel.canChooseDirectories = true panel.canChooseDirectories = true
@ -205,14 +223,18 @@ extension GeneralPref {
} }
guard let vimrUrl = Bundle.main.url(forResource: "vimr", withExtension: nil) else { guard let vimrUrl = Bundle.main.url(forResource: "vimr", withExtension: nil) else {
self.alert(title: "Something Went Wrong.", self.alert(
info: "The CLI tool 'vimr' could not be found. Please re-download VimR and try again.") title: "Something Went Wrong.",
info: "The CLI tool 'vimr' could not be found. Please re-download VimR and try again."
)
return return
} }
guard let targetUrl = panel.url?.appendingPathComponent("vimr") else { guard let targetUrl = panel.url?.appendingPathComponent("vimr") else {
self.alert(title: "Something Went Wrong.", self.alert(
info: "The target directory could not be determined. Please try again with a different directory.") title: "Something Went Wrong.",
info: "The target directory could not be determined. Please try again with a different directory."
)
return return
} }
@ -228,18 +250,18 @@ extension GeneralPref {
self.emit(.setDefaultUsesVcsIgnores(sender.boolState)) self.emit(.setDefaultUsesVcsIgnores(sender.boolState))
} }
@objc func openUntitledWindowWhenLaunchingAction(_ sender: NSButton) { @objc func openUntitledWindowWhenLaunchingAction(_: NSButton) {
self.emit(.setOpenOnLaunch(self.openWhenLaunchingCheckbox.boolState)) self.emit(.setOpenOnLaunch(self.openWhenLaunchingCheckbox.boolState))
} }
@objc func openUntitledWindowOnReactivationAction(_ sender: NSButton) { @objc func openUntitledWindowOnReactivationAction(_: NSButton) {
self.emit(.setOpenOnReactivation(self.openOnReactivationCheckbox.boolState)) self.emit(.setOpenOnReactivation(self.openOnReactivationCheckbox.boolState))
} }
@objc func afterLastWindowAction(_ sender: NSPopUpButton) { @objc func afterLastWindowAction(_ sender: NSPopUpButton) {
let index = sender.indexOfSelectedItem let index = sender.indexOfSelectedItem
guard index >= 0 && index <= 2 else { guard index >= 0, index <= 2 else {
return return
} }
@ -256,4 +278,5 @@ extension GeneralPref {
} }
} }
private let indexToAfterLastWindowAction: [AppState.AfterLastWindowAction] = [.doNothing, .hide, .quit] private let indexToAfterLastWindowAction: [AppState.AfterLastWindowAction] = [.doNothing, .hide,
.quit]

View File

@ -6,7 +6,6 @@
import Foundation import Foundation
class GeneralPrefReducer: ReducerType { class GeneralPrefReducer: ReducerType {
typealias StateType = AppState typealias StateType = AppState
typealias ActionType = GeneralPref.Action typealias ActionType = GeneralPref.Action
@ -14,7 +13,6 @@ class GeneralPrefReducer: ReducerType {
var state = pair.state var state = pair.state
switch pair.action { switch pair.action {
case let .setOpenOnLaunch(value): case let .setOpenOnLaunch(value):
state.openNewMainWindowOnLaunch = value state.openNewMainWindowOnLaunch = value
@ -26,7 +24,6 @@ class GeneralPrefReducer: ReducerType {
case let .setDefaultUsesVcsIgnores(value): case let .setDefaultUsesVcsIgnores(value):
state.openQuickly.defaultUsesVcsIgnores = value state.openQuickly.defaultUsesVcsIgnores = value
} }
return (state, pair.action, true) return (state, pair.action, true)

View File

@ -3,11 +3,10 @@
* See LICENSE * See LICENSE
*/ */
import Foundation
import Commons import Commons
import Foundation
class HtmlPreviewMiddleware: MiddlewareType { class HtmlPreviewMiddleware: MiddlewareType {
static func selectFirstHtmlUrl(uuid: UUID) -> URL { static func selectFirstHtmlUrl(uuid: UUID) -> URL {
FileUtils.tempDir().appendingPathComponent("\(uuid)-select-first.html") FileUtils.tempDir().appendingPathComponent("\(uuid)-select-first.html")
} }

View File

@ -4,21 +4,19 @@
*/ */
import Cocoa import Cocoa
import RxSwift
import PureLayout
import WebKit
import EonilFSEvents import EonilFSEvents
import MaterialIcons import MaterialIcons
import os import os
import PureLayout
import RxSwift
import WebKit
import Workspace import Workspace
private let fileSystemEventsLatency = 1.0 private let fileSystemEventsLatency = 1.0
private let monitorDispatchQueue = DispatchQueue.global(qos: .userInitiated) private let monitorDispatchQueue = DispatchQueue.global(qos: .userInitiated)
class HtmlPreviewTool: NSView, UiComponent, WKNavigationDelegate { class HtmlPreviewTool: NSView, UiComponent, WKNavigationDelegate {
enum Action { enum Action {
case selectHtmlFile(URL) case selectHtmlFile(URL)
} }
@ -70,9 +68,10 @@ class HtmlPreviewTool: NSView, UiComponent, WKNavigationDelegate {
sinceWhen: EonilFSEventsEventID.getCurrentEventId(), sinceWhen: EonilFSEventsEventID.getCurrentEventId(),
latency: fileSystemEventsLatency, latency: fileSystemEventsLatency,
flags: [.fileEvents], flags: [.fileEvents],
handler: { [weak self] event in handler: { [weak self] _ in
self?.reloadWebview(with: serverUrl.payload) self?.reloadWebview(with: serverUrl.payload)
}) }
)
self.monitor?.setDispatchQueue(monitorDispatchQueue) self.monitor?.setDispatchQueue(monitorDispatchQueue)
try self.monitor?.start() try self.monitor?.start()
} catch { } catch {
@ -92,7 +91,7 @@ class HtmlPreviewTool: NSView, UiComponent, WKNavigationDelegate {
private func reloadWebview(with url: URL) { private func reloadWebview(with url: URL) {
DispatchQueue.main.async { 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 self.scrollTop = result as? Int ?? 0
} }
} }
@ -117,12 +116,15 @@ class HtmlPreviewTool: NSView, UiComponent, WKNavigationDelegate {
private var monitor: EonilFSEventStream? private var monitor: EonilFSEventStream?
private let disposeBag = DisposeBag() private let disposeBag = DisposeBag()
private let log = OSLog(subsystem: Defs.loggerSubsystem, private let log = OSLog(
category: Defs.LoggerCategory.ui) 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() let panel = NSOpenPanel()
panel.canChooseDirectories = false panel.canChooseDirectories = false
panel.allowsMultipleSelection = 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)") self.webview.evaluateJavaScript("document.body.scrollTop = \(self.scrollTop)")
} }
} }
extension HtmlPreviewTool { extension HtmlPreviewTool {
class InnerCustomToolbar: CustomToolBar { class InnerCustomToolbar: CustomToolBar {
fileprivate weak var htmlPreviewTool: HtmlPreviewTool? { fileprivate weak var htmlPreviewTool: HtmlPreviewTool? {
didSet { self.selectHtmlFile.target = self.htmlPreviewTool } didSet { self.selectHtmlFile.target = self.htmlPreviewTool }
} }
@ -182,6 +182,7 @@ extension HtmlPreviewTool {
selectHtmlFile.autoPinEdge(toSuperviewEdge: .right, withInset: InnerToolBar.itemPadding) 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") }
} }
} }

View File

@ -6,11 +6,10 @@
import Foundation import Foundation
class HtmlPreviewReducer { class HtmlPreviewReducer {
static let basePath = "tools/html-preview" static let basePath = "tools/html-preview"
static func serverUrl(baseUrl: URL, uuid: UUID) -> URL { static func serverUrl(baseUrl: URL, uuid: UUID) -> URL {
baseUrl.appendingPathComponent("\(uuid)/\(basePath)/index.html") baseUrl.appendingPathComponent("\(uuid)/\(self.basePath)/index.html")
} }
let mainWindow: MainWindowReducer let mainWindow: MainWindowReducer
@ -22,7 +21,6 @@ class HtmlPreviewReducer {
} }
class MainWindowReducer: ReducerType { class MainWindowReducer: ReducerType {
typealias StateType = MainWindow.State typealias StateType = MainWindow.State
typealias ActionType = UuidAction<MainWindow.Action> typealias ActionType = UuidAction<MainWindow.Action>
@ -32,7 +30,6 @@ class HtmlPreviewReducer {
var state = pair.state var state = pair.state
switch pair.action.payload { switch pair.action.payload {
case .setTheme: case .setTheme:
guard state.htmlPreview.htmlFile == nil else { return pair } guard state.htmlPreview.htmlFile == nil else { return pair }
state.htmlPreview.server = Marked( state.htmlPreview.server = Marked(
@ -41,7 +38,6 @@ class HtmlPreviewReducer {
default: default:
return pair return pair
} }
return (state, pair.action, true) return (state, pair.action, true)
@ -51,7 +47,6 @@ class HtmlPreviewReducer {
} }
class HtmlPreviewToolReducer: ReducerType { class HtmlPreviewToolReducer: ReducerType {
typealias StateType = MainWindow.State typealias StateType = MainWindow.State
typealias ActionType = UuidAction<HtmlPreviewTool.Action> typealias ActionType = UuidAction<HtmlPreviewTool.Action>
@ -60,13 +55,11 @@ class HtmlPreviewReducer {
func typedReduce(_ pair: ReduceTuple) -> ReduceTuple { func typedReduce(_ pair: ReduceTuple) -> ReduceTuple {
var state = pair.state var state = pair.state
switch pair.action.payload { switch pair.action.payload {
case let .selectHtmlFile(url):
case .selectHtmlFile(let url):
state.htmlPreview.htmlFile = url state.htmlPreview.htmlFile = url
state.htmlPreview.server = Marked( state.htmlPreview.server = Marked(
HtmlPreviewReducer.serverUrl(baseUrl: self.baseServerUrl, uuid: state.uuid) HtmlPreviewReducer.serverUrl(baseUrl: self.baseServerUrl, uuid: state.uuid)
) )
} }
return (state, pair.action, true) return (state, pair.action, true)

View File

@ -4,11 +4,10 @@
*/ */
import Foundation import Foundation
import Swifter
import os import os
import Swifter
class HttpServerMiddleware { class HttpServerMiddleware {
let htmlPreviewTool: HtmlPreviewToolMiddleware let htmlPreviewTool: HtmlPreviewToolMiddleware
let htmlPreviewMainWindow: HtmlPreviewMainWindowMiddleware let htmlPreviewMainWindow: HtmlPreviewMainWindowMiddleware
let markdownPreview: MarkdownPreviewMiddleware let markdownPreview: MarkdownPreviewMiddleware
@ -63,15 +62,18 @@ class HttpServerMiddleware {
} }
} }
private let log = OSLog(subsystem: Defs.loggerSubsystem, private let log = OSLog(
category: Defs.LoggerCategory.middleware) subsystem: Defs.loggerSubsystem,
category: Defs.LoggerCategory.middleware
)
class HtmlPreviewMainWindowMiddleware: MiddlewareType { class HtmlPreviewMainWindowMiddleware: MiddlewareType {
typealias StateType = MainWindow.State typealias StateType = MainWindow.State
typealias ActionType = UuidAction<MainWindow.Action> 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.server = server
self.baseUrl = baseUrl self.baseUrl = baseUrl
self.cssUrl = cssUrl self.cssUrl = cssUrl
@ -108,18 +110,21 @@ class HttpServerMiddleware {
private let baseUrl: URL private let baseUrl: URL
private let cssUrl: URL private let cssUrl: URL
private let log = OSLog(subsystem: Defs.loggerSubsystem, private let log = OSLog(
category: Defs.LoggerCategory.middleware) subsystem: Defs.loggerSubsystem,
category: Defs.LoggerCategory.middleware
)
private func fullUrl(with path: String) -> URL { self.baseUrl.appendingPathComponent(path) } private func fullUrl(with path: String) -> URL { self.baseUrl.appendingPathComponent(path) }
} }
class HtmlPreviewToolMiddleware: MiddlewareType { class HtmlPreviewToolMiddleware: MiddlewareType {
typealias StateType = MainWindow.State typealias StateType = MainWindow.State
typealias ActionType = UuidAction<HtmlPreviewTool.Action> 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.server = server
self.baseUrl = baseUrl self.baseUrl = baseUrl
self.cssUrl = cssUrl self.cssUrl = cssUrl
@ -152,14 +157,15 @@ class HttpServerMiddleware {
private let baseUrl: URL private let baseUrl: URL
private let cssUrl: URL private let cssUrl: URL
private let log = OSLog(subsystem: Defs.loggerSubsystem, private let log = OSLog(
category: Defs.LoggerCategory.middleware) subsystem: Defs.loggerSubsystem,
category: Defs.LoggerCategory.middleware
)
private func fullUrl(with path: String) -> URL { self.baseUrl.appendingPathComponent(path) } private func fullUrl(with path: String) -> URL { self.baseUrl.appendingPathComponent(path) }
} }
class MarkdownPreviewMiddleware: MiddlewareType { class MarkdownPreviewMiddleware: MiddlewareType {
typealias StateType = MainWindow.State typealias StateType = MainWindow.State
typealias ActionType = UuidAction<MainWindow.Action> typealias ActionType = UuidAction<MainWindow.Action>
@ -213,8 +219,10 @@ class HttpServerMiddleware {
private let cssUrl: URL private let cssUrl: URL
private let baseCssUrl: URL private let baseCssUrl: URL
private let log = OSLog(subsystem: Defs.loggerSubsystem, private let log = OSLog(
category: Defs.LoggerCategory.middleware) subsystem: Defs.loggerSubsystem,
category: Defs.LoggerCategory.middleware
)
private func fullUrl(with path: String) -> URL { self.baseUrl.appendingPathComponent(path) } private func fullUrl(with path: String) -> URL { self.baseUrl.appendingPathComponent(path) }
} }

View File

@ -7,11 +7,11 @@ import Cocoa
import PureLayout import PureLayout
class ImageAndTextTableCell: NSTableCellView { class ImageAndTextTableCell: NSTableCellView {
private let _textField = NSTextField(forAutoLayout: ()) private let _textField = NSTextField(forAutoLayout: ())
private let _imageView = NSImageView(forAutoLayout: ()) private let _imageView = NSImageView(forAutoLayout: ())
// MARK: - API // MARK: - API
static let font = NSFont.systemFont(ofSize: 12) static let font = NSFont.systemFont(ofSize: 12)
static let widthWithoutText = (2 + 16 + 4 + 2).cgf static let widthWithoutText = (2 + 16 + 4 + 2).cgf
@ -86,5 +86,6 @@ class ImageAndTextTableCell: NSTableCellView {
return self 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") }
} }

View File

@ -8,21 +8,19 @@ import PureLayout
import RxSwift import RxSwift
class KeysPref: PrefPane, UiComponent, NSTextFieldDelegate { class KeysPref: PrefPane, UiComponent, NSTextFieldDelegate {
typealias StateType = AppState typealias StateType = AppState
enum Action { enum Action {
case isLeftOptionMeta(Bool) case isLeftOptionMeta(Bool)
case isRightOptionMeta(Bool) case isRightOptionMeta(Bool)
} }
override var displayName: String { override var displayName: String {
return "Keys" "Keys"
} }
override var pinToContainer: Bool { override var pinToContainer: Bool {
return true true
} }
required init(source: Observable<StateType>, emitter: ActionEmitter, state: StateType) { required init(source: Observable<StateType>, emitter: ActionEmitter, state: StateType) {
@ -40,7 +38,7 @@ class KeysPref: PrefPane, UiComponent, NSTextFieldDelegate {
.observeOn(MainScheduler.instance) .observeOn(MainScheduler.instance)
.subscribe(onNext: { state in .subscribe(onNext: { state in
if self.isLeftOptionMeta != state.mainWindowTemplate.isLeftOptionMeta if self.isLeftOptionMeta != state.mainWindowTemplate.isLeftOptionMeta
|| self.isRightOptionMeta != state.mainWindowTemplate.isRightOptionMeta || self.isRightOptionMeta != state.mainWindowTemplate.isRightOptionMeta
{ {
self.isLeftOptionMeta = state.mainWindowTemplate.isLeftOptionMeta self.isLeftOptionMeta = state.mainWindowTemplate.isLeftOptionMeta
self.isRightOptionMeta = state.mainWindowTemplate.isRightOptionMeta self.isRightOptionMeta = state.mainWindowTemplate.isRightOptionMeta
@ -60,7 +58,8 @@ class KeysPref: PrefPane, UiComponent, NSTextFieldDelegate {
private let isLeftOptionMetaCheckbox = NSButton(forAutoLayout: ()) private let isLeftOptionMetaCheckbox = NSButton(forAutoLayout: ())
private let isRightOptionMetaCheckbox = NSButton(forAutoLayout: ()) private let isRightOptionMetaCheckbox = NSButton(forAutoLayout: ())
required init?(coder: NSCoder) { @available(*, unavailable)
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
@ -73,14 +72,18 @@ class KeysPref: PrefPane, UiComponent, NSTextFieldDelegate {
let paneTitle = self.paneTitleTextField(title: "Keys") let paneTitle = self.paneTitleTextField(title: "Keys")
let isLeftOptionMeta = self.isLeftOptionMetaCheckbox let isLeftOptionMeta = self.isLeftOptionMetaCheckbox
self.configureCheckbox(button: isLeftOptionMeta, self.configureCheckbox(
title: "Use Left Option as Meta", button: isLeftOptionMeta,
action: #selector(KeysPref.isLeftOptionMetaAction(_:))) title: "Use Left Option as Meta",
action: #selector(KeysPref.isLeftOptionMetaAction(_:))
)
let isRightOptionMeta = self.isRightOptionMetaCheckbox let isRightOptionMeta = self.isRightOptionMetaCheckbox
self.configureCheckbox(button: isRightOptionMeta, self.configureCheckbox(
title: "Use Right Option as Meta", button: isRightOptionMeta,
action: #selector(KeysPref.isRightOptionMetaAction(_:))) title: "Use Right Option as Meta",
action: #selector(KeysPref.isRightOptionMetaAction(_:))
)
let metaInfo = self.infoTextField(markdown: #""" let metaInfo = self.infoTextField(markdown: #"""
When an Option key is set to Meta, then every input containing the corresponding Option key will\ 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 // MARK: - Actions
extension KeysPref {
extension KeysPref {
@objc func isLeftOptionMetaAction(_ sender: NSButton) { @objc func isLeftOptionMetaAction(_ sender: NSButton) {
self.emit(.isLeftOptionMeta(sender.boolState)) self.emit(.isLeftOptionMeta(sender.boolState))
} }

View File

@ -6,7 +6,6 @@
import Foundation import Foundation
class KeysPrefReducer: ReducerType { class KeysPrefReducer: ReducerType {
typealias StateType = AppState typealias StateType = AppState
typealias ActionType = KeysPref.Action typealias ActionType = KeysPref.Action
@ -14,15 +13,13 @@ class KeysPrefReducer: ReducerType {
var state = pair.state var state = pair.state
switch pair.action { switch pair.action {
case let .isLeftOptionMeta(value): case let .isLeftOptionMeta(value):
state.mainWindowTemplate.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): case let .isRightOptionMeta(value):
state.mainWindowTemplate.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) return (state, pair.action, true)

View File

@ -4,20 +4,20 @@
*/ */
import Cocoa import Cocoa
import RxSwift
import MessagePack
import Commons import Commons
import MessagePack
import RxSwift
import Workspace import Workspace
// MARK: - RpcEvent Actions // MARK: - RpcEvent Actions
extension MainWindow {
extension MainWindow {
func rpcEventAction(params rawParams: [MessagePackValue]) { func rpcEventAction(params rawParams: [MessagePackValue]) {
guard rawParams.count > 0 else { return } guard rawParams.count > 0 else { return }
guard let strEvent = rawParams[0].stringValue, guard let strEvent = rawParams[0].stringValue,
let event = RpcEvent(rawValue: "\(RpcEvent.prefix).\(strEvent)") let event = RpcEvent(rawValue: "\(RpcEvent.prefix).\(strEvent)")
else { else {
return return
} }
let params = Array(rawParams.suffix(from: 1)) let params = Array(rawParams.suffix(from: 1))
@ -64,7 +64,7 @@ extension MainWindow {
guard let fontName = params[0].stringValue, guard let fontName = params[0].stringValue,
let fontSize = params[1].int64Value, let fontSize = params[1].int64Value,
let font = NSFont(name: fontName, size: fontSize.cgf) let font = NSFont(name: fontName, size: fontSize.cgf)
else { else {
return return
} }
@ -79,9 +79,8 @@ extension MainWindow {
case .setCharacterspacing: case .setCharacterspacing:
guard params.count == 1 else { return } guard params.count == 1 else { return }
guard let characterspacing = params[0].floatValue 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 // MARK: - File Menu Item Actions
extension MainWindow {
@IBAction func newTab(_ sender: Any?) { extension MainWindow {
@IBAction func newTab(_: Any?) {
self.neoVimView self.neoVimView
.newTab() .newTab()
.subscribe() .subscribe()
.disposed(by: self.disposeBag) .disposed(by: self.disposeBag)
} }
@IBAction func openDocument(_ sender: Any?) { @IBAction func openDocument(_: Any?) {
let panel = NSOpenPanel() let panel = NSOpenPanel()
panel.canChooseDirectories = true panel.canChooseDirectories = true
panel.allowsMultipleSelection = 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)) self.emit(self.uuidAction(for: .openQuickly))
} }
@IBAction func closeWindow(_ sender: Any?) { @IBAction func closeWindow(_: Any?) {
self.closeWindow = true self.closeWindow = true
self.window.performClose(nil) self.window.performClose(nil)
} }
@IBAction func saveDocument(_ sender: Any?) { @IBAction func saveDocument(_: Any?) {
self.neoVimView self.neoVimView
.currentBuffer() .currentBuffer()
.observeOn(MainScheduler.instance) .observeOn(MainScheduler.instance)
@ -185,7 +184,7 @@ extension MainWindow {
.disposed(by: self.disposeBag) .disposed(by: self.disposeBag)
} }
@IBAction func saveDocumentAs(_ sender: Any?) { @IBAction func saveDocumentAs(_: Any?) {
self.neoVimView self.neoVimView
.currentBuffer() .currentBuffer()
.observeOn(MainScheduler.instance) .observeOn(MainScheduler.instance)
@ -194,7 +193,8 @@ extension MainWindow {
self.neoVimView self.neoVimView
.saveCurrentTab(url: url) .saveCurrentTab(url: url)
.andThen( .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() .subscribe()
.disposed(by: self.disposeBag) .disposed(by: self.disposeBag)
@ -214,7 +214,9 @@ extension MainWindow {
let alert = NSAlert() let alert = NSAlert()
alert.addButton(withTitle: "OK") alert.addButton(withTitle: "OK")
alert.messageText = "Invalid File Name" 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.alertStyle = .warning
alert.runModal() alert.runModal()
@ -231,36 +233,36 @@ extension MainWindow {
} }
// MARK: - Tools Menu Item Actions // MARK: - Tools Menu Item Actions
extension MainWindow {
@IBAction func toggleAllTools(_ sender: Any?) { extension MainWindow {
@IBAction func toggleAllTools(_: Any?) {
self.workspace.toggleAllTools() self.workspace.toggleAllTools()
self.focusNvimView(self) self.focusNvimView(self)
self.emit(self.uuidAction(for: .toggleAllTools(self.workspace.isAllToolsVisible))) self.emit(self.uuidAction(for: .toggleAllTools(self.workspace.isAllToolsVisible)))
} }
@IBAction func toggleToolButtons(_ sender: Any?) { @IBAction func toggleToolButtons(_: Any?) {
self.workspace.toggleToolButtons() self.workspace.toggleToolButtons()
self.emit(self.uuidAction(for: .toggleToolButtons(self.workspace.isToolButtonsVisible))) 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 } guard let fileBrowser = self.fileBrowserContainer else { return }
self.toggle(tool: fileBrowser, toolType: .fileBrowser) self.toggle(tool: fileBrowser, toolType: .fileBrowser)
} }
@IBAction func toggleBufferList(_ sender: Any?) { @IBAction func toggleBufferList(_: Any?) {
guard let bufferList = self.buffersListContainer else { return } guard let bufferList = self.buffersListContainer else { return }
self.toggle(tool: bufferList, toolType: .bufferList) self.toggle(tool: bufferList, toolType: .bufferList)
} }
@IBAction func toggleMarkdownPreview(_ sender: Any?) { @IBAction func toggleMarkdownPreview(_: Any?) {
guard let markdownPreview = self.previewContainer else { return } guard let markdownPreview = self.previewContainer else { return }
self.toggle(tool: markdownPreview, toolType: .markdownPreview) self.toggle(tool: markdownPreview, toolType: .markdownPreview)
} }
@IBAction func toggleHtmlPreview(_ sender: Any?) { @IBAction func toggleHtmlPreview(_: Any?) {
guard let htmlPreview = self.htmlPreviewContainer else { return } guard let htmlPreview = self.htmlPreviewContainer else { return }
self.toggle(tool: htmlPreview, toolType: .htmlPreview) self.toggle(tool: htmlPreview, toolType: .htmlPreview)
} }
@ -287,8 +289,8 @@ extension MainWindow {
} }
// MARK: - NSUserInterfaceValidationsProtocol // MARK: - NSUserInterfaceValidationsProtocol
extension MainWindow {
extension MainWindow {
func validateUserInterfaceItem(_ item: NSValidatedUserInterfaceItem) -> Bool { func validateUserInterfaceItem(_ item: NSValidatedUserInterfaceItem) -> Bool {
let canSave = self.neoVimView.currentBuffer().syncValue()?.type == "" let canSave = self.neoVimView.currentBuffer().syncValue()?.type == ""
let canSaveAs = canSave let canSaveAs = canSave
@ -303,31 +305,29 @@ extension MainWindow {
} }
switch action { switch action {
case #selector(self.toggleAllTools(_:)), #selector(self.toggleToolButtons(_:)):
case #selector(toggleAllTools(_:)), #selector(toggleToolButtons(_:)):
return canToggleTools return canToggleTools
case #selector(toggleFileBrowser(_:)): case #selector(self.toggleFileBrowser(_:)):
return canToggleFileBrowser return canToggleFileBrowser
case #selector(focusNvimView(_:)): case #selector(self.focusNvimView(_:)):
return canFocusNvimView return canFocusNvimView
case #selector(openDocument(_:)): case #selector(self.openDocument(_:)):
return canOpen return canOpen
case #selector(openQuickly(_:)): case #selector(self.openQuickly(_:)):
return canOpenQuickly return canOpenQuickly
case #selector(saveDocument(_:)): case #selector(self.saveDocument(_:)):
return canSave return canSave
case #selector(saveDocumentAs(_:)): case #selector(self.saveDocumentAs(_:)):
return canSaveAs return canSaveAs
default: default:
return true return true
} }
} }
} }

View File

@ -7,9 +7,9 @@ import Cocoa
import PureLayout import PureLayout
// MARK: - Custom title // MARK: - Custom title
extension MainWindow {
func themeTitlebar(grow: Bool) { extension MainWindow {
func themeTitlebar(grow _: Bool) {
if self.window.styleMask.contains(.fullScreen) { if self.window.styleMask.contains(.fullScreen) {
return return
} }
@ -100,7 +100,8 @@ extension MainWindow {
self.titleView = title self.titleView = title
if let button = self.window.standardWindowButton(.documentIconButton) { 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) contentView.addSubview(button)
button.autoSetDimension(.width, toSize: 16) button.autoSetDimension(.width, toSize: 16)
button.autoSetDimension(.height, toSize: 16) button.autoSetDimension(.height, toSize: 16)
@ -110,11 +111,15 @@ extension MainWindow {
// rightView.left = leftView.right + gap // rightView.left = leftView.right + gap
// rightView.right = parentView.centerX + (leftView.width + gap + rightView.width) / 2 - 4 // rightView.right = parentView.centerX + (leftView.width + gap + rightView.width) / 2 - 4
// The (-4) at the end is an empirical value... // The (-4) at the end is an empirical value...
contentView.addConstraint(NSLayoutConstraint(item: title, attribute: .left, contentView.addConstraint(NSLayoutConstraint(
relatedBy: .equal, item: title,
toItem: button, attribute: .right, attribute: .left,
multiplier: 1, relatedBy: .equal,
constant: repIconToTitleGap)) toItem: button,
attribute: .right,
multiplier: 1,
constant: repIconToTitleGap
))
contentView.addConstraint( contentView.addConstraint(
// Here we use title.intrinsicContentSize instead of title.frame because title.frame is still zero. // Here we use title.intrinsicContentSize instead of title.frame because title.frame is still zero.
NSLayoutConstraint( NSLayoutConstraint(
@ -122,7 +127,8 @@ extension MainWindow {
relatedBy: .equal, relatedBy: .equal,
toItem: contentView, attribute: .centerX, toItem: contentView, attribute: .centerX,
multiplier: 1, 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

View File

@ -4,14 +4,14 @@
*/ */
import Cocoa import Cocoa
import RxSwift
import NvimView import NvimView
import RxPack import RxPack
import RxSwift
import Workspace import Workspace
// MARK: - NvimViewDelegate // MARK: - NvimViewDelegate
extension MainWindow {
extension MainWindow {
// Use only when Cmd-Q'ing // Use only when Cmd-Q'ing
func waitTillNvimExits() { func waitTillNvimExits() {
self.neoVimView.waitTillNvimExits() self.neoVimView.waitTillNvimExits()
@ -35,7 +35,9 @@ extension MainWindow {
self.window.toggleFullScreen(nil) 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 return
} }
@ -92,8 +94,10 @@ extension MainWindow {
self self
.updateCssColors() .updateCssColors()
.subscribe(onSuccess: { colors in .subscribe(onSuccess: { colors in
self.emit(self.uuidAction( self.emit(
for: .setTheme(Theme(from: nvimTheme, additionalColorDict: colors))) self.uuidAction(
for: .setTheme(Theme(from: nvimTheme, additionalColorDict: colors))
)
) )
}) })
.disposed(by: self.disposeBag) .disposed(by: self.disposeBag)
@ -103,10 +107,12 @@ extension MainWindow {
let alert = NSAlert() let alert = NSAlert()
alert.addButton(withTitle: "Close") alert.addButton(withTitle: "Close")
alert.messageText = "Sorry, an error occurred." alert.messageText = "Sorry, an error occurred."
alert.informativeText = "VimR encountered an error from which it cannot recover. This window will now close.\n" alert
+ reason .informativeText =
"VimR encountered an error from which it cannot recover. This window will now close.\n"
+ reason
alert.alertStyle = .critical alert.alertStyle = .critical
alert.beginSheetModal(for: self.window) { response in alert.beginSheetModal(for: self.window) { _ in
self.windowController.close() self.windowController.close()
} }
} }
@ -132,7 +138,7 @@ extension MainWindow {
"CursorColumn", // code background and foreground "CursorColumn", // code background and foreground
] ]
typealias HlResult = Dictionary<String, RxNeovimApi.Value> typealias HlResult = [String: RxNeovimApi.Value]
typealias ColorNameHlResultTuple = (colorName: String, hlResult: HlResult) typealias ColorNameHlResultTuple = (colorName: String, hlResult: HlResult)
typealias ColorNameObservableTuple = (colorName: String, observable: Observable<HlResult>) typealias ColorNameObservableTuple = (colorName: String, observable: Observable<HlResult>)
@ -164,7 +170,6 @@ extension MainWindow {
// MARK: - NSWindowDelegate // MARK: - NSWindowDelegate
extension MainWindow { extension MainWindow {
func windowWillEnterFullScreen(_: Notification) { func windowWillEnterFullScreen(_: Notification) {
self.unthemeTitlebar(dueFullScreen: true) self.unthemeTitlebar(dueFullScreen: true)
} }
@ -175,20 +180,24 @@ extension MainWindow {
} }
} }
func windowDidBecomeMain(_ notification: Notification) { func windowDidBecomeMain(_: Notification) {
self.emit(self.uuidAction(for: .becomeKey(isFullScreen: self.window.styleMask.contains(.fullScreen)))) self
.emit(
self
.uuidAction(for: .becomeKey(isFullScreen: self.window.styleMask.contains(.fullScreen)))
)
self.neoVimView.didBecomeMain().subscribe().disposed(by: self.disposeBag) self.neoVimView.didBecomeMain().subscribe().disposed(by: self.disposeBag)
} }
func windowDidResignMain(_ notification: Notification) { func windowDidResignMain(_: Notification) {
self.neoVimView.didResignMain().subscribe().disposed(by: self.disposeBag) 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))) self.emit(self.uuidAction(for: .frameChanged(to: self.window.frame)))
} }
func windowDidResize(_ notification: Notification) { func windowDidResize(_: Notification) {
if self.window.styleMask.contains(.fullScreen) { if self.window.styleMask.contains(.fullScreen) {
return return
} }
@ -199,7 +208,7 @@ extension MainWindow {
func windowShouldClose(_: NSWindow) -> Bool { func windowShouldClose(_: NSWindow) -> Bool {
defer { self.closeWindow = false } defer { self.closeWindow = false }
if (self.neoVimView.isBlocked().syncValue() ?? false) { if self.neoVimView.isBlocked().syncValue() ?? false {
let alert = NSAlert() let alert = NSAlert()
alert.messageText = "Nvim is waiting for your input." alert.messageText = "Nvim is waiting for your input."
alert.alertStyle = .informational alert.alertStyle = .informational
@ -221,7 +230,7 @@ extension MainWindow {
return false return false
} }
guard (self.neoVimView.isCurrentBufferDirty().syncValue() ?? false) else { guard self.neoVimView.isCurrentBufferDirty().syncValue() ?? false else {
try? self.neoVimView.closeCurrentTab().wait() try? self.neoVimView.closeCurrentTab().wait()
return false return false
} }
@ -249,13 +258,13 @@ extension MainWindow {
} }
// MARK: - WorkspaceDelegate // MARK: - WorkspaceDelegate
extension MainWindow {
func resizeWillStart(workspace: Workspace, tool: WorkspaceTool?) { extension MainWindow {
func resizeWillStart(workspace _: Workspace, tool _: WorkspaceTool?) {
self.neoVimView.enterResizeMode() self.neoVimView.enterResizeMode()
} }
func resizeDidEnd(workspace: Workspace, tool: WorkspaceTool?) { func resizeDidEnd(workspace _: Workspace, tool: WorkspaceTool?) {
self.neoVimView.exitResizeMode() self.neoVimView.exitResizeMode()
if let workspaceTool = tool, let toolIdentifier = self.toolIdentifier(for: workspaceTool) { if let workspaceTool = tool, let toolIdentifier = self.toolIdentifier(for: workspaceTool) {
@ -270,13 +279,14 @@ extension MainWindow {
} }
func moved(tool: WorkspaceTool) { func moved(tool: WorkspaceTool) {
let tools = self.workspace.orderedTools.compactMap { (tool: WorkspaceTool) -> (Tools, WorkspaceTool)? in let tools = self.workspace.orderedTools
guard let toolId = self.toolIdentifier(for: tool) else { .compactMap { (tool: WorkspaceTool) -> (Tools, WorkspaceTool)? in
return nil guard let toolId = self.toolIdentifier(for: tool) else {
} return nil
}
return (toolId, tool) return (toolId, tool)
} }
self.emit(self.uuidAction(for: .setToolsState(tools))) self.emit(self.uuidAction(for: .setToolsState(tools)))
} }

View File

@ -8,9 +8,7 @@ import NvimView
import Workspace import Workspace
extension MainWindow { extension MainWindow {
enum Action { enum Action {
case cd(to: URL) case cd(to: URL)
case setBufferList([NvimView.Buffer]) case setBufferList([NvimView.Buffer])
@ -46,7 +44,6 @@ extension MainWindow {
} }
enum FocusableView { enum FocusableView {
case neoVimView case neoVimView
case fileBrowser case fileBrowser
case bufferList case bufferList
@ -55,13 +52,12 @@ extension MainWindow {
} }
enum Tools: String, Codable { enum Tools: String, Codable {
static let all = Set( static let all = Set(
[ [
Tools.fileBrowser, Tools.fileBrowser,
Tools.buffersList, Tools.buffersList,
Tools.preview, Tools.preview,
Tools.htmlPreview Tools.htmlPreview,
] ]
) )
@ -72,7 +68,6 @@ extension MainWindow {
} }
enum OpenMode { enum OpenMode {
case `default` case `default`
case currentTab case currentTab
case newTab case newTab

View File

@ -4,18 +4,18 @@
*/ */
import Cocoa import Cocoa
import RxSwift
import NvimView import NvimView
import PureLayout
import os import os
import PureLayout
import RxSwift
import Workspace import Workspace
class MainWindow: NSObject, class MainWindow: NSObject,
UiComponent, UiComponent,
NSWindowDelegate, NSWindowDelegate,
NSUserInterfaceValidations, NSUserInterfaceValidations,
WorkspaceDelegate { WorkspaceDelegate
{
typealias StateType = State typealias StateType = State
let disposeBag = DisposeBag() let disposeBag = DisposeBag()
@ -67,7 +67,8 @@ class MainWindow: NSObject,
var sourceFileUrls = [URL]() var sourceFileUrls = [URL]()
if let sourceFileUrl = Bundle(for: MainWindow.self) if let sourceFileUrl = Bundle(for: MainWindow.self)
.url(forResource: "com.qvacua.VimR", withExtension: "vim") { .url(forResource: "com.qvacua.VimR", withExtension: "vim")
{
sourceFileUrls.append(sourceFileUrl) sourceFileUrls.append(sourceFileUrl)
} }
@ -97,9 +98,11 @@ class MainWindow: NSObject,
} }
if state.activeTools[.htmlPreview] == true { if state.activeTools[.htmlPreview] == true {
self.htmlPreview = HtmlPreviewTool(source: source, self.htmlPreview = HtmlPreviewTool(
emitter: emitter, source: source,
state: state) emitter: emitter,
state: state
)
let htmlPreviewConfig = WorkspaceTool.Config( let htmlPreviewConfig = WorkspaceTool.Config(
title: "HTML", title: "HTML",
view: self.htmlPreview!, view: self.htmlPreview!,
@ -107,7 +110,7 @@ class MainWindow: NSObject,
) )
self.htmlPreviewContainer = WorkspaceTool(htmlPreviewConfig) self.htmlPreviewContainer = WorkspaceTool(htmlPreviewConfig)
self.htmlPreviewContainer!.dimension = state.tools[.htmlPreview]? self.htmlPreviewContainer!.dimension = state.tools[.htmlPreview]?
.dimension ?? 250 .dimension ?? 250
tools[.htmlPreview] = self.htmlPreviewContainer tools[.htmlPreview] = self.htmlPreviewContainer
} }
@ -123,8 +126,8 @@ class MainWindow: NSObject,
) )
self.fileBrowserContainer = WorkspaceTool(fileBrowserConfig) self.fileBrowserContainer = WorkspaceTool(fileBrowserConfig)
self.fileBrowserContainer!.dimension = state self.fileBrowserContainer!.dimension = state
.tools[.fileBrowser]? .tools[.fileBrowser]?
.dimension ?? 200 .dimension ?? 200
tools[.fileBrowser] = self.fileBrowserContainer tools[.fileBrowser] = self.fileBrowserContainer
} }
@ -132,12 +135,14 @@ class MainWindow: NSObject,
self.buffersList = BuffersList( self.buffersList = BuffersList(
source: source, emitter: emitter, state: state source: source, emitter: emitter, state: state
) )
let buffersListConfig = WorkspaceTool.Config(title: "Buffers", let buffersListConfig = WorkspaceTool.Config(
view: self.buffersList!) title: "Buffers",
view: self.buffersList!
)
self.buffersListContainer = WorkspaceTool(buffersListConfig) self.buffersListContainer = WorkspaceTool(buffersListConfig)
self.buffersListContainer!.dimension = state self.buffersListContainer!.dimension = state
.tools[.buffersList]? .tools[.buffersList]?
.dimension ?? 200 .dimension ?? 200
tools[.buffersList] = self.buffersListContainer tools[.buffersList] = self.buffersListContainer
} }
@ -163,11 +168,13 @@ class MainWindow: NSObject,
return return
} }
self.workspace.append(tool: tool, self.workspace.append(
location: state.tools[toolId]?.location ?? .left) 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 { if state.tools[toolId]?.open == true {
toolContainer.toggle() toolContainer.toggle()
} }
@ -204,7 +211,7 @@ class MainWindow: NSObject,
} }
func uuidAction(for action: Action) -> UuidAction<Action> { func uuidAction(for action: Action) -> UuidAction<Action> {
return UuidAction(uuid: self.uuid, action: action) UuidAction(uuid: self.uuid, action: action)
} }
func show() { func show() {
@ -213,7 +220,7 @@ class MainWindow: NSObject,
// The following should only be used when Cmd-Q'ing // The following should only be used when Cmd-Q'ing
func quitNeoVimWithoutSaving() -> Completable { func quitNeoVimWithoutSaving() -> Completable {
return self.neoVimView.quitNeoVimWithoutSaving() self.neoVimView.quitNeoVimWithoutSaving()
} }
@IBAction func debug2(_: Any?) { @IBAction func debug2(_: Any?) {
@ -222,10 +229,11 @@ class MainWindow: NSObject,
theme.background = .yellow theme.background = .yellow
theme.highlightForeground = .orange theme.highlightForeground = .orange
theme.highlightBackground = .red theme.highlightBackground = .red
self.emit(uuidAction(for: .setTheme(theme))) self.emit(self.uuidAction(for: .setTheme(theme)))
} }
// MARK: - Private // MARK: - Private
private var currentBuffer: NvimView.Buffer? private var currentBuffer: NvimView.Buffer?
private var goToLineFromCli: Marked<Int>? private var goToLineFromCli: Marked<Int>?
@ -246,8 +254,10 @@ class MainWindow: NSObject,
private var usesTheme = true private var usesTheme = true
private var lastThemeMark = Token() private var lastThemeMark = Token()
private let log = OSLog(subsystem: Defs.loggerSubsystem, private let log = OSLog(
category: Defs.LoggerCategory.ui) subsystem: Defs.loggerSubsystem,
category: Defs.LoggerCategory.ui
)
private func setupScrollAndCursorDebouncers() { private func setupScrollAndCursorDebouncers() {
Observable Observable
@ -265,12 +275,11 @@ class MainWindow: NSObject,
.observeOn(MainScheduler.instance) .observeOn(MainScheduler.instance)
.subscribe(onNext: { [weak self] event in .subscribe(onNext: { [weak self] event in
switch event { switch event {
case .neoVimStopped: self?.neoVimStopped() 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() case .cwdChanged: self?.cwdChanged()
@ -278,32 +287,30 @@ class MainWindow: NSObject,
case .tabChanged: self?.tabChanged() 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 let .ipcBecameInvalid(reason):
case .ipcBecameInvalid(let reason):
self?.ipcBecameInvalid(reason: reason) self?.ipcBecameInvalid(reason: reason)
case .scroll: self?.scroll() 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 .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)") 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 case .rpcEventSubscribed: break
} }
}, onError: { error in }, onError: { error in
// FIXME call onError // FIXME: call onError
self.log.error(error) self.log.error(error)
}) })
.disposed(by: self.disposeBag) .disposed(by: self.disposeBag)
@ -318,7 +325,8 @@ class MainWindow: NSObject,
} }
if state.viewToBeFocused != nil, if state.viewToBeFocused != nil,
case .neoVimView = state.viewToBeFocused! { case .neoVimView = state.viewToBeFocused!
{
self.window.makeFirstResponder(self.neoVimView) self.window.makeFirstResponder(self.neoVimView)
} }
@ -331,11 +339,11 @@ class MainWindow: NSObject,
Completable Completable
.empty() .empty()
.andThen { .andThen {
if state.preview.status == .markdown if state.preview.status == .markdown,
&& state.previewTool.isReverseSearchAutomatically state.previewTool.isReverseSearchAutomatically,
&& state.preview.previewPosition state.preview.previewPosition
.hasDifferentMark(as: self.previewPosition) { .hasDifferentMark(as: self.previewPosition)
{
self.previewPosition = state.preview.previewPosition self.previewPosition = state.preview.previewPosition
return self.neoVimView.cursorGo( return self.neoVimView.cursorGo(
to: state.preview.previewPosition.payload to: state.preview.previewPosition.payload
@ -389,7 +397,8 @@ class MainWindow: NSObject,
self.unthemeTitlebar(dueFullScreen: false) self.unthemeTitlebar(dueFullScreen: false)
self.window.backgroundColor = .windowBackgroundColor self.window.backgroundColor = .windowBackgroundColor
self.workspace.theme = .default self.workspace.theme = .default
}) }
)
self.usesTheme = state.appearance.usesTheme self.usesTheme = state.appearance.usesTheme
self.currentBuffer = state.currentBuffer self.currentBuffer = state.currentBuffer
@ -404,7 +413,8 @@ class MainWindow: NSObject,
self.neoVimView.isRightOptionMeta = state.isRightOptionMeta self.neoVimView.isRightOptionMeta = state.isRightOptionMeta
if self.neoVimView.trackpadScrollResistance if self.neoVimView.trackpadScrollResistance
!= state.trackpadScrollResistance.cgf { != state.trackpadScrollResistance.cgf
{
self.neoVimView.trackpadScrollResistance = CGFloat( self.neoVimView.trackpadScrollResistance = CGFloat(
state.trackpadScrollResistance state.trackpadScrollResistance
) )
@ -420,9 +430,10 @@ class MainWindow: NSObject,
} }
if self.defaultFont != state.appearance.font if self.defaultFont != state.appearance.font
|| self.linespacing != state.appearance.linespacing || self.linespacing != state.appearance.linespacing
|| self.characterspacing != state.appearance.characterspacing || self.characterspacing != state.appearance.characterspacing
|| self.usesLigatures != state.appearance.usesLigatures { || self.usesLigatures != state.appearance.usesLigatures
{
self.defaultFont = state.appearance.font self.defaultFont = state.appearance.font
self.linespacing = state.appearance.linespacing self.linespacing = state.appearance.linespacing
self.characterspacing = state.appearance.characterspacing self.characterspacing = state.appearance.characterspacing
@ -506,10 +517,10 @@ class MainWindow: NSObject,
notification.identifier = UUID().uuidString notification.identifier = UUID().uuidString
notification.title = "Error during initialization" notification.title = "Error during initialization"
notification.informativeText = notification.informativeText =
""" """
There was an error during the initialization of NeoVim. There was an error during the initialization of NeoVim.
Use :messages to view the error messages. Use :messages to view the error messages.
""" """
NSUserNotificationCenter.default.deliver(notification) NSUserNotificationCenter.default.deliver(notification)
} }

View File

@ -6,7 +6,6 @@
import Foundation import Foundation
class MainWindowReducer: ReducerType { class MainWindowReducer: ReducerType {
typealias StateType = MainWindow.State typealias StateType = MainWindow.State
typealias ActionType = UuidAction<MainWindow.Action> typealias ActionType = UuidAction<MainWindow.Action>
@ -14,11 +13,10 @@ class MainWindowReducer: ReducerType {
var state = tuple.state var state = tuple.state
switch tuple.action.payload { switch tuple.action.payload {
case let .frameChanged(to: frame):
case let .frameChanged(to:frame):
state.frame = frame state.frame = frame
case let .cd(to:cwd): case let .cd(to: cwd):
if state.cwd != cwd { if state.cwd != cwd {
state.cwd = cwd state.cwd = cwd
} }
@ -41,9 +39,11 @@ class MainWindowReducer: ReducerType {
state.viewToBeFocused = view state.viewToBeFocused = view
case let .setState(for: tool, with: workspaceTool): case let .setState(for: tool, with: workspaceTool):
state.tools[tool] = WorkspaceToolState(location: workspaceTool.location, state.tools[tool] = WorkspaceToolState(
dimension: workspaceTool.dimension, location: workspaceTool.location,
open: workspaceTool.isSelected) dimension: workspaceTool.dimension,
open: workspaceTool.isSelected
)
if workspaceTool.isSelected { if workspaceTool.isSelected {
state.tools state.tools
.filter { $0 != tool && $1.location == workspaceTool.location } .filter { $0 != tool && $1.location == workspaceTool.location }
@ -56,9 +56,11 @@ class MainWindowReducer: ReducerType {
let toolId = toolPair.0 let toolId = toolPair.0
let tool = toolPair.1 let tool = toolPair.1
state.tools[toolId] = WorkspaceToolState(location: tool.location, state.tools[toolId] = WorkspaceToolState(
dimension: tool.dimension, location: tool.location,
open: tool.isSelected) dimension: tool.dimension,
open: tool.isSelected
)
if tool.isSelected { if tool.isSelected {
state.tools state.tools
@ -83,7 +85,6 @@ class MainWindowReducer: ReducerType {
default: default:
return tuple return tuple
} }
return (state, tuple.action, true) return (state, tuple.action, true)

View File

@ -3,12 +3,11 @@
* See LICENSE * See LICENSE
*/ */
import Foundation
import Down import Down
import Foundation
import os import os
class MarkdownPreviewMiddleware { class MarkdownPreviewMiddleware {
let markdownTool: MarkdownToolMiddleware let markdownTool: MarkdownToolMiddleware
let mainWindow: MainWindowMiddleware let mainWindow: MainWindowMiddleware
@ -19,7 +18,6 @@ class MarkdownPreviewMiddleware {
} }
class PreviewGenerator { class PreviewGenerator {
init() { init() {
// We know that the files are there! // We know that the files are there!
self.template = try! String(contentsOf: Resources.markdownTemplateUrl) self.template = try! String(contentsOf: Resources.markdownTemplateUrl)
@ -39,10 +37,9 @@ class MarkdownPreviewMiddleware {
} }
self.removePreviewHtmlFile(uuid: uuid) self.removePreviewHtmlFile(uuid: uuid)
guard let htmlUrl = preview.html else {return } guard let htmlUrl = preview.html else { return }
switch preview.status { switch preview.status {
case .none: case .none:
self.writePage(html: self.emptyHtml, uuid: uuid, url: htmlUrl) 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)") self.log.error("error while rendering \(buffer) to \(htmlUrl): \(error)")
return return
} }
} }
} }
@ -101,15 +97,17 @@ class MarkdownPreviewMiddleware {
private let template: String private let template: String
private var previewFiles = [UUID: URL]() private var previewFiles = [UUID: URL]()
private let log = OSLog(subsystem: Defs.loggerSubsystem, private let log = OSLog(
category: Defs.LoggerCategory.middleware) subsystem: Defs.loggerSubsystem,
category: Defs.LoggerCategory.middleware
)
private func render(_ bufferUrl: URL, to htmlUrl: URL) throws { private func render(_ bufferUrl: URL, to htmlUrl: URL) throws {
let md = try String(contentsOf: bufferUrl) let md = try String(contentsOf: bufferUrl)
let down = Down(markdownString: md) let down = Down(markdownString: md)
let body = try down.toHTML(DownOptions.sourcePos) 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 let htmlFilePath = htmlUrl.path
try html.write(toFile: htmlFilePath, atomically: true, encoding: .utf8) try html.write(toFile: htmlFilePath, atomically: true, encoding: .utf8)
@ -128,7 +126,6 @@ class MarkdownPreviewMiddleware {
} }
class MarkdownToolMiddleware: MiddlewareType { class MarkdownToolMiddleware: MiddlewareType {
typealias StateType = MainWindow.State typealias StateType = MainWindow.State
typealias ActionType = UuidAction<MarkdownTool.Action> typealias ActionType = UuidAction<MarkdownTool.Action>
@ -150,7 +147,6 @@ class MarkdownPreviewMiddleware {
} }
class MainWindowMiddleware: MiddlewareType { class MainWindowMiddleware: MiddlewareType {
typealias StateType = MainWindow.State typealias StateType = MainWindow.State
typealias ActionType = UuidAction<MainWindow.Action> typealias ActionType = UuidAction<MainWindow.Action>
@ -162,14 +158,12 @@ class MarkdownPreviewMiddleware {
let uuidAction = tuple.action let uuidAction = tuple.action
switch uuidAction.payload { switch uuidAction.payload {
case .newCurrentBuffer: fallthrough case .newCurrentBuffer: fallthrough
case .bufferWritten: fallthrough case .bufferWritten: fallthrough
case .setTheme: case .setTheme:
self.generator.apply(result.state, uuid: uuidAction.uuid) self.generator.apply(result.state, uuid: uuidAction.uuid)
default: return result default: return result
} }
return result return result

View File

@ -4,12 +4,11 @@
*/ */
import Cocoa import Cocoa
import NvimView
import Commons import Commons
import NvimView
class MarkdownPreviewReducer { class MarkdownPreviewReducer {
private static func previewState(
static private func previewState(
for uuid: UUID, for uuid: UUID,
baseUrl: URL, baseUrl: URL,
buffer: NvimView.Buffer?, buffer: NvimView.Buffer?,
@ -58,7 +57,6 @@ class MarkdownPreviewReducer {
} }
class PreviewToolReducer: ReducerType { class PreviewToolReducer: ReducerType {
typealias StateType = MainWindow.State typealias StateType = MainWindow.State
typealias ActionType = UuidAction<MarkdownTool.Action> typealias ActionType = UuidAction<MarkdownTool.Action>
@ -66,7 +64,6 @@ class MarkdownPreviewReducer {
var state = tuple.state var state = tuple.state
switch tuple.action.payload { switch tuple.action.payload {
case .refreshNow: case .refreshNow:
state.preview = MarkdownPreviewReducer.previewState( state.preview = MarkdownPreviewReducer.previewState(
for: tuple.state.uuid, for: tuple.state.uuid,
@ -77,20 +74,25 @@ class MarkdownPreviewReducer {
) )
state.preview.lastSearch = .reload state.preview.lastSearch = .reload
case let .reverseSearch(to:position): case let .reverseSearch(to: position):
state.preview.previewPosition = Marked(position) state.preview.previewPosition = Marked(position)
state.preview.lastSearch = .reverse state.preview.lastSearch = .reverse
case let .scroll(to:position): case let .scroll(to: position):
if state.preview.lastSearch == .reload { if state.preview.lastSearch == .reload {
state.preview.lastSearch = .none 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.lastSearch = .none
state.preview.previewPosition = Marked(mark: state.preview.previewPosition.mark, payload: position) state.preview.previewPosition = Marked(
break; mark: state.preview.previewPosition.mark,
payload: position
)
break
} }
state.preview.previewPosition = Marked(position) state.preview.previewPosition = Marked(position)
@ -98,7 +100,6 @@ class MarkdownPreviewReducer {
default: default:
return tuple return tuple
} }
return (state, tuple.action, true) return (state, tuple.action, true)
@ -110,7 +111,6 @@ class MarkdownPreviewReducer {
} }
class BuffersListReducer: ReducerType { class BuffersListReducer: ReducerType {
typealias StateType = MainWindow.State typealias StateType = MainWindow.State
typealias ActionType = UuidAction<BuffersList.Action> typealias ActionType = UuidAction<BuffersList.Action>
@ -118,7 +118,6 @@ class MarkdownPreviewReducer {
var state = tuple.state var state = tuple.state
switch tuple.action.payload { switch tuple.action.payload {
case let .open(buffer): case let .open(buffer):
state.preview = MarkdownPreviewReducer.previewState( state.preview = MarkdownPreviewReducer.previewState(
for: tuple.state.uuid, for: tuple.state.uuid,
@ -128,7 +127,6 @@ class MarkdownPreviewReducer {
previewPosition: Marked(.beginning) previewPosition: Marked(.beginning)
) )
state.preview.lastSearch = .none state.preview.lastSearch = .none
} }
return (state, tuple.action, true) return (state, tuple.action, true)
@ -140,7 +138,6 @@ class MarkdownPreviewReducer {
} }
class MainWindowReducer: ReducerType { class MainWindowReducer: ReducerType {
typealias StateType = MainWindow.State typealias StateType = MainWindow.State
typealias ActionType = UuidAction<MainWindow.Action> typealias ActionType = UuidAction<MainWindow.Action>
@ -148,7 +145,6 @@ class MarkdownPreviewReducer {
var state = tuple.state var state = tuple.state
switch tuple.action.payload { switch tuple.action.payload {
case let .newCurrentBuffer(buffer): case let .newCurrentBuffer(buffer):
state.preview = MarkdownPreviewReducer.previewState( state.preview = MarkdownPreviewReducer.previewState(
for: tuple.state.uuid, for: tuple.state.uuid,
@ -169,14 +165,15 @@ class MarkdownPreviewReducer {
) )
state.preview.lastSearch = .reload state.preview.lastSearch = .reload
case let .setCursor(to:position): case let .setCursor(to: position):
if state.preview.lastSearch == .reload { if state.preview.lastSearch == .reload {
state.preview.lastSearch = .none state.preview.lastSearch = .none
break break
} }
guard state.previewTool.isForwardSearchAutomatically, guard state.previewTool.isForwardSearchAutomatically,
state.preview.lastSearch != .reverse else { state.preview.lastSearch != .reverse
else {
state.preview.editorPosition = Marked( state.preview.editorPosition = Marked(
mark: state.preview.editorPosition.mark, mark: state.preview.editorPosition.mark,
payload: position.payload payload: position.payload

View File

@ -4,16 +4,14 @@
*/ */
import Cocoa import Cocoa
import RxSwift
import PureLayout
import WebKit
import os
import NvimView import NvimView
import os
import PureLayout
import RxSwift
import WebKit
class MarkdownTool: NSView, UiComponent, WKNavigationDelegate { class MarkdownTool: NSView, UiComponent, WKNavigationDelegate {
enum Action { enum Action {
case refreshNow case refreshNow
case reverseSearch(to: Position) case reverseSearch(to: Position)
@ -84,7 +82,8 @@ class MarkdownTool: NSView, UiComponent, WKNavigationDelegate {
.observeOn(MainScheduler.instance) .observeOn(MainScheduler.instance)
.subscribe(onNext: { state in .subscribe(onNext: { state in
if state.viewToBeFocused != nil, if state.viewToBeFocused != nil,
case .markdownPreview = state.viewToBeFocused! { case .markdownPreview = state.viewToBeFocused!
{
self.beFirstResponder() self.beFirstResponder()
} }
@ -92,9 +91,10 @@ class MarkdownTool: NSView, UiComponent, WKNavigationDelegate {
self.automaticReverseMenuItem.boolState = state.previewTool.isReverseSearchAutomatically self.automaticReverseMenuItem.boolState = state.previewTool.isReverseSearchAutomatically
self.refreshOnWriteMenuItem.boolState = state.previewTool.isRefreshOnWrite self.refreshOnWriteMenuItem.boolState = state.previewTool.isRefreshOnWrite
if state.preview.status == .markdown if state.preview.status == .markdown,
&& state.previewTool.isForwardSearchAutomatically state.previewTool.isForwardSearchAutomatically,
&& state.preview.editorPosition.hasDifferentMark(as: self.editorPosition) { state.preview.editorPosition.hasDifferentMark(as: self.editorPosition)
{
self.forwardSearch(position: state.preview.editorPosition.payload) self.forwardSearch(position: state.preview.editorPosition.payload)
} }
@ -133,18 +133,23 @@ class MarkdownTool: NSView, UiComponent, WKNavigationDelegate {
private func addViews() { private func addViews() {
self.webview.navigationDelegate = self 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.webview.configureForAutoLayout()
self.addSubview(self.webview) self.addSubview(self.webview)
self.webview.autoPinEdgesToSuperviewEdges() 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)") 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)") self.webview.evaluateJavaScript("document.body.scrollTop = \(self.scrollTop)")
} }
@ -165,33 +170,45 @@ class MarkdownTool: NSView, UiComponent, WKNavigationDelegate {
private let userContentController = WKUserContentController() private let userContentController = WKUserContentController()
private let webviewMessageHandler = WebviewMessageHandler() private let webviewMessageHandler = WebviewMessageHandler()
private let automaticForwardMenuItem = NSMenuItem(title: "Automatic Forward Search", private let automaticForwardMenuItem = NSMenuItem(
action: nil, title: "Automatic Forward Search",
keyEquivalent: "") action: nil,
private let automaticReverseMenuItem = NSMenuItem(title: "Automatic Reverse Search", keyEquivalent: ""
action: nil, )
keyEquivalent: "") private let automaticReverseMenuItem = NSMenuItem(
private let refreshOnWriteMenuItem = NSMenuItem(title: "Refresh on Write", action: nil, keyEquivalent: "") 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, private let log = OSLog(
category: Defs.LoggerCategory.ui) subsystem: Defs.loggerSubsystem,
category: Defs.LoggerCategory.ui
)
required init?(coder: NSCoder) { @available(*, unavailable)
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
private func forwardSearch(position: Position) { private func forwardSearch(position: Position) {
self.webview.evaluateJavaScript("scrollToPosition(\(position.row), \(position.column));") { result, error in self.webview
if let scrollTop = result as? Int { .evaluateJavaScript("scrollToPosition(\(position.row), \(position.column));") { result, _ in
self.scrollTop = scrollTop if let scrollTop = result as? Int {
self.scrollTop = scrollTop
}
} }
}
} }
} }
// MARK: - Actions // MARK: - Actions
extension MarkdownTool {
extension MarkdownTool {
@objc func refreshNowAction(_: Any?) { @objc func refreshNowAction(_: Any?) {
self.emit(UuidAction(uuid: self.uuid, action: .refreshNow)) self.emit(UuidAction(uuid: self.uuid, action: .refreshNow))
} }
@ -205,11 +222,13 @@ extension MarkdownTool {
} }
@objc func automaticForwardSearchAction(_ sender: NSMenuItem) { @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) { @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) { @objc func refreshOnWriteAction(_ sender: NSMenuItem) {
@ -218,9 +237,8 @@ extension MarkdownTool {
} }
private class WebviewMessageHandler: NSObject, WKScriptMessageHandler { private class WebviewMessageHandler: NSObject, WKScriptMessageHandler {
var source: Observable<(Position, Int)> { var source: Observable<(Position, Int)> {
return self.subject.asObservable() self.subject.asObservable()
} }
deinit { deinit {
@ -235,7 +253,7 @@ private class WebviewMessageHandler: NSObject, WKScriptMessageHandler {
guard let lineBegin = msgBody["lineBegin"], guard let lineBegin = msgBody["lineBegin"],
let columnBegin = msgBody["columnBegin"], let columnBegin = msgBody["columnBegin"],
let scrollTop = msgBody["scrollTop"] let scrollTop = msgBody["scrollTop"]
else { else {
return return
} }

View File

@ -6,7 +6,6 @@
import Foundation import Foundation
class MarkdownToolReducer: ReducerType { class MarkdownToolReducer: ReducerType {
typealias StateType = MainWindow.State typealias StateType = MainWindow.State
typealias ActionType = UuidAction<MarkdownTool.Action> typealias ActionType = UuidAction<MarkdownTool.Action>
@ -18,19 +17,17 @@ class MarkdownToolReducer: ReducerType {
var state = tuple.state var state = tuple.state
switch tuple.action.payload { switch tuple.action.payload {
case let .setAutomaticReverseSearch(to: value):
case let .setAutomaticReverseSearch(to:value):
state.previewTool.isReverseSearchAutomatically = value state.previewTool.isReverseSearchAutomatically = value
case let .setAutomaticForwardSearch(to:value): case let .setAutomaticForwardSearch(to: value):
state.previewTool.isForwardSearchAutomatically = value state.previewTool.isForwardSearchAutomatically = value
case let .setRefreshOnWrite(to:value): case let .setRefreshOnWrite(to: value):
state.previewTool.isRefreshOnWrite = value state.previewTool.isRefreshOnWrite = value
default: default:
return tuple return tuple
} }
return (state, tuple.action, true) return (state, tuple.action, true)

View File

@ -6,7 +6,6 @@
import Cocoa import Cocoa
class OpenQuicklyFileViewRow: NSTableRowView { class OpenQuicklyFileViewRow: NSTableRowView {
override func drawSelection(in dirtyRect: NSRect) { override func drawSelection(in dirtyRect: NSRect) {
if self.isSelected { if self.isSelected {
NSColor.selectedControlColor.set() NSColor.selectedControlColor.set()

View File

@ -7,7 +7,6 @@ import Foundation
import RxSwift import RxSwift
class OpenQuicklyReducer: ReducerType { class OpenQuicklyReducer: ReducerType {
typealias StateType = AppState typealias StateType = AppState
typealias ActionType = OpenQuicklyWindow.Action typealias ActionType = OpenQuicklyWindow.Action
@ -17,7 +16,6 @@ class OpenQuicklyReducer: ReducerType {
var appState = pair.state var appState = pair.state
switch pair.action { switch pair.action {
case let .setUsesVcsIgnores(usesVcsIgnores): case let .setUsesVcsIgnores(usesVcsIgnores):
guard let uuid = appState.currentMainWindowUuid else { return pair } guard let uuid = appState.currentMainWindowUuid else { return pair }
appState.mainWindows[uuid]?.usesVcsIgnores = usesVcsIgnores appState.mainWindows[uuid]?.usesVcsIgnores = usesVcsIgnores
@ -29,21 +27,17 @@ class OpenQuicklyReducer: ReducerType {
case .close: case .close:
appState.openQuickly.open = false appState.openQuickly.open = false
break
} }
return (appState, pair.action, true) return (appState, pair.action, true)
} }
class MainWindowReducer: ReducerType { class MainWindowReducer: ReducerType {
typealias StateType = AppState typealias StateType = AppState
typealias ActionType = UuidAction<MainWindow.Action> typealias ActionType = UuidAction<MainWindow.Action>
func typedReduce(_ pair: ReduceTuple) -> ReduceTuple { func typedReduce(_ pair: ReduceTuple) -> ReduceTuple {
switch pair.action.payload { switch pair.action.payload {
case .openQuickly: case .openQuickly:
var appState = pair.state var appState = pair.state
@ -56,7 +50,6 @@ class OpenQuicklyReducer: ReducerType {
default: default:
return pair return pair
} }
} }
} }

View File

@ -4,28 +4,27 @@
*/ */
import Cocoa import Cocoa
import RxSwift
import RxCocoa
import PureLayout
import os
import Commons import Commons
import os
import PureLayout
import RxCocoa
import RxSwift
class OpenQuicklyWindow: NSObject, class OpenQuicklyWindow: NSObject,
UiComponent, UiComponent,
NSWindowDelegate, NSWindowDelegate,
NSTextFieldDelegate, NSTextFieldDelegate,
NSTableViewDelegate { NSTableViewDelegate
{
typealias StateType = AppState typealias StateType = AppState
enum Action { enum Action {
case setUsesVcsIgnores(Bool) case setUsesVcsIgnores(Bool)
case open(URL) case open(URL)
case close case close
} }
@objc dynamic private(set) var unsortedScoredUrls = [ScoredUrl]() @objc private(set) dynamic var unsortedScoredUrls = [ScoredUrl]()
// Call this only when quitting // Call this only when quitting
func cleanUp() { func cleanUp() {
@ -62,6 +61,7 @@ class OpenQuicklyWindow: NSObject,
} }
// MARK: - Private // MARK: - Private
private let emit: (Action) -> Void private let emit: (Action) -> Void
private let disposeBag = DisposeBag() private let disposeBag = DisposeBag()
@ -83,8 +83,10 @@ class OpenQuicklyWindow: NSObject,
private let cwdControl = NSPathControl(forAutoLayout: ()) private let cwdControl = NSPathControl(forAutoLayout: ())
private let fileView = NSTableView.standardTableView() private let fileView = NSTableView.standardTableView()
private let log = OSLog(subsystem: Defs.loggerSubsystem, private let log = OSLog(
category: Defs.LoggerCategory.ui) subsystem: Defs.loggerSubsystem,
category: Defs.LoggerCategory.ui
)
private var window: NSWindow { self.windowController.window! } private var window: NSWindow { self.windowController.window! }
@ -108,7 +110,7 @@ class OpenQuicklyWindow: NSObject,
let windowIsOpen = self.window.isKeyWindow let windowIsOpen = self.window.isKeyWindow
// The window is open and the user changed the setting // 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.usesVcsIgnores = curWinState.usesVcsIgnores
self.useVcsIgnoresCheckBox.boolState = curWinState.usesVcsIgnores self.useVcsIgnoresCheckBox.boolState = curWinState.usesVcsIgnores
@ -190,7 +192,7 @@ class OpenQuicklyWindow: NSObject,
} }
private func endProgress() { private func endProgress() {
DispatchQueue.main.async {self.progressIndicator.stopAnimation(self) } DispatchQueue.main.async { self.progressIndicator.stopAnimation(self) }
} }
private func updateRootUrls(state: AppState) { private func updateRootUrls(state: AppState) {
@ -298,10 +300,10 @@ class OpenQuicklyWindow: NSObject,
} }
// MARK: - NSTableViewDelegate // MARK: - NSTableViewDelegate
extension OpenQuicklyWindow {
func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? { extension OpenQuicklyWindow {
return OpenQuicklyFileViewRow() func tableView(_: NSTableView, rowViewForRow _: Int) -> NSTableRowView? {
OpenQuicklyFileViewRow()
} }
func tableView(_ tableView: NSTableView, viewFor _: NSTableColumn?, row: Int) -> NSView? { func tableView(_ tableView: NSTableView, viewFor _: NSTableColumn?, row: Int) -> NSView? {
@ -313,7 +315,7 @@ extension OpenQuicklyWindow {
)?.reset() )?.reset()
let cell = cachedCell ?? ImageAndTextTableCell(withIdentifier: "file-view-row") 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].") self.log.error("Could not convert arranged objects to [ScoredUrl].")
return nil return nil
} }
@ -351,27 +353,26 @@ extension OpenQuicklyWindow {
} }
// MARK: - NSTextFieldDelegate // MARK: - NSTextFieldDelegate
extension OpenQuicklyWindow {
extension OpenQuicklyWindow {
func control( func control(
_ control: NSControl, _: NSControl,
textView: NSTextView, textView _: NSTextView,
doCommandBy commandSelector: Selector doCommandBy commandSelector: Selector
) -> Bool { ) -> Bool {
switch commandSelector { switch commandSelector {
case NSSelectorFromString("cancelOperation:"): case NSSelectorFromString("cancelOperation:"):
self.window.performClose(self) self.window.performClose(self)
return true return true
case NSSelectorFromString("insertNewline:"): 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].") self.log.error("Could not convert arranged objects to [ScoredUrl].")
return true return true
} }
let selectedRow = self.fileView.selectedRow 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.emit(.open(sortedUrls[selectedRow].url))
self.window.performClose(self) self.window.performClose(self)
@ -387,7 +388,6 @@ extension OpenQuicklyWindow {
default: default:
return false return false
} }
} }
@ -410,8 +410,8 @@ extension OpenQuicklyWindow {
} }
// MARK: - NSWindowDelegate // MARK: - NSWindowDelegate
extension OpenQuicklyWindow {
extension OpenQuicklyWindow {
func windowShouldClose(_: NSWindow) -> Bool { func windowShouldClose(_: NSWindow) -> Bool {
self.emit(.close) self.emit(.close)

View File

@ -8,7 +8,6 @@ import DictionaryCoding
import os import os
class PrefMiddleware: MiddlewareType { class PrefMiddleware: MiddlewareType {
typealias StateType = AppState typealias StateType = AppState
typealias ActionType = AnyAction typealias ActionType = AnyAction
@ -27,7 +26,7 @@ class PrefMiddleware: MiddlewareType {
} }
func typedApply(_ reduce: @escaping TypedActionReduceFunction) -> TypedActionReduceFunction { func typedApply(_ reduce: @escaping TypedActionReduceFunction) -> TypedActionReduceFunction {
return { tuple in { tuple in
let result = reduce(tuple) let result = reduce(tuple)
guard result.modified else { guard result.modified else {
@ -52,7 +51,7 @@ class PrefMiddleware: MiddlewareType {
notification.identifier = UUID().uuidString notification.identifier = UUID().uuidString
notification.title = "No monospaced font" notification.title = "No monospaced font"
notification.informativeText = "The font you selected\(newFontNameText) does not seem " 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) NSUserNotificationCenter.default.deliver(notification)
} }
} }
@ -73,12 +72,11 @@ class PrefMiddleware: MiddlewareType {
private var currentFont = NSFont.userFixedPitchFont(ofSize: 13)! private var currentFont = NSFont.userFixedPitchFont(ofSize: 13)!
class MainWindowMiddleware: MiddlewareType { class MainWindowMiddleware: MiddlewareType {
typealias StateType = AppState typealias StateType = AppState
typealias ActionType = UuidAction<MainWindow.Action> typealias ActionType = UuidAction<MainWindow.Action>
func typedApply(_ reduce: @escaping TypedActionReduceFunction) -> TypedActionReduceFunction { func typedApply(_ reduce: @escaping TypedActionReduceFunction) -> TypedActionReduceFunction {
return { tuple in { tuple in
let result = reduce(tuple) let result = reduce(tuple)
guard case .close = tuple.action.payload else { guard case .close = tuple.action.payload else {
@ -96,8 +94,10 @@ class PrefMiddleware: MiddlewareType {
} }
} }
private let log = OSLog(subsystem: Defs.loggerSubsystem, private let log = OSLog(
category: Defs.LoggerCategory.middleware) subsystem: Defs.loggerSubsystem,
category: Defs.LoggerCategory.middleware
)
} }
} }

View File

@ -6,10 +6,9 @@
import Cocoa import Cocoa
class PrefPane: NSView { class PrefPane: NSView {
// Return true to place this to the upper left corner when the scroll view is bigger than this view. // Return true to place this to the upper left corner when the scroll view is bigger than this view.
override var isFlipped: Bool { override var isFlipped: Bool {
return true true
} }
var displayName: String { var displayName: String {
@ -17,7 +16,7 @@ class PrefPane: NSView {
} }
var pinToContainer: Bool { var pinToContainer: Bool {
return false false
} }
func paneWillAppear() { func paneWillAppear() {
@ -30,19 +29,19 @@ class PrefPane: NSView {
} }
// MARK: - Control Utils // MARK: - Control Utils
extension PrefPane {
extension PrefPane {
func paneTitleTextField(title: String) -> NSTextField { func paneTitleTextField(title: String) -> NSTextField {
let field = NSTextField.defaultTitleTextField() let field = NSTextField.defaultTitleTextField()
field.font = NSFont.boldSystemFont(ofSize: 16) field.font = NSFont.boldSystemFont(ofSize: 16)
field.alignment = .left; field.alignment = .left
field.stringValue = title field.stringValue = title
return field return field
} }
func titleTextField(title: String) -> NSTextField { func titleTextField(title: String) -> NSTextField {
let field = NSTextField.defaultTitleTextField() let field = NSTextField.defaultTitleTextField()
field.alignment = .right; field.alignment = .right
field.stringValue = title field.stringValue = title
return field return field
} }

View File

@ -7,21 +7,22 @@ import Cocoa
import NvimView import NvimView
class PrefUtils { class PrefUtils {
static func value<T>(from dict: [String: Any], for key: String) -> T? { 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 { 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]? { 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 { static func float(from dict: [String: Any], for key: String,
return (dict[key] as? NSNumber)?.floatValue ?? defaultValue default defaultValue: Float) -> Float
{
(dict[key] as? NSNumber)?.floatValue ?? defaultValue
} }
static func float(from dict: [String: Any], for key: String) -> Float? { 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 { 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? { 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 { static func string(from dict: [String: Any], for key: String,
return dict[key] as? String ?? defaultValue default defaultValue: String) -> String
{
dict[key] as? String ?? defaultValue
} }
static func saneFont(_ fontName: String, fontSize: CGFloat) -> NSFont { static func saneFont(_ fontName: String, fontSize: CGFloat) -> NSFont {
@ -66,19 +69,19 @@ class PrefUtils {
static func saneLinespacing(_ fLinespacing: Float) -> CGFloat { static func saneLinespacing(_ fLinespacing: Float) -> CGFloat {
let linespacing = fLinespacing.cgf let linespacing = fLinespacing.cgf
guard linespacing >= NvimView.minLinespacing && linespacing <= NvimView.maxLinespacing else { guard linespacing >= NvimView.minLinespacing, linespacing <= NvimView.maxLinespacing else {
return NvimView.defaultLinespacing return NvimView.defaultLinespacing
} }
return linespacing return linespacing
} }
static func saneCharacterspacing(_ fCharacterspacing: Float) -> CGFloat { static func saneCharacterspacing(_ fCharacterspacing: Float) -> CGFloat {
let characterspacing = fCharacterspacing.cgf let characterspacing = fCharacterspacing.cgf
guard characterspacing >= 0.0 else { guard characterspacing >= 0.0 else {
return NvimView.defaultCharacterspacing return NvimView.defaultCharacterspacing
} }
return characterspacing return characterspacing
} }
} }

View File

@ -4,18 +4,17 @@
*/ */
import Cocoa import Cocoa
import RxSwift
import PureLayout import PureLayout
import RxSwift
class PrefWindow: NSObject, class PrefWindow: NSObject,
UiComponent, UiComponent,
NSWindowDelegate, NSWindowDelegate,
NSTableViewDataSource, NSTableViewDelegate { NSTableViewDataSource, NSTableViewDelegate
{
typealias StateType = AppState typealias StateType = AppState
enum Action { enum Action {
case close case close
} }
@ -66,7 +65,7 @@ class PrefWindow: NSObject,
private let windowController: NSWindowController private let windowController: NSWindowController
private var window: NSWindow { private var window: NSWindow {
return self.windowController.window! self.windowController.window!
} }
private let categoryView = NSTableView.standardSourceListTableView() private let categoryView = NSTableView.standardSourceListTableView()
@ -76,7 +75,7 @@ class PrefWindow: NSObject,
private let panes: [PrefPane] private let panes: [PrefPane]
private var currentPane: PrefPane { private var currentPane: PrefPane {
get { get {
return self.paneContainer.documentView as! PrefPane self.paneContainer.documentView as! PrefPane
} }
set { set {
@ -124,8 +123,8 @@ class PrefWindow: NSObject,
} }
// MARK: - NSWindowDelegate // MARK: - NSWindowDelegate
extension PrefWindow {
extension PrefWindow {
func windowShouldClose(_: NSWindow) -> Bool { func windowShouldClose(_: NSWindow) -> Bool {
self.emit(.close) self.emit(.close)
@ -138,20 +137,24 @@ extension PrefWindow {
} }
// MARK: - NSTableViewDataSource // MARK: - NSTableViewDataSource
extension PrefWindow {
extension PrefWindow {
@objc(numberOfRowsInTableView:) func numberOfRows(in _: NSTableView) -> Int { @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? { @objc(tableView: objectValueForTableColumn:row:) func tableView(
return self.panes[row].displayName _: NSTableView,
objectValueFor _: NSTableColumn?,
row: Int
) -> Any? {
self.panes[row].displayName
} }
} }
// MARK: - NSTableViewDelegate // MARK: - NSTableViewDelegate
extension PrefWindow {
extension PrefWindow {
func tableViewSelectionDidChange(_: Notification) { func tableViewSelectionDidChange(_: Notification) {
let idx = self.categoryView.selectedRow let idx = self.categoryView.selectedRow
self.panes[idx].paneWillAppear() self.panes[idx].paneWillAppear()

View File

@ -6,7 +6,6 @@
import Foundation import Foundation
class PrefWindowReducer: ReducerType { class PrefWindowReducer: ReducerType {
typealias StateType = AppState typealias StateType = AppState
typealias ActionType = PrefWindow.Action typealias ActionType = PrefWindow.Action
@ -14,10 +13,8 @@ class PrefWindowReducer: ReducerType {
var state = pair.state var state = pair.state
switch pair.action { switch pair.action {
case .close: case .close:
state.preferencesOpen = Marked(false) state.preferencesOpen = Marked(false)
} }
return (state, pair.action, true) return (state, pair.action, true)

View File

@ -6,7 +6,6 @@
import Foundation import Foundation
class Resources { class Resources {
static let resourceUrl = Bundle.main.resourceURL! static let resourceUrl = Bundle.main.resourceURL!
static let previewUrl = resourceUrl.appendingPathComponent("preview") static let previewUrl = resourceUrl.appendingPathComponent("preview")

View File

@ -6,7 +6,6 @@
import Foundation import Foundation
class RpcAppearanceEpic: EpicType { class RpcAppearanceEpic: EpicType {
typealias StateType = AppState typealias StateType = AppState
typealias ActionType = UuidAction<MainWindow.Action> typealias ActionType = UuidAction<MainWindow.Action>
typealias EmitActionType = AppearancePref.Action typealias EmitActionType = AppearancePref.Action
@ -18,23 +17,21 @@ class RpcAppearanceEpic: EpicType {
func typedApply( func typedApply(
_ reduce: @escaping TypedActionReduceFunction _ reduce: @escaping TypedActionReduceFunction
) -> TypedActionReduceFunction { ) -> TypedActionReduceFunction {
return { tuple in { tuple in
let result = reduce(tuple) let result = reduce(tuple)
switch tuple.action.payload { switch tuple.action.payload {
case let .setFont(font):
case .setFont(let font):
self.emit(.setFont(font)) self.emit(.setFont(font))
case .setLinespacing(let linespacing): case let .setLinespacing(linespacing):
self.emit(.setLinespacing(linespacing)) self.emit(.setLinespacing(linespacing))
case .setCharacterspacing(let characterspacing): case let .setCharacterspacing(characterspacing):
self.emit(.setCharacterspacing(characterspacing)) self.emit(.setCharacterspacing(characterspacing))
default: default:
break break
} }
return result return result

View File

@ -6,7 +6,6 @@
import Foundation import Foundation
enum RpcEvent: String, CaseIterable { enum RpcEvent: String, CaseIterable {
static let prefix = "com.qvacua.vimr.rpc-events" static let prefix = "com.qvacua.vimr.rpc-events"
case makeSessionTemporary = "com.qvacua.vimr.rpc-events.make-session-temporary" case makeSessionTemporary = "com.qvacua.vimr.rpc-events.make-session-temporary"

View File

@ -6,8 +6,7 @@
import Cocoa import Cocoa
class ShortcutItem: NSObject, Comparable { 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 title: String
@objc dynamic var isLeaf: Bool @objc dynamic var isLeaf: Bool
@ -19,11 +18,11 @@ class ShortcutItem: NSObject, Comparable {
var isContainer: Bool { !self.isLeaf } var isContainer: Bool { !self.isLeaf }
override var description: String { override var description: String {
"<ShortcutItem: \(title), " + "<ShortcutItem: \(self.title), " +
"id: '\(self.identifier ?? "")', " + "id: '\(self.identifier ?? "")', " +
"isLeaf: \(self.isLeaf), " + "isLeaf: \(self.isLeaf), " +
"childrenCount: \(self.children?.count ?? -1)" + "childrenCount: \(self.children?.count ?? -1)" +
">" ">"
} }
let item: NSMenuItem? let item: NSMenuItem?

View File

@ -9,10 +9,10 @@ import RxSwift
import ShortcutRecorder import ShortcutRecorder
class ShortcutsPref: PrefPane, class ShortcutsPref: PrefPane,
UiComponent, UiComponent,
NSOutlineViewDelegate, NSOutlineViewDelegate,
RecorderControlDelegate { RecorderControlDelegate
{
typealias StateType = AppState typealias StateType = AppState
@objc dynamic var content = [ShortcutItem]() @objc dynamic var content = [ShortcutItem]()
@ -21,7 +21,7 @@ class ShortcutsPref: PrefPane,
override var pinToContainer: Bool { true } 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. // We know that the identifier is not empty.
let shortcutSuiteName = Bundle.main.bundleIdentifier! + ".menuitems" let shortcutSuiteName = Bundle.main.bundleIdentifier! + ".menuitems"
self.shortcutsUserDefaults = UserDefaults(suiteName: shortcutSuiteName) self.shortcutsUserDefaults = UserDefaults(suiteName: shortcutSuiteName)
@ -33,14 +33,15 @@ class ShortcutsPref: PrefPane,
super.init(frame: .zero) super.init(frame: .zero)
if let version = self.shortcutsUserDefaults?.integer(forKey: defaultsVersionKey), if let version = self.shortcutsUserDefaults?.integer(forKey: defaultsVersionKey),
version > defaultsVersion { version > defaultsVersion
{
let alert = NSAlert() let alert = NSAlert()
alert.alertStyle = .warning alert.alertStyle = .warning
alert.messageText = "Incompatible Defaults for Shortcuts" alert.messageText = "Incompatible Defaults for Shortcuts"
alert.informativeText = "The stored defaults for shortcuts are not compatible with " alert.informativeText = "The stored defaults for shortcuts are not compatible with "
+ "this version of VimR. You can delete the stored defaults " + "this version of VimR. You can delete the stored defaults "
+ "by executing 'defaults delete com.qvacua.VimR.menuitems' " + "by executing 'defaults delete com.qvacua.VimR.menuitems' "
+ "in Terminal." + "in Terminal."
alert.runModal() alert.runModal()
return return
} }
@ -59,7 +60,8 @@ class ShortcutsPref: PrefPane,
self.shortcutList.expandItem(nil, expandChildren: true) 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 shortcutList = NSOutlineView.standardOutlineView()
private let shortcutScrollView = NSScrollView.standardScrollView() private let shortcutScrollView = NSScrollView.standardScrollView()
@ -126,7 +128,7 @@ class ShortcutsPref: PrefPane,
private func traverseMenuItems(with fn: (String, NSMenuItem) -> Void) { private func traverseMenuItems(with fn: (String, NSMenuItem) -> Void) {
var queue = self.shortcutItemsRoot.children ?? [] var queue = self.shortcutItemsRoot.children ?? []
while (!queue.isEmpty) { while !queue.isEmpty {
guard let item = queue.popLast() else { break } guard let item = queue.popLast() else { break }
if item.isContainer, let children = item.children { if item.isContainer, let children = item.children {
queue.append(contentsOf: children) queue.append(contentsOf: children)
@ -168,18 +170,18 @@ class ShortcutsPref: PrefPane,
shortcutItem: ShortcutItem(title: $0.title, isLeaf: false, item: $0) shortcutItem: ShortcutItem(title: $0.title, isLeaf: false, item: $0)
) )
} }
while (!queue.isEmpty) { while !queue.isEmpty {
guard let entry = queue.popLast() else { break } guard let entry = queue.popLast() else { break }
if !entry.shortcutItem.isLeaf 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) entry.parent.children?.append(entry.shortcutItem)
} }
if entry.shortcutItem.isContainer, if entry.shortcutItem.isContainer,
let childMenuItems = entry.shortcutItem.item?.submenu?.items { let childMenuItems = entry.shortcutItem.item?.submenu?.items
{
let shortcutChildItems = childMenuItems let shortcutChildItems = childMenuItems
.filter { !$0.title.isEmpty } .filter { !$0.title.isEmpty }
.map { menuItem in .map { menuItem in
@ -233,9 +235,9 @@ class ShortcutsPref: PrefPane,
} }
// MARK: - Actions // MARK: - Actions
extension ShortcutsPref {
@objc func resetToDefault(_ sender: NSButton) { extension ShortcutsPref {
@objc func resetToDefault(_: NSButton) {
guard let window = self.window else { return } guard let window = self.window else { return }
let alert = NSAlert() let alert = NSAlert()
@ -260,8 +262,8 @@ extension ShortcutsPref {
} }
// MARK: - NSOutlineViewDelegate // MARK: - NSOutlineViewDelegate
extension ShortcutsPref {
extension ShortcutsPref {
private func isUppercase(_ str: String) -> Bool { private func isUppercase(_ str: String) -> Bool {
for c in str.unicodeScalars { for c in str.unicodeScalars {
if !CharacterSet.uppercaseLetters.contains(c) { return false } if !CharacterSet.uppercaseLetters.contains(c) { return false }
@ -270,7 +272,7 @@ extension ShortcutsPref {
return true return true
} }
func outlineView(_ outlineView: NSOutlineView, rowViewForItem item: Any) -> NSTableRowView? { func outlineView(_: NSOutlineView, rowViewForItem _: Any) -> NSTableRowView? {
let view = self.shortcutList.makeView( let view = self.shortcutList.makeView(
withIdentifier: NSUserInterfaceItemIdentifier("shortcut-row-view"), withIdentifier: NSUserInterfaceItemIdentifier("shortcut-row-view"),
owner: self owner: self
@ -279,7 +281,7 @@ extension ShortcutsPref {
return view return view
} }
func outlineView(_: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? { func outlineView(_: NSOutlineView, viewFor _: NSTableColumn?, item: Any) -> NSView? {
let cellView = self.shortcutList.makeView( let cellView = self.shortcutList.makeView(
withIdentifier: NSUserInterfaceItemIdentifier("shortcut-cell-view"), withIdentifier: NSUserInterfaceItemIdentifier("shortcut-cell-view"),
owner: self owner: self
@ -306,7 +308,7 @@ extension ShortcutsPref {
return cellView return cellView
} }
func outlineView(_: NSOutlineView, heightOfRowByItem item: Any) -> CGFloat { 28 } func outlineView(_: NSOutlineView, heightOfRowByItem _: Any) -> CGFloat { 28 }
private func areShortcutsEqual(_ identifier: String) -> Bool { private func areShortcutsEqual(_ identifier: String) -> Bool {
guard let dataFromDefaults = self.shortcutsDefaultsController.value( guard let dataFromDefaults = self.shortcutsDefaultsController.value(
@ -324,9 +326,9 @@ extension ShortcutsPref {
} }
// MARK: - SRRecorderControlDelegate // MARK: - SRRecorderControlDelegate
extension ShortcutsPref {
func recorderControlDidEndRecording(_ sender: RecorderControl) { extension ShortcutsPref {
func recorderControlDidEndRecording(_: RecorderControl) {
self.treeController.rearrangeObjects() self.treeController.rearrangeObjects()
} }
} }
@ -335,7 +337,6 @@ private let defaultsVersionKey = "version"
private let defaultsVersion = 337 private let defaultsVersion = 337
private class DataToKeyEquivalentTransformer: ValueTransformer { private class DataToKeyEquivalentTransformer: ValueTransformer {
override func transformedValue(_ value: Any?) -> Any? { override func transformedValue(_ value: Any?) -> Any? {
guard let shortcut = ValueTransformer guard let shortcut = ValueTransformer
.keyedUnarchiveFromDataTransformer .keyedUnarchiveFromDataTransformer
@ -346,7 +347,6 @@ private class DataToKeyEquivalentTransformer: ValueTransformer {
} }
private class DataToKeyEquivalentModifierMaskTransformer: ValueTransformer { private class DataToKeyEquivalentModifierMaskTransformer: ValueTransformer {
override func transformedValue(_ value: Any?) -> Any? { override func transformedValue(_ value: Any?) -> Any? {
guard let shortcut = ValueTransformer guard let shortcut = ValueTransformer
.keyedUnarchiveFromDataTransformer .keyedUnarchiveFromDataTransformer

View File

@ -7,19 +7,18 @@ import Cocoa
import ShortcutRecorder import ShortcutRecorder
class ShortcutTableRow: NSTableRowView { class ShortcutTableRow: NSTableRowView {
init(withIdentifier identifier: String) { init(withIdentifier identifier: String) {
super.init(frame: .zero) super.init(frame: .zero)
self.identifier = NSUserInterfaceItemIdentifier(identifier) self.identifier = NSUserInterfaceItemIdentifier(identifier)
} }
required init?(coder: NSCoder) { @available(*, unavailable)
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
} }
class ShortcutTableCell: NSTableCellView { class ShortcutTableCell: NSTableCellView {
static let font = NSFont.systemFont(ofSize: 13) static let font = NSFont.systemFont(ofSize: 13)
static let boldFont = NSFont.boldSystemFont(ofSize: 13) static let boldFont = NSFont.boldSystemFont(ofSize: 13)
@ -117,6 +116,6 @@ class ShortcutTableCell: NSTableCellView {
private let shortcutRecorder = RecorderControl(forAutoLayout: ()) private let shortcutRecorder = RecorderControl(forAutoLayout: ())
private let _textField = NSTextField(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") }
} }

View File

@ -4,24 +4,21 @@
*/ */
import Cocoa import Cocoa
import Commons
import NvimView import NvimView
import RxSwift import RxSwift
import Commons
import Workspace import Workspace
struct AppState: Codable { struct AppState: Codable {
enum AfterLastWindowAction: String, Codable { enum AfterLastWindowAction: String, Codable {
case doNothing = "do-nothing" case doNothing = "do-nothing"
case hide = "hide" case hide
case quit = "quit" case quit
} }
static let `default` = AppState() static let `default` = AppState()
enum CodingKeys: String, CodingKey { enum CodingKeys: String, CodingKey {
case openNewMainWindowOnLaunch = "open-new-window-when-launching" case openNewMainWindowOnLaunch = "open-new-window-when-launching"
case openNewMainWindowOnReactivation = "open-new-window-on-reactivation" case openNewMainWindowOnReactivation = "open-new-window-on-reactivation"
case afterLastWindowAction = "after-last-window-action" case afterLastWindowAction = "after-last-window-action"
@ -56,33 +53,44 @@ struct AppState: Codable {
init(from decoder: Decoder) throws { init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self) let container = try decoder.container(keyedBy: CodingKeys.self)
self.openNewMainWindowOnLaunch = try container.decode(forKey: .openNewMainWindowOnLaunch, self.openNewMainWindowOnLaunch = try container.decode(
default: AppState.default.openNewMainWindowOnLaunch) forKey: .openNewMainWindowOnLaunch,
self.openNewMainWindowOnReactivation = try container.decode( default: AppState.default
forKey: .openNewMainWindowOnReactivation, default: AppState.default.openNewMainWindowOnReactivation .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.openQuickly = try container.decode(
self.mainWindowTemplate = try container.decode(forKey: .mainWindowTemplate, default: MainWindow.State.default) forKey: .openQuickly,
default: OpenQuicklyWindow.State.default
)
self.mainWindowTemplate = try container.decode(
forKey: .mainWindowTemplate,
default: MainWindow.State.default
)
} }
// Use generated encode(to:) // Use generated encode(to:)
private init() { private init() {}
}
} }
extension OpenQuicklyWindow { extension OpenQuicklyWindow {
struct State: Codable { struct State: Codable {
static let `default` = State() static let `default` = State()
enum CodingKeys: String, CodingKey { enum CodingKeys: String, CodingKey {
case defaultUsesVcsIgnore = "default-uses-vcs-ignores" case defaultUsesVcsIgnore = "default-uses-vcs-ignores"
} }
@ -103,17 +111,14 @@ extension OpenQuicklyWindow {
try container.encode(self.defaultUsesVcsIgnores, forKey: .defaultUsesVcsIgnore) try container.encode(self.defaultUsesVcsIgnores, forKey: .defaultUsesVcsIgnore)
} }
private init() { private init() {}
}
} }
} }
struct PreviewState { struct PreviewState {
static let `default` = PreviewState() static let `default` = PreviewState()
enum Status { enum Status {
case none case none
case notSaved case notSaved
case error case error
@ -121,7 +126,6 @@ struct PreviewState {
} }
enum SearchAction { enum SearchAction {
case none case none
case forward case forward
case reverse case reverse
@ -160,7 +164,6 @@ struct PreviewState {
} }
struct HtmlPreviewState { struct HtmlPreviewState {
static let `default` = HtmlPreviewState() static let `default` = HtmlPreviewState()
var htmlFile: URL? var htmlFile: URL?
@ -168,11 +171,9 @@ struct HtmlPreviewState {
} }
struct AppearanceState: Codable { struct AppearanceState: Codable {
static let `default` = AppearanceState() static let `default` = AppearanceState()
enum CodingKeys: String, CodingKey { enum CodingKeys: String, CodingKey {
case usesTheme = "uses-theme" case usesTheme = "uses-theme"
case showsFileIcon = "shows-file-icon" case showsFileIcon = "shows-file-icon"
case editorFontName = "editor-font-name" case editorFontName = "editor-font-name"
@ -196,15 +197,23 @@ struct AppearanceState: Codable {
if let fontName = try container.decodeIfPresent(String.self, forKey: .editorFontName), if let fontName = try container.decodeIfPresent(String.self, forKey: .editorFontName),
let fontSize = try container.decodeIfPresent(Float.self, forKey: .editorFontSize), 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 self.font = font
} else { } else {
self.font = NvimView.defaultFont self.font = NvimView.defaultFont
} }
self.linespacing = (try container.decodeIfPresent(Float.self, forKey: .editorLinespacing) ?? 1.0).cgf self
self.characterspacing = (try container.decodeIfPresent(Float.self, forKey: .editorCharacterspacing) ?? 1.0).cgf .linespacing = (try container.decodeIfPresent(Float.self, forKey: .editorLinespacing) ?? 1.0)
self.usesLigatures = try container.decodeIfPresent(Bool.self, forKey: .editorUsesLigatures) ?? true .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.usesTheme = try container.decodeIfPresent(Bool.self, forKey: .usesTheme) ?? true
self.showsFileIcon = try container.decodeIfPresent(Bool.self, forKey: .showsFileIcon) ?? 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) try container.encode(self.usesLigatures, forKey: .editorUsesLigatures)
} }
private init() { private init() {}
}
} }
extension MainWindow { extension MainWindow {
struct State: Codable { struct State: Codable {
static let `default` = State(isAllToolsVisible: true, isToolButtonsVisible: true) static let `default` = State(isAllToolsVisible: true, isToolButtonsVisible: true)
static let defaultTools: [MainWindow.Tools: WorkspaceToolState] = [ static let defaultTools: [MainWindow.Tools: WorkspaceToolState] = [
@ -239,7 +245,8 @@ extension MainWindow {
.htmlPreview: WorkspaceToolState(location: .right, dimension: 500, open: false), .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 isAllToolsVisible = true
var isToolButtonsVisible = true var isToolButtonsVisible = true
@ -303,12 +310,11 @@ extension MainWindow {
} }
enum CodingKeys: String, CodingKey { enum CodingKeys: String, CodingKey {
case allToolsVisible = "is-all-tools-visible" case allToolsVisible = "is-all-tools-visible"
case toolButtonsVisible = "is-tool-buttons-visible" case toolButtonsVisible = "is-tool-buttons-visible"
case orderedTools = "ordered-tools" case orderedTools = "ordered-tools"
case activeTools = "active-tools" case activeTools = "active-tools"
case frame = "frame" case frame
case isLeftOptionMeta = "is-left-option-meta" case isLeftOptionMeta = "is-left-option-meta"
case isRightOptionMeta = "is-right-option-meta" case isRightOptionMeta = "is-right-option-meta"
@ -319,7 +325,7 @@ extension MainWindow {
case drawsParallel = "draws-parallel" case drawsParallel = "draws-parallel"
case isShowHidden = "is-show-hidden" case isShowHidden = "is-show-hidden"
case appearance = "appearance" case appearance
case workspaceTools = "workspace-tool" case workspaceTools = "workspace-tool"
case previewTool = "preview-tool" case previewTool = "preview-tool"
} }
@ -327,34 +333,58 @@ extension MainWindow {
init(from decoder: Decoder) throws { init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self) let container = try decoder.container(keyedBy: CodingKeys.self)
self.isLeftOptionMeta = try container.decode(forKey: .isLeftOptionMeta, default: State.default.isLeftOptionMeta) self.isLeftOptionMeta = try container.decode(
self.isRightOptionMeta = try container.decode(forKey: .isRightOptionMeta, forKey: .isLeftOptionMeta,
default: State.default.isRightOptionMeta) default: State.default.isLeftOptionMeta
self.useInteractiveZsh = try container.decode(forKey: .useInteractiveZsh, )
default: State.default.useInteractiveZsh) self.isRightOptionMeta = try container.decode(
self.trackpadScrollResistance = try container.decode(forKey: .trackpadScrollResistance, forKey: .isRightOptionMeta,
default: State.default.trackpadScrollResistance) default: State.default.isRightOptionMeta
self.useLiveResize = try container.decode(forKey: .useLiveResize, default: State.default.useLiveResize) )
self.drawsParallel = try container.decode(forKey: .drawsParallel, default: State.default.drawsParallel) 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) { if let frameRawValue = try container.decodeIfPresent(String.self, forKey: .frame) {
self.frame = NSRectFromString(frameRawValue) self.frame = NSRectFromString(frameRawValue)
} else { } else {
self.frame = CGRect(x: 100, y: 100, width: 600, height: 400) self.frame = CGRect(x: 100, y: 100, width: 600, height: 400)
} }
self.isAllToolsVisible = try container.decode(forKey: .allToolsVisible, default: State.default.isAllToolsVisible) self.isAllToolsVisible = try container.decode(
self.isToolButtonsVisible = try container.decode(forKey: .toolButtonsVisible, forKey: .allToolsVisible,
default: State.default.isToolButtonsVisible) 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.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) let missingOrderedTools = MainWindow.Tools.all.subtracting(self.orderedTools)
self.orderedTools.append(contentsOf: missingOrderedTools) self.orderedTools.append(contentsOf: missingOrderedTools)
// See [1] // See [1]
let rawActiveTools: [String: Bool] = try container.decode(forKey: .activeTools, default: [:]) 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 { guard let toolId = MainWindow.Tools(rawValue: key) else {
return nil return nil
} }
@ -364,8 +394,9 @@ extension MainWindow {
let missingActiveTools = MainWindow.Tools.all.subtracting(self.activeTools.keys) let missingActiveTools = MainWindow.Tools.all.subtracting(self.activeTools.keys)
missingActiveTools.forEach { self.activeTools[$0] = true } missingActiveTools.forEach { self.activeTools[$0] = true }
let rawTools: [String: WorkspaceToolState] = try container.decode(forKey: .workspaceTools, default: [:]) let rawTools: [String: WorkspaceToolState] = try container
self.tools = rawTools.flatMapToDict { (key, value) in .decode(forKey: .workspaceTools, default: [:])
self.tools = rawTools.flatMapToDict { key, value in
guard let tool = MainWindow.Tools(rawValue: key) else { guard let tool = MainWindow.Tools(rawValue: key) else {
return nil return nil
} }
@ -377,9 +408,15 @@ extension MainWindow {
self.tools[missingTool] = MainWindow.State.defaultTools[missingTool]! self.tools[missingTool] = MainWindow.State.defaultTools[missingTool]!
} }
self.previewTool = try container.decode(forKey: .previewTool, default: State.default.previewTool) self.previewTool = try container.decode(
self.fileBrowserShowHidden = try container.decode(forKey: .isShowHidden, forKey: .previewTool,
default: State.default.fileBrowserShowHidden) default: State.default.previewTool
)
self.fileBrowserShowHidden = try container.decode(
forKey: .isShowHidden,
default: State.default
.fileBrowserShowHidden
)
} }
func encode(to encoder: Encoder) throws { func encode(to encoder: Encoder) throws {
@ -397,10 +434,14 @@ extension MainWindow {
try container.encode(self.fileBrowserShowHidden, forKey: .isShowHidden) try container.encode(self.fileBrowserShowHidden, forKey: .isShowHidden)
// See [1] // See [1]
try container.encode(Dictionary(uniqueKeysWithValues: self.tools.map { k, v in (k.rawValue, v) }), try container.encode(
forKey: .workspaceTools) Dictionary(uniqueKeysWithValues: self.tools.map { k, v in (k.rawValue, v) }),
try container.encode(Dictionary(uniqueKeysWithValues: self.activeTools.map { k, v in (k.rawValue, v) }), forKey: .workspaceTools
forKey: .activeTools) )
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.appearance, forKey: .appearance)
try container.encode(self.orderedTools, forKey: .orderedTools) try container.encode(self.orderedTools, forKey: .orderedTools)
@ -410,14 +451,12 @@ extension MainWindow {
} }
struct WorkspaceToolState: Codable { struct WorkspaceToolState: Codable {
static let `default` = WorkspaceToolState() static let `default` = WorkspaceToolState()
enum CodingKeys: String, CodingKey { enum CodingKeys: String, CodingKey {
case location
case location = "location" case open = "is-visible"
case `open` = "is-visible" case dimension
case dimension = "dimension"
} }
var location = WorkspaceBarLocation.left var location = WorkspaceBarLocation.left
@ -443,8 +482,7 @@ struct WorkspaceToolState: Codable {
// Use generated encode(to:) // Use generated encode(to:)
private init() { private init() {}
}
init(location: WorkspaceBarLocation, dimension: CGFloat, open: Bool) { init(location: WorkspaceBarLocation, dimension: CGFloat, open: Bool) {
self.location = location self.location = location
@ -454,13 +492,10 @@ struct WorkspaceToolState: Codable {
} }
extension MarkdownTool { extension MarkdownTool {
struct State: Codable { struct State: Codable {
static let `default` = State() static let `default` = State()
enum CodingKeys: String, CodingKey { enum CodingKeys: String, CodingKey {
case forwardSearchAutomatically = "is-forward-search-automatically" case forwardSearchAutomatically = "is-forward-search-automatically"
case reverseSearchAutomatically = "is-reverse-search-automatically" case reverseSearchAutomatically = "is-reverse-search-automatically"
case refreshOnWrite = "is-refresh-on-write" case refreshOnWrite = "is-refresh-on-write"
@ -470,17 +505,25 @@ extension MarkdownTool {
var isReverseSearchAutomatically = false var isReverseSearchAutomatically = false
var isRefreshOnWrite = true var isRefreshOnWrite = true
private init() { private init() {}
}
init(from decoder: Decoder) throws { init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self) let container = try decoder.container(keyedBy: CodingKeys.self)
self.isForwardSearchAutomatically = try container.decode(forKey: .forwardSearchAutomatically, self.isForwardSearchAutomatically = try container.decode(
default: State.default.isForwardSearchAutomatically) forKey: .forwardSearchAutomatically,
self.isReverseSearchAutomatically = try container.decode(forKey: .reverseSearchAutomatically, default: State.default
default: State.default.isReverseSearchAutomatically) .isForwardSearchAutomatically
self.isRefreshOnWrite = try container.decode(forKey: .refreshOnWrite, default: State.default.isRefreshOnWrite) )
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 { func encode(to encoder: Encoder) throws {
@ -493,10 +536,9 @@ extension MarkdownTool {
} }
} }
fileprivate extension KeyedDecodingContainer where K: CodingKey { private extension KeyedDecodingContainer where K: CodingKey {
func decode<T: Decodable>(forKey key: K, default: T) throws -> T {
func decode<T: Decodable>(forKey key: K, `default`: T) throws -> T { try self.decodeIfPresent(T.self, forKey: key) ?? `default`
return try self.decodeIfPresent(T.self, forKey: key) ?? `default`
} }
} }

View File

@ -4,23 +4,23 @@
*/ */
import Cocoa import Cocoa
import NvimView
import Commons import Commons
import NvimView
func changeTheme(themePrefChanged: Bool, themeChanged: Bool, usesTheme: Bool, func changeTheme(themePrefChanged: Bool, themeChanged: Bool, usesTheme: Bool,
forTheme: () -> Void, forDefaultTheme: () -> Void) -> Bool { forTheme: () -> Void, forDefaultTheme: () -> Void) -> Bool
{
if themePrefChanged && usesTheme { if themePrefChanged, usesTheme {
forTheme() forTheme()
return true return true
} }
if themePrefChanged && !usesTheme { if themePrefChanged, !usesTheme {
forDefaultTheme() forDefaultTheme()
return true return true
} }
if !themePrefChanged && themeChanged && usesTheme { if !themePrefChanged, themeChanged, usesTheme {
forTheme() forTheme()
return true return true
} }
@ -29,7 +29,6 @@ func changeTheme(themePrefChanged: Bool, themeChanged: Bool, usesTheme: Bool,
} }
struct Theme: CustomStringConvertible { struct Theme: CustomStringConvertible {
static let `default` = Theme() static let `default` = Theme()
var foreground = NSColor.textColor var foreground = NSColor.textColor
@ -53,11 +52,11 @@ struct Theme: CustomStringConvertible {
var cssCodeBackgroundColor = NSColor(hex: "1b1f23")! var cssCodeBackgroundColor = NSColor(hex: "1b1f23")!
public var description: String { public var description: String {
return "Theme<" + "Theme<" +
"fg: \(self.foreground.hex), bg: \(self.background.hex), " + "fg: \(self.foreground.hex), bg: \(self.background.hex), " +
"hl-fg: \(self.highlightForeground.hex), hl-bg: \(self.highlightBackground.hex)" + "hl-fg: \(self.highlightForeground.hex), hl-bg: \(self.highlightBackground.hex)" +
"dir-fg: \(self.directoryForeground.hex)" + "dir-fg: \(self.directoryForeground.hex)" +
">" ">"
} }
init() {} init() {}

View File

@ -7,14 +7,12 @@ import Cocoa
import NvimView import NvimView
import PureLayout import PureLayout
protocol ThemedView: class { protocol ThemedView: AnyObject {
var theme: Theme { get } var theme: Theme { get }
var lastThemeMark: Token { get } var lastThemeMark: Token { get }
} }
class ThemedTableRow: NSTableRowView { class ThemedTableRow: NSTableRowView {
weak var triangleView: NSButton? weak var triangleView: NSButton?
var themeToken: Token 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 let cell = self.view(atColumn: 0) as? ThemedTableCell {
if cell.isDir { if cell.isDir {
cell.textField?.textColor cell.textField?.textColor
= self.themedView?.theme.directoryForeground ?? Theme.default.directoryForeground = self.themedView?.theme.directoryForeground ?? Theme.default.directoryForeground
} else { } else {
cell.textField?.textColor = self.themedView?.theme.foreground ?? Theme.default.foreground cell.textField?.textColor = self.themedView?.theme.foreground ?? Theme.default.foreground
} }
@ -51,7 +49,7 @@ class ThemedTableRow: NSTableRowView {
override func drawSelection(in dirtyRect: NSRect) { override func drawSelection(in dirtyRect: NSRect) {
if let cell = self.view(atColumn: 0) as? ThemedTableCell { if let cell = self.view(atColumn: 0) as? ThemedTableCell {
cell.textField?.textColor cell.textField?.textColor
= self.themedView?.theme.highlightForeground ?? Theme.default.highlightForeground = self.themedView?.theme.highlightForeground ?? Theme.default.highlightForeground
} }
self.themedView?.theme.highlightBackground.set() self.themedView?.theme.highlightBackground.set()
@ -60,12 +58,13 @@ class ThemedTableRow: NSTableRowView {
private weak var themedView: ThemedView? 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 { class ThemedTableCell: NSTableCellView {
// MARK: - API // MARK: - API
static let font = NSFont.systemFont(ofSize: 12) static let font = NSFont.systemFont(ofSize: 12)
static let widthWithoutText = (2 + 16 + 4 + 2).cgf static let widthWithoutText = (2 + 16 + 4 + 2).cgf
@ -166,5 +165,6 @@ class ThemedTableCell: NSTableCellView {
private let _textField = NSTextField(forAutoLayout: ()) private let _textField = NSTextField(forAutoLayout: ())
private let _imageView = NSImageView(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") }
} }

View File

@ -8,20 +8,18 @@ import PureLayout
import RxSwift import RxSwift
class ToolsPref: PrefPane, UiComponent { class ToolsPref: PrefPane, UiComponent {
typealias StateType = AppState typealias StateType = AppState
enum Action { enum Action {
case setActiveTools([MainWindow.Tools: Bool]) case setActiveTools([MainWindow.Tools: Bool])
} }
override var displayName: String { override var displayName: String {
return "Tools" "Tools"
} }
override var pinToContainer: Bool { override var pinToContainer: Bool {
return true true
} }
required init(source: Observable<StateType>, emitter: ActionEmitter, state: StateType) { required init(source: Observable<StateType>, emitter: ActionEmitter, state: StateType) {
@ -36,7 +34,7 @@ class ToolsPref: PrefPane, UiComponent {
source source
.observeOn(MainScheduler.instance) .observeOn(MainScheduler.instance)
.subscribe(onNext: { state in .subscribe(onNext: { _ in
self.updateViews() self.updateViews()
}) })
@ -53,7 +51,8 @@ class ToolsPref: PrefPane, UiComponent {
private let previewCheckbox = NSButton(forAutoLayout: ()) private let previewCheckbox = NSButton(forAutoLayout: ())
private let htmlCheckbox = NSButton(forAutoLayout: ()) private let htmlCheckbox = NSButton(forAutoLayout: ())
required init?(coder: NSCoder) { @available(*, unavailable)
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
@ -69,24 +68,32 @@ class ToolsPref: PrefPane, UiComponent {
let fileBrowser = self.fileBrowserCheckbox let fileBrowser = self.fileBrowserCheckbox
fileBrowser.target = self fileBrowser.target = self
self.configureCheckbox(button: fileBrowser, self.configureCheckbox(
title: "File Browser", button: fileBrowser,
action: #selector(ToolsPref.fileBrowserAction(_:))) title: "File Browser",
action: #selector(ToolsPref.fileBrowserAction(_:))
)
let openedFilesList = self.openedFilesListCheckbox let openedFilesList = self.openedFilesListCheckbox
openedFilesList.target = self openedFilesList.target = self
self.configureCheckbox(button: openedFilesList, self.configureCheckbox(
title: "Buffers", button: openedFilesList,
action: #selector(ToolsPref.openedFilesListAction(_:))) title: "Buffers",
action: #selector(ToolsPref.openedFilesListAction(_:))
)
let preview = self.previewCheckbox let preview = self.previewCheckbox
preview.target = self preview.target = self
self.configureCheckbox(button: preview, self.configureCheckbox(
title: "Markdown Preview", button: preview,
action: #selector(ToolsPref.previewAction(_:))) title: "Markdown Preview",
action: #selector(ToolsPref.previewAction(_:))
)
let html = self.htmlCheckbox let html = self.htmlCheckbox
html.target = self html.target = self
self.configureCheckbox(button: html, self.configureCheckbox(
title: "HTML Preview", button: html,
action: #selector(ToolsPref.htmlPreviewAction(_:))) title: "HTML Preview",
action: #selector(ToolsPref.htmlPreviewAction(_:))
)
let info = self.infoTextField( let info = self.infoTextField(
markdown: "You can turn off tools you don't need. The effect takes place when new windows are opened." 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 // MARK: - Actions
extension ToolsPref {
@IBAction func fileBrowserAction(_ sender: Any?) { extension ToolsPref {
@IBAction func fileBrowserAction(_: Any?) {
self.tools[.fileBrowser] = self.fileBrowserCheckbox.boolState self.tools[.fileBrowser] = self.fileBrowserCheckbox.boolState
self.emit(.setActiveTools(self.tools)) self.emit(.setActiveTools(self.tools))
} }
@IBAction func openedFilesListAction(_ sender: Any?) { @IBAction func openedFilesListAction(_: Any?) {
self.tools[.buffersList] = self.openedFilesListCheckbox.boolState self.tools[.buffersList] = self.openedFilesListCheckbox.boolState
self.emit(.setActiveTools(self.tools)) self.emit(.setActiveTools(self.tools))
} }
@IBAction func previewAction(_ sender: Any?) { @IBAction func previewAction(_: Any?) {
self.tools[.preview] = self.previewCheckbox.boolState self.tools[.preview] = self.previewCheckbox.boolState
self.emit(.setActiveTools(self.tools)) self.emit(.setActiveTools(self.tools))
} }
@IBAction func htmlPreviewAction(_ sender: Any?) { @IBAction func htmlPreviewAction(_: Any?) {
self.tools[.htmlPreview] = self.htmlCheckbox.boolState self.tools[.htmlPreview] = self.htmlCheckbox.boolState
self.emit(.setActiveTools(self.tools)) self.emit(.setActiveTools(self.tools))
} }

View File

@ -6,7 +6,6 @@
import Foundation import Foundation
class ToolsPrefReducer: ReducerType { class ToolsPrefReducer: ReducerType {
typealias StateType = AppState typealias StateType = AppState
typealias ActionType = ToolsPref.Action typealias ActionType = ToolsPref.Action
@ -14,10 +13,8 @@ class ToolsPrefReducer: ReducerType {
var state = pair.state var state = pair.state
switch pair.action { switch pair.action {
case let .setActiveTools(tools): case let .setActiveTools(tools):
state.mainWindowTemplate.activeTools = tools state.mainWindowTemplate.activeTools = tools
} }
return (state, pair.action, true) return (state, pair.action, true)

View File

@ -7,24 +7,21 @@ import Foundation
import RxSwift import RxSwift
struct StateActionPair<S, A> { struct StateActionPair<S, A> {
var state: S var state: S
var action: A var action: A
var modified: Bool var modified: Bool
} }
protocol UuidTagged { protocol UuidTagged {
var uuid: UUID { get } var uuid: UUID { get }
} }
class UuidAction<A>: UuidTagged, CustomStringConvertible { class UuidAction<A>: UuidTagged, CustomStringConvertible {
let uuid: UUID let uuid: UUID
let payload: A let payload: A
var description: String { 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) { init(uuid: UUID, action: A) {
@ -34,12 +31,11 @@ class UuidAction<A>: UuidTagged, CustomStringConvertible {
} }
class UuidState<S>: UuidTagged, CustomStringConvertible { class UuidState<S>: UuidTagged, CustomStringConvertible {
let uuid: UUID let uuid: UUID
let payload: S let payload: S
var description: String { 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) { init(uuid: UUID, state: S) {
@ -49,27 +45,25 @@ class UuidState<S>: UuidTagged, CustomStringConvertible {
} }
class Token: Hashable, CustomStringConvertible { class Token: Hashable, CustomStringConvertible {
func hash(into hasher: inout Hasher) { func hash(into hasher: inout Hasher) {
hasher.combine(ObjectIdentifier(self)) hasher.combine(ObjectIdentifier(self))
} }
var description: String { var description: String {
return ObjectIdentifier(self).debugDescription ObjectIdentifier(self).debugDescription
} }
static func == (left: Token, right: Token) -> Bool { static func == (left: Token, right: Token) -> Bool {
return left === right left === right
} }
} }
class Marked<T>: CustomStringConvertible { class Marked<T>: CustomStringConvertible {
let mark: Token let mark: Token
let payload: T let payload: T
var description: String { var description: String {
return "Marked<\(mark) -> \(self.payload)>" "Marked<\(mark) -> \(self.payload)>"
} }
convenience init(_ payload: T) { convenience init(_ payload: T) {
@ -82,21 +76,18 @@ class Marked<T>: CustomStringConvertible {
} }
func hasDifferentMark(as other: Marked<T>) -> Bool { func hasDifferentMark(as other: Marked<T>) -> Bool {
return self.mark != other.mark self.mark != other.mark
} }
} }
class UiComponentTemplate: UiComponent { class UiComponentTemplate: UiComponent {
typealias StateType = State typealias StateType = State
struct State { struct State {
var someField: String var someField: String
} }
enum Action { enum Action {
case doSth case doSth
} }
@ -115,7 +106,7 @@ class UiComponentTemplate: UiComponent {
source source
.observeOn(MainScheduler.instance) .observeOn(MainScheduler.instance)
.subscribe( .subscribe(
onNext: { state in onNext: { _ in
Swift.print("Hello, \(self.someField)") Swift.print("Hello, \(self.someField)")
} }
) )

View File

@ -7,11 +7,9 @@ import Cocoa
import RxSwift import RxSwift
class UiRoot: UiComponent { class UiRoot: UiComponent {
typealias StateType = AppState typealias StateType = AppState
enum Action { enum Action {
case quit case quit
} }
@ -56,11 +54,9 @@ class UiRoot: UiComponent {
guard self.mainWindows.isEmpty else { return } guard self.mainWindows.isEmpty else { return }
switch state.afterLastWindowAction { switch state.afterLastWindowAction {
case .doNothing: return case .doNothing: return
case .hide: NSApp.hide(self) case .hide: NSApp.hide(self)
case .quit: self.emit(.quit) case .quit: self.emit(.quit)
} }
}) })
.disposed(by: self.disposeBag) .disposed(by: self.disposeBag)
@ -108,9 +104,11 @@ class UiRoot: UiComponent {
.completableSubject() .completableSubject()
self.subjectForMainWindows[state.uuid] = subject self.subjectForMainWindows[state.uuid] = subject
return MainWindow(source: subject.asObservable(), return MainWindow(
emitter: self.emitter, source: subject.asObservable(),
state: state) emitter: self.emitter,
state: state
)
} }
private func removeMainWindow(with uuid: UUID) { private func removeMainWindow(with uuid: UUID) {

View File

@ -6,7 +6,6 @@
import Foundation import Foundation
class UiRootReducer: ReducerType { class UiRootReducer: ReducerType {
typealias StateType = AppState typealias StateType = AppState
typealias ActionType = UiRoot.Action typealias ActionType = UiRoot.Action
@ -16,17 +15,14 @@ class UiRootReducer: ReducerType {
var appState = tuple.state var appState = tuple.state
switch tuple.action { switch tuple.action {
case .quit: case .quit:
appState.quit = true appState.quit = true
} }
return (appState, tuple.action, true) return (appState, tuple.action, true)
} }
class MainWindowReducer: ReducerType { class MainWindowReducer: ReducerType {
typealias StateType = AppState typealias StateType = AppState
typealias ActionType = UuidAction<MainWindow.Action> typealias ActionType = UuidAction<MainWindow.Action>
@ -35,7 +31,6 @@ class UiRootReducer: ReducerType {
let uuid = tuple.action.uuid let uuid = tuple.action.uuid
switch tuple.action.payload { switch tuple.action.payload {
case let .becomeKey(isFullScreen): case let .becomeKey(isFullScreen):
appState.currentMainWindowUuid = uuid appState.currentMainWindowUuid = uuid
@ -49,7 +44,7 @@ class UiRootReducer: ReducerType {
isFullScreen: isFullScreen isFullScreen: isFullScreen
) )
case let .frameChanged(to:frame): case let .frameChanged(to: frame):
if appState.mainWindows[uuid]?.isTemporarySession == true { if appState.mainWindows[uuid]?.isTemporarySession == true {
break break
} }
@ -63,7 +58,7 @@ class UiRootReducer: ReducerType {
break break
} }
appState.mainWindowTemplate.orderedTools = tools.map { $0.0 } appState.mainWindowTemplate.orderedTools = tools.map(\.0)
case let .toggleAllTools(value): case let .toggleAllTools(value):
if appState.mainWindows[uuid]?.isTemporarySession == true { if appState.mainWindows[uuid]?.isTemporarySession == true {
@ -90,8 +85,8 @@ class UiRootReducer: ReducerType {
} }
if appState.currentMainWindowUuid == uuid, if appState.currentMainWindowUuid == uuid,
let mainWindowToClose = appState.mainWindows[uuid] { let mainWindowToClose = appState.mainWindows[uuid]
{
appState.currentMainWindowUuid = nil appState.currentMainWindowUuid = nil
appState.mainWindowTemplate = self.mainWindowTemplate( appState.mainWindowTemplate = self.mainWindowTemplate(
from: appState.mainWindowTemplate, from: appState.mainWindowTemplate,
@ -107,7 +102,6 @@ class UiRootReducer: ReducerType {
default: default:
return tuple return tuple
} }
return (appState, tuple.action, true) return (appState, tuple.action, true)
@ -115,8 +109,8 @@ class UiRootReducer: ReducerType {
private func mainWindowTemplate(from old: MainWindow.State, private func mainWindowTemplate(from old: MainWindow.State,
new: MainWindow.State, new: MainWindow.State,
isFullScreen: Bool) -> MainWindow.State { isFullScreen: Bool) -> MainWindow.State
{
var result = old var result = old
if !isFullScreen { if !isFullScreen {