1
1
mirror of https://github.com/qvacua/vimr.git synced 2024-12-25 23:02:35 +03:00

GH-296 Refactor pref data

This commit is contained in:
Tae Won Ha 2016-11-19 13:29:18 +01:00
parent f9a90ba355
commit d372ca9682
No known key found for this signature in database
GPG Key ID: E40743465B5B8B44
10 changed files with 457 additions and 225 deletions

View File

@ -72,6 +72,7 @@
4B6A709C1D6507A000E12030 /* Nimble.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4B56F29B1D29926600C1F92E /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
4B6B0A781DA2A1A500212D6D /* FileOutlineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B6B0A771DA2A1A500212D6D /* FileOutlineView.swift */; };
4B705BA11DDF7639005F844B /* ToolPrefData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B705BA01DDF7639005F844B /* ToolPrefData.swift */; };
4B705BDA1DE04F83005F844B /* Pref38To128Converter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B705BD91DE04F83005F844B /* Pref38To128Converter.swift */; };
4B854A1D1D31447C00E08DE1 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B854A1C1D31447C00E08DE1 /* main.m */; };
4B8AC0441DBCB3A2007CCC9B /* NeoVimObjectsExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B8AC0431DBCB3A1007CCC9B /* NeoVimObjectsExtensions.swift */; };
4B97E2CC1D33F53D00FC0660 /* MainWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B97E2CE1D33F53D00FC0660 /* MainWindow.xib */; };
@ -296,6 +297,7 @@
4B6A70951D6100E300E12030 /* SwiftCommons.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftCommons.swift; sourceTree = "<group>"; };
4B6B0A771DA2A1A500212D6D /* FileOutlineView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileOutlineView.swift; sourceTree = "<group>"; };
4B705BA01DDF7639005F844B /* ToolPrefData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToolPrefData.swift; sourceTree = "<group>"; };
4B705BD91DE04F83005F844B /* Pref38To128Converter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Pref38To128Converter.swift; sourceTree = "<group>"; };
4B854A1A1D31447C00E08DE1 /* NeoVimServer */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = NeoVimServer; sourceTree = BUILT_PRODUCTS_DIR; };
4B854A1C1D31447C00E08DE1 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
4B8AC0431DBCB3A1007CCC9B /* NeoVimObjectsExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NeoVimObjectsExtensions.swift; sourceTree = "<group>"; };
@ -470,6 +472,7 @@
4B238BEB1D3ED54D00CBDD98 /* AppearancePrefPane.swift */,
4BBDD3EC1D967DA2008FCC92 /* AdvancedPrefPane.swift */,
4B37ADBA1D6EC11600970D55 /* TestPane.swift */,
4B705BD91DE04F83005F844B /* Pref38To128Converter.swift */,
);
name = Preferences;
sourceTree = "<group>";
@ -1070,6 +1073,7 @@
4BAD81D51D80B5D8004F91AE /* OpenQuicklyFileViewRow.swift in Sources */,
4B4546871D468CEC00A1E27F /* PrefStore.swift in Sources */,
4B4FC8E61D3E8739009352C3 /* PrefWindowComponent.swift in Sources */,
4B705BDA1DE04F83005F844B /* Pref38To128Converter.swift in Sources */,
4B6A70961D6100E300E12030 /* SwiftCommons.swift in Sources */,
4B56CE301D5FC27100337673 /* GeneralPrefPane.swift in Sources */,
4BF8EED81D85574800CAC08A /* PrefUtils.swift in Sources */,

View File

@ -7,16 +7,44 @@ import Cocoa
import PureLayout
import RxSwift
struct AdvancedPrefData: Equatable {
let useSnapshotUpdateChannel: Bool
let useInteractiveZsh: Bool
}
struct AdvancedPrefData: Equatable, StandardPrefData {
func == (left: AdvancedPrefData, right: AdvancedPrefData) -> Bool {
fileprivate static let useSnapshotUpdateChannel = "use-snapshot-update-channel"
fileprivate static let useInteractiveZsh = "use-interactive-zsh"
static func ==(left: AdvancedPrefData, right: AdvancedPrefData) -> Bool {
return left.useSnapshotUpdateChannel == right.useSnapshotUpdateChannel &&
left.useInteractiveZsh == right.useInteractiveZsh
}
static let `default` = AdvancedPrefData(useSnapshotUpdateChannel: false, useInteractiveZsh: false)
let useSnapshotUpdateChannel: Bool
let useInteractiveZsh: Bool
init(useSnapshotUpdateChannel: Bool, useInteractiveZsh: Bool) {
self.useSnapshotUpdateChannel = useSnapshotUpdateChannel
self.useInteractiveZsh = useInteractiveZsh
}
init?(dict: [String: Any]) {
guard let useSnapshot = PrefUtils.bool(from: dict, for: AdvancedPrefData.useSnapshotUpdateChannel),
let useInteractiveZsh = PrefUtils.bool(from: dict, for: AdvancedPrefData.useInteractiveZsh)
else {
return nil
}
self.init(useSnapshotUpdateChannel: useSnapshot, useInteractiveZsh: useInteractiveZsh)
}
func dict() -> [String: Any] {
return [
AdvancedPrefData.useSnapshotUpdateChannel: self.useSnapshotUpdateChannel,
AdvancedPrefData.useInteractiveZsh: self.useInteractiveZsh,
]
}
}
class AdvancedPrefPane: PrefPane {
override var displayName: String {

View File

@ -7,18 +7,57 @@ import Cocoa
import PureLayout
import RxSwift
struct AppearancePrefData: Equatable {
let editorFont: NSFont
let editorLinespacing: CGFloat
let editorUsesLigatures: Bool
}
struct AppearancePrefData: Equatable, StandardPrefData {
func == (left: AppearancePrefData, right: AppearancePrefData) -> Bool {
fileprivate static let editorFontName = "editor-font-name"
fileprivate static let editorFontSize = "editor-font-size"
fileprivate static let editorLinespacing = "editor-linespacing"
fileprivate static let editorUsesLigatures = "editor-uses-ligatures"
static func ==(left: AppearancePrefData, right: AppearancePrefData) -> Bool {
return left.editorUsesLigatures == right.editorUsesLigatures
&& left.editorFont.isEqual(to: right.editorFont)
&& left.editorLinespacing == right.editorLinespacing
}
static let `default` = AppearancePrefData(editorFont: NeoVimView.defaultFont,
editorLinespacing: NeoVimView.defaultLinespacing,
editorUsesLigatures: false)
var editorFont: NSFont
var editorLinespacing: CGFloat
var editorUsesLigatures: Bool
init(editorFont: NSFont, editorLinespacing: CGFloat, editorUsesLigatures: Bool) {
self.editorFont = editorFont
self.editorLinespacing = editorLinespacing
self.editorUsesLigatures = editorUsesLigatures
}
init?(dict: [String: Any]) {
guard let editorFontName = dict[AppearancePrefData.editorFontName] as? String,
let fEditorFontSize = PrefUtils.float(from: dict, for: AppearancePrefData.editorFontSize),
let fEditorLinespacing = PrefUtils.float(from: dict, for: AppearancePrefData.editorLinespacing),
let editorUsesLigatures = PrefUtils.bool(from: dict, for: AppearancePrefData.editorUsesLigatures)
else {
return nil
}
self.init(editorFont: PrefUtils.saneFont(editorFontName, fontSize: CGFloat(fEditorFontSize)),
editorLinespacing: CGFloat(fEditorLinespacing),
editorUsesLigatures: editorUsesLigatures)
}
func dict() -> [String: Any] {
return [
AppearancePrefData.editorFontName: self.editorFont.fontName,
AppearancePrefData.editorFontSize: Float(self.editorFont.pointSize),
AppearancePrefData.editorLinespacing: Float(self.editorLinespacing),
AppearancePrefData.editorUsesLigatures: self.editorUsesLigatures,
]
}
}
class AppearancePrefPane: PrefPane, NSComboBoxDelegate, NSControlTextEditingDelegate {
override var displayName: String {
@ -264,12 +303,12 @@ extension AppearancePrefPane {
fileprivate func cappedLinespacing(_ linespacing: Float) -> CGFloat {
let cgfLinespacing = CGFloat(linespacing)
guard cgfLinespacing >= PrefStore.minEditorLinespacing else {
return PrefStore.defaultEditorLinespacing
guard cgfLinespacing >= NeoVimView.minLinespacing else {
return NeoVimView.defaultLinespacing
}
guard cgfLinespacing <= PrefStore.maxEditorLinespacing else {
return PrefStore.maxEditorLinespacing
guard cgfLinespacing <= NeoVimView.maxLinespacing else {
return NeoVimView.maxLinespacing
}
return cgfLinespacing
@ -278,12 +317,12 @@ extension AppearancePrefPane {
fileprivate func cappedFontSize(_ size: Int) -> CGFloat {
let cgfSize = CGFloat(size)
guard cgfSize >= PrefStore.minEditorFontSize else {
return PrefStore.defaultEditorFontSize
guard cgfSize >= NeoVimView.minFontSize else {
return NeoVimView.defaultFont.pointSize
}
guard cgfSize <= PrefStore.maxEditorFontSize else {
return PrefStore.maxEditorFontSize
guard cgfSize <= NeoVimView.maxFontSize else {
return NeoVimView.maxFontSize
}
return cgfSize

View File

@ -7,19 +7,61 @@ import Cocoa
import PureLayout
import RxSwift
struct GeneralPrefData: Equatable {
let openNewWindowWhenLaunching: Bool
let openNewWindowOnReactivation: Bool
struct GeneralPrefData: Equatable, StandardPrefData {
let ignorePatterns: Set<FileItemIgnorePattern>
}
fileprivate static let openNewWindowWhenLaunching = "open-new-window-when-launching"
fileprivate static let openNewWindowOnReactivation = "open-new-window-on-reactivation"
fileprivate static let ignorePatterns = "ignore-patterns"
func == (left: GeneralPrefData, right: GeneralPrefData) -> Bool {
fileprivate static let defaultIgnorePatterns = Set(
[ "*/.git", "*.o", "*.d", "*.dia" ].map(FileItemIgnorePattern.init)
)
static func ==(left: GeneralPrefData, right: GeneralPrefData) -> Bool {
return left.openNewWindowWhenLaunching == right.openNewWindowWhenLaunching
&& left.openNewWindowOnReactivation == right.openNewWindowOnReactivation
&& left.ignorePatterns == right.ignorePatterns
}
static let `default` = GeneralPrefData(openNewWindowWhenLaunching: true,
openNewWindowOnReactivation: true,
ignorePatterns: GeneralPrefData.defaultIgnorePatterns)
var openNewWindowWhenLaunching: Bool
var openNewWindowOnReactivation: Bool
var ignorePatterns: Set<FileItemIgnorePattern>
init(openNewWindowWhenLaunching: Bool,
openNewWindowOnReactivation: Bool,
ignorePatterns: Set<FileItemIgnorePattern>)
{
self.openNewWindowWhenLaunching = openNewWindowWhenLaunching
self.openNewWindowOnReactivation = openNewWindowOnReactivation
self.ignorePatterns = ignorePatterns
}
init?(dict: [String: Any]) {
guard let openNewWinWhenLaunching = PrefUtils.bool(from: dict, for: GeneralPrefData.openNewWindowWhenLaunching),
let openNewWinOnReactivation = PrefUtils.bool(from: dict, for: GeneralPrefData.openNewWindowOnReactivation),
let ignorePatternsStr = dict[GeneralPrefData.ignorePatterns] as? String
else {
return nil
}
self.init(openNewWindowWhenLaunching: openNewWinWhenLaunching,
openNewWindowOnReactivation: openNewWinOnReactivation,
ignorePatterns: PrefUtils.ignorePatterns(fromString: ignorePatternsStr))
}
func dict() -> [String: Any] {
return [
GeneralPrefData.openNewWindowWhenLaunching: self.openNewWindowWhenLaunching,
GeneralPrefData.openNewWindowOnReactivation: self.openNewWindowOnReactivation,
GeneralPrefData.ignorePatterns: PrefUtils.ignorePatternString(fromSet: self.ignorePatterns),
]
}
}
class GeneralPrefPane: PrefPane, NSTextFieldDelegate {
override var displayName: String {

View File

@ -14,19 +14,62 @@ enum MainWindowAction {
case close(mainWindow: MainWindowComponent, mainWindowPrefData: MainWindowPrefData)
}
struct MainWindowPrefData {
struct MainWindowPrefData: StandardPrefData {
let isAllToolsVisible: Bool
let isToolButtonsVisible: Bool
fileprivate static let isAllToolsVisible = "is-all-tools-visible"
fileprivate static let isToolButtonsVisible = "is-tool-buttons-visible"
fileprivate static let toolPrefDatas = "tool-pref-datas"
// FIXME: REMOVE!
let isFileBrowserVisible: Bool
let fileBrowserWidth: Float
static let `default` = MainWindowPrefData(isAllToolsVisible: true,
isToolButtonsVisible: true,
toolPrefDatas: [ ToolPrefData.defaults[.fileBrowser]! ])
var isAllToolsVisible: Bool
var isToolButtonsVisible: Bool
var toolPrefDatas: [ToolPrefData]
init(isAllToolsVisible: Bool, isToolButtonsVisible: Bool, toolPrefDatas: [ToolPrefData]) {
self.isAllToolsVisible = isAllToolsVisible
self.isToolButtonsVisible = isToolButtonsVisible
self.toolPrefDatas = toolPrefDatas
}
enum ToolIdentifier: String {
init?(dict: [String: Any]) {
case fileBrowser = "com.qvacua.vimr.tool.file-browser"
guard let isAllToolsVisible = PrefUtils.bool(from: dict, for: MainWindowPrefData.isAllToolsVisible),
let isToolButtonsVisible = PrefUtils.bool(from: dict, for: MainWindowPrefData.isToolButtonsVisible),
let toolDataDicts = dict[MainWindowPrefData.toolPrefDatas] as? [[String: Any]]
else {
return nil
}
// Add default tool pref data for missing identifiers.
let toolDatas = toolDataDicts.map { ToolPrefData(dict: $0) }.flatMap { $0 }
let missingToolDatas = Set(ToolIdentifier.all)
.subtracting(toolDatas.map { $0.identifier })
.map { ToolPrefData.defaults[$0] }
.flatMap { $0 }
self.init(isAllToolsVisible: isAllToolsVisible,
isToolButtonsVisible: isToolButtonsVisible,
toolPrefDatas: [ toolDatas, missingToolDatas ].flatMap { $0 })
}
func dict() -> [String: Any] {
return [
MainWindowPrefData.isAllToolsVisible: self.isAllToolsVisible,
MainWindowPrefData.isToolButtonsVisible: self.isToolButtonsVisible,
MainWindowPrefData.toolPrefDatas: self.toolPrefDatas.map { $0.dict() },
]
}
func toolPrefData(for identifier: ToolIdentifier) -> ToolPrefData {
guard let tool = self.toolPrefDatas.filter({ $0.identifier == identifier }).first else {
preconditionFailure("[ERROR] No tool for \(identifier) found!")
}
return tool
}
}
class MainWindowComponent: WindowComponent, NSWindowDelegate, NSUserInterfaceValidations, WorkspaceDelegate {
@ -103,7 +146,6 @@ class MainWindowComponent: WindowComponent, NSWindowDelegate, NSUserInterfaceVal
toolIdentifier: .fileBrowser,
minimumDimension: 100)
self.tools[.fileBrowser] = fileBrowserTool
self.workspace.append(tool: fileBrowserTool, location: .left)
self.addReactions()
@ -120,7 +162,9 @@ class MainWindowComponent: WindowComponent, NSWindowDelegate, NSUserInterfaceVal
// By default the tool buttons are shown and no tools are shown.
let mainWindowData = initialData.mainWindow
fileBrowserTool.dimension = CGFloat(mainWindowData.fileBrowserWidth)
let fileBrowserToolData = mainWindowData.toolPrefData(for: .fileBrowser)
self.workspace.append(tool: fileBrowserTool, location: fileBrowserToolData.location)
fileBrowserTool.dimension = CGFloat(fileBrowserToolData.dimension)
if !mainWindowData.isAllToolsVisible {
self.toggleAllTools(self)
@ -130,7 +174,7 @@ class MainWindowComponent: WindowComponent, NSWindowDelegate, NSUserInterfaceVal
self.toggleToolButtons(self)
}
if mainWindowData.isFileBrowserVisible {
if fileBrowserToolData.isVisible {
fileBrowserTool.toggle()
}
@ -360,13 +404,13 @@ extension MainWindowComponent {
@IBAction func makeFontBigger(_ sender: Any?) {
let curFont = self.neoVimView.font
let font = self.fontManager.convert(curFont, toSize: min(curFont.pointSize + 1, PrefStore.maxEditorFontSize))
let font = self.fontManager.convert(curFont, toSize: min(curFont.pointSize + 1, NeoVimView.maxFontSize))
self.neoVimView.font = font
}
@IBAction func makeFontSmaller(_ sender: Any?) {
let curFont = self.neoVimView.font
let font = self.fontManager.convert(curFont, toSize: max(curFont.pointSize - 1, PrefStore.minEditorFontSize))
let font = self.fontManager.convert(curFont, toSize: max(curFont.pointSize - 1, NeoVimView.minFontSize))
self.neoVimView.font = font
}
}
@ -407,10 +451,13 @@ extension MainWindowComponent {
self.fileItemService.unmonitor(url: self._cwd)
let fileBrowser = self.tools[.fileBrowser]!
let fileBrowserData = ToolPrefData(identifier: .fileBrowser,
location: fileBrowser.location,
isVisible: fileBrowser.isSelected,
dimension: fileBrowser.dimension)
let prefData = MainWindowPrefData(isAllToolsVisible: self.workspace.isAllToolsVisible,
isToolButtonsVisible: self.workspace.isToolButtonsVisible,
isFileBrowserVisible: self.workspace.bars[.left]?.isOpen ?? true,
fileBrowserWidth: Float(fileBrowser.dimension))
toolPrefDatas: [ fileBrowserData ])
self.publish(event: MainWindowAction.close(mainWindow: self, mainWindowPrefData: prefData))
}

View File

@ -0,0 +1,88 @@
/**
* Tae Won Ha - http://taewon.de - @hataewon
* See LICENSE
*/
import Foundation
fileprivate class PrefKeys38 {
static let openNewWindowWhenLaunching = "open-new-window-when-launching"
static let openNewWindowOnReactivation = "open-new-window-on-reactivation"
static let openQuicklyIgnorePatterns = "open-quickly-ignore-patterns"
static let editorFontName = "editor-font-name"
static let editorFontSize = "editor-font-size"
static let editorLinespacing = "editor-linespacing"
static let editorUsesLigatures = "editor-uses-ligatures"
static let useSnapshotUpdateChannel = "use-snapshot-update-channel"
static let useInteractiveZsh = "use-interactive-zsh"
static let isAllToolsVisible = "is-all-tools-visible"
static let isToolButtonsShown = "is-tool-buttons-visible"
static let isFileBrowserOpen = "is-file-browser-visible"
static let fileBrowserWidth = "file-browser-width"
}
class Pref38To128Converter {
static let fromVersion = "38"
static let toVersion = "128"
fileprivate let userDefaults = UserDefaults.standard
fileprivate let fontManager = NSFontManager.shared()
func prefData128(from dict38: [String: Any]) -> PrefData {
let editorFontName = dict38[PrefKeys38.editorFontName] as? String ?? NeoVimView.defaultFont.fontName
let editorFontSize = CGFloat(
PrefUtils.float(from: dict38, for: PrefKeys38.editorFontSize) ?? Float(NeoVimView.defaultFont.pointSize)
)
let editorFont = PrefUtils.saneFont(editorFontName, fontSize: editorFontSize)
let usesLigatures = PrefUtils.bool(from: dict38, for: PrefKeys38.editorUsesLigatures, default: false)
let linespacingAsStr = PrefUtils.value(from: dict38, for: PrefKeys38.editorLinespacing, default: "1")
let linespacing = PrefUtils.saneLinespacing(Float(linespacingAsStr) ?? 1)
let openNewWindowWhenLaunching = PrefUtils.bool(from: dict38,
for: PrefKeys38.openNewWindowWhenLaunching,
default: true)
let openNewWindowOnReactivation = PrefUtils.bool(from: dict38,
for: PrefKeys38.openNewWindowOnReactivation,
default: true)
let ignorePatternsList = (dict38[PrefKeys38.openQuicklyIgnorePatterns] as? String) ?? "*/.git, *.o, *.d, *.dia"
let ignorePatterns = PrefUtils.ignorePatterns(fromString: ignorePatternsList)
let useSnapshotUpdate = PrefUtils.bool(from: dict38, for: PrefKeys38.useSnapshotUpdateChannel, default: false)
let useInteractiveZsh = PrefUtils.bool(from: dict38, for: PrefKeys38.useInteractiveZsh, default: false)
let isAllToolsVisible = PrefUtils.bool(from: dict38, for: PrefKeys38.isAllToolsVisible, default: true)
let isToolButtonsVisible = PrefUtils.bool(from: dict38, for: PrefKeys38.isToolButtonsShown, default: true)
let isFileBrowserVisible = PrefUtils.bool(from: dict38, for: PrefKeys38.isFileBrowserOpen, default: true)
let fileBrowserWidth = PrefUtils.float(from: dict38, for: PrefKeys38.fileBrowserWidth, default: 200)
let fileBrowserData = ToolPrefData(identifier: .fileBrowser,
location: .left,
isVisible: isFileBrowserVisible,
dimension: CGFloat(fileBrowserWidth))
return PrefData(
general: GeneralPrefData(
openNewWindowWhenLaunching: openNewWindowWhenLaunching,
openNewWindowOnReactivation: openNewWindowOnReactivation,
ignorePatterns: ignorePatterns
),
appearance: AppearancePrefData(editorFont: editorFont,
editorLinespacing: linespacing,
editorUsesLigatures: usesLigatures),
advanced: AdvancedPrefData(useSnapshotUpdateChannel: useSnapshotUpdate,
useInteractiveZsh: useInteractiveZsh),
mainWindow: MainWindowPrefData(isAllToolsVisible: isAllToolsVisible,
isToolButtonsVisible: isToolButtonsVisible,
toolPrefDatas: [ fileBrowserData ])
)
}
}

View File

@ -6,181 +6,96 @@
import Cocoa
import RxSwift
struct PrefData {
protocol StandardPrefData {
init?(dict: [String: Any])
func dict() -> [String: Any]
}
struct PrefData: StandardPrefData {
fileprivate static let general = "general"
fileprivate static let appearance = "appearance"
fileprivate static let advanced = "advanced"
fileprivate static let mainWindow = "mainWindow"
static let `default` = PrefData(general: .default, appearance: .default, advanced: .default, mainWindow: .default)
var general: GeneralPrefData
var appearance: AppearancePrefData
var advanced: AdvancedPrefData
var mainWindow: MainWindowPrefData
init(general: GeneralPrefData,
appearance: AppearancePrefData,
advanced: AdvancedPrefData,
mainWindow: MainWindowPrefData)
{
self.general = general
self.appearance = appearance
self.advanced = advanced
self.mainWindow = mainWindow
}
private class PrefKeys {
static let openNewWindowWhenLaunching = "open-new-window-when-launching"
static let openNewWindowOnReactivation = "open-new-window-on-reactivation"
static let openQuicklyIgnorePatterns = "open-quickly-ignore-patterns"
static let editorFontName = "editor-font-name"
static let editorFontSize = "editor-font-size"
static let editorLinespacing = "editor-linespacing"
static let editorUsesLigatures = "editor-uses-ligatures"
static let useSnapshotUpdateChannel = "use-snapshot-update-channel"
static let useInteractiveZsh = "use-interactive-zsh"
static let isAllToolsVisible = "is-all-tools-visible"
static let isToolButtonsShown = "is-tool-buttons-visible"
static let isFileBrowserOpen = "is-file-browser-visible"
static let fileBrowserWidth = "file-browser-width"
init?(dict: [String: Any]) {
guard let generalDict: [String: Any] = PrefUtils.value(from: dict, for: PrefData.general),
let appearanceDict: [String: Any] = PrefUtils.value(from: dict, for: PrefData.appearance),
let advancedDict: [String: Any] = PrefUtils.value(from: dict, for: PrefData.advanced),
let mainWindowDict: [String: Any] = PrefUtils.value(from: dict, for: PrefData.mainWindow)
else {
return nil
}
guard let general = GeneralPrefData(dict: generalDict),
let appearance = AppearancePrefData(dict: appearanceDict),
let advanced = AdvancedPrefData(dict: advancedDict),
let mainWindow = MainWindowPrefData(dict: mainWindowDict)
else {
return nil
}
self.init(general: general, appearance: appearance, advanced: advanced, mainWindow: mainWindow)
}
func dict() -> [String: Any] {
return [
PrefData.general: self.general.dict(),
PrefData.appearance: self.appearance.dict(),
PrefData.advanced: self.advanced.dict(),
PrefData.mainWindow: self.mainWindow.dict(),
]
}
}
// TODO: We should generalize the persisting of pref data.
/**
To reset prefs
To reset prefs for 38
$ defaults write com.qvacua.vimr 38 -dict editor-font-name InputMonoCompressed-Regular editor-font-size 13 editor-uses-ligatures 0 open-new-window-on-reactivation 1 open-new-window-when-launching 1
$ defaults read ~/Library/Preferences/com.qvacua.VimR
*/
class PrefStore: StandardFlow {
fileprivate static let compatibleVersion = "38"
fileprivate static let defaultEditorFont = NeoVimView.defaultFont
static let minEditorFontSize = NeoVimView.minFontSize
static let maxEditorFontSize = NeoVimView.maxFontSize
static let defaultEditorFontSize = NeoVimView.defaultFont.pointSize
static let defaultEditorLinespacing = NeoVimView.defaultLinespacing
static let minEditorLinespacing = NeoVimView.minLinespacing
static let maxEditorLinespacing = NeoVimView.maxLinespacing
fileprivate static let compatibleVersion = "128"
fileprivate static let lastCompatibleVersion = "38"
fileprivate let userDefaults = UserDefaults.standard
fileprivate let fontManager = NSFontManager.shared()
var data = PrefData(
general: GeneralPrefData(openNewWindowWhenLaunching: true,
openNewWindowOnReactivation: true,
ignorePatterns: Set([ "*/.git", "*.o", "*.d", "*.dia" ].map(FileItemIgnorePattern.init))),
appearance: AppearancePrefData(editorFont: PrefStore.defaultEditorFont,
editorLinespacing: 1,
editorUsesLigatures: false),
advanced: AdvancedPrefData(useSnapshotUpdateChannel: false,
useInteractiveZsh: false),
mainWindow: MainWindowPrefData(isAllToolsVisible: true,
isToolButtonsVisible: true,
isFileBrowserVisible: true,
fileBrowserWidth: 200)
)
var data = PrefData.default
override init(source: Observable<Any>) {
super.init(source: source)
if let prefs = self.userDefaults.dictionary(forKey: PrefStore.compatibleVersion) {
self.data = self.prefDataFromDict(prefs)
} else {
self.userDefaults.setValue(self.prefsDict(self.data), forKey: PrefStore.compatibleVersion)
}
// 128
self.data = PrefData(dict: prefs) ?? .default
} else if let dictLastVersion = self.userDefaults.dictionary(forKey: PrefStore.lastCompatibleVersion) {
// 38
self.data = Pref38To128Converter().prefData128(from: dictLastVersion)
}
fileprivate func prefDataFromDict(_ prefs: [String: Any]) -> PrefData {
let editorFontName = prefs[PrefKeys.editorFontName] as? String ?? PrefStore.defaultEditorFont.fontName
let editorFontSize = CGFloat(
(prefs[PrefKeys.editorFontSize] as? NSNumber)?.floatValue ?? Float(PrefStore.defaultEditorFont.pointSize)
)
let editorFont = self.saneFont(editorFontName, fontSize: editorFontSize)
let usesLigatures = self.bool(from: prefs, for: PrefKeys.editorUsesLigatures, default: false)
let linespacing = self.saneLinespacing(Float((prefs[PrefKeys.editorLinespacing] as? String) ?? "1") ?? 1)
let openNewWindowWhenLaunching = self.bool(from: prefs, for: PrefKeys.openNewWindowWhenLaunching, default: true)
let openNewWindowOnReactivation = self.bool(from: prefs, for: PrefKeys.openNewWindowOnReactivation, default: true)
let ignorePatternsList = (prefs[PrefKeys.openQuicklyIgnorePatterns] as? String) ?? "*/.git, *.o, *.d, *.dia"
let ignorePatterns = PrefUtils.ignorePatterns(fromString: ignorePatternsList)
let useSnapshotUpdate = self.bool(from: prefs, for: PrefKeys.useSnapshotUpdateChannel, default: false)
let useInteractiveZsh = self.bool(from: prefs, for: PrefKeys.useInteractiveZsh, default: false)
let isAllToolsVisible = self.bool(from: prefs, for: PrefKeys.isAllToolsVisible, default: true)
let isToolButtonsVisible = self.bool(from: prefs, for: PrefKeys.isToolButtonsShown, default: true)
let isFileBrowserVisible = self.bool(from: prefs, for: PrefKeys.isFileBrowserOpen, default: true)
let fileBrowserWidth = (prefs[PrefKeys.fileBrowserWidth] as? NSNumber)?.floatValue ?? Float(200)
return PrefData(
general: GeneralPrefData(
openNewWindowWhenLaunching: openNewWindowWhenLaunching,
openNewWindowOnReactivation: openNewWindowOnReactivation,
ignorePatterns: ignorePatterns
),
appearance: AppearancePrefData(editorFont: editorFont,
editorLinespacing: linespacing,
editorUsesLigatures: usesLigatures),
advanced: AdvancedPrefData(useSnapshotUpdateChannel: useSnapshotUpdate,
useInteractiveZsh: useInteractiveZsh),
mainWindow: MainWindowPrefData(isAllToolsVisible: isAllToolsVisible,
isToolButtonsVisible: isToolButtonsVisible,
isFileBrowserVisible: isFileBrowserVisible,
fileBrowserWidth: fileBrowserWidth)
)
}
fileprivate func bool(from prefs: [String: Any], for key: String, default defaultValue: Bool) -> Bool {
return (prefs[key] as? NSNumber)?.boolValue ?? defaultValue
}
fileprivate func saneFont(_ fontName: String, fontSize: CGFloat) -> NSFont {
var editorFont = NSFont(name: fontName, size: fontSize) ?? PrefStore.defaultEditorFont
if !editorFont.isFixedPitch {
editorFont = fontManager.convert(PrefStore.defaultEditorFont, toSize: editorFont.pointSize)
}
if editorFont.pointSize < PrefStore.minEditorFontSize || editorFont.pointSize > PrefStore.maxEditorFontSize {
editorFont = fontManager.convert(editorFont, toSize: PrefStore.defaultEditorFont.pointSize)
}
return editorFont
}
fileprivate func saneLinespacing(_ fLinespacing: Float) -> CGFloat {
let linespacing = CGFloat(fLinespacing)
guard linespacing >= PrefStore.minEditorLinespacing && linespacing <= PrefStore.maxEditorLinespacing else {
return PrefStore.defaultEditorLinespacing
}
return linespacing
}
fileprivate func prefsDict(_ prefData: PrefData) -> [String: Any] {
let generalData = prefData.general
let appearanceData = prefData.appearance
let advancedData = prefData.advanced
let mainWindowData = prefData.mainWindow
let ignorePatterns = PrefUtils.ignorePatternString(fromSet: generalData.ignorePatterns) as Any
let prefs: [String: Any] = [
// General
PrefKeys.openNewWindowWhenLaunching: generalData.openNewWindowWhenLaunching as Any,
PrefKeys.openNewWindowOnReactivation: generalData.openNewWindowOnReactivation as Any,
PrefKeys.openQuicklyIgnorePatterns: ignorePatterns,
// Appearance
PrefKeys.editorFontName: appearanceData.editorFont.fontName as Any,
PrefKeys.editorFontSize: appearanceData.editorFont.pointSize as Any,
PrefKeys.editorLinespacing: String(format: "%.2f", appearanceData.editorLinespacing) as Any,
PrefKeys.editorUsesLigatures: appearanceData.editorUsesLigatures as Any,
// Advanced
PrefKeys.useSnapshotUpdateChannel: advancedData.useSnapshotUpdateChannel as Any,
PrefKeys.useInteractiveZsh: advancedData.useInteractiveZsh as Any,
// MainWindow
PrefKeys.isAllToolsVisible: mainWindowData.isAllToolsVisible,
PrefKeys.isToolButtonsShown: mainWindowData.isToolButtonsVisible,
PrefKeys.isFileBrowserOpen: mainWindowData.isFileBrowserVisible,
PrefKeys.fileBrowserWidth: mainWindowData.fileBrowserWidth
]
return prefs
// We write self.data here for when PrefData(dict: prefs) was nil or the prefs last compatible version was
// converted.
self.userDefaults.setValue(self.data.dict(), forKey: PrefStore.compatibleVersion)
}
override func subscription(source: Observable<Any>) -> Disposable {
@ -198,7 +113,7 @@ class PrefStore: StandardFlow {
return
}
self.userDefaults.setValue(self.prefsDict(self.data), forKey: PrefStore.compatibleVersion)
self.userDefaults.setValue(self.data.dict(), forKey: PrefStore.compatibleVersion)
self.publish(event: self.data)
})
}

View File

@ -34,4 +34,57 @@ class PrefUtils {
.sorted()
.joined(separator: ", ")
}
static func value<T>(from dict: [String: Any], for key: String) -> T? {
return dict[key] as? T
}
static func value<T>(from dict: [String: Any], for key: String, default defaultValue: T) -> T {
return dict[key] as? T ?? defaultValue
}
static func float(from dict: [String: Any], for key: String, default defaultValue: Float) -> Float {
return (dict[key] as? NSNumber)?.floatValue ?? defaultValue
}
static func float(from dict: [String: Any], for key: String) -> Float? {
guard let number = dict[key] as? NSNumber else {
return nil
}
return number.floatValue
}
static func bool(from dict: [String: Any], for key: String) -> Bool? {
guard let number = dict[key] as? NSNumber else {
return nil
}
return number.boolValue
}
static func bool(from dict: [String: Any], for key: String, default defaultValue: Bool) -> Bool {
return (dict[key] as? NSNumber)?.boolValue ?? defaultValue
}
static func saneFont(_ fontName: String, fontSize: CGFloat) -> NSFont {
var editorFont = NSFont(name: fontName, size: fontSize) ?? NeoVimView.defaultFont
if !editorFont.isFixedPitch {
editorFont = NSFontManager.shared().convert(NeoVimView.defaultFont, toSize: editorFont.pointSize)
}
if editorFont.pointSize < NeoVimView.minFontSize || editorFont.pointSize > NeoVimView.maxFontSize {
editorFont = NSFontManager.shared().convert(editorFont, toSize: NeoVimView.defaultFont.pointSize)
}
return editorFont
}
static func saneLinespacing(_ fLinespacing: Float) -> CGFloat {
let linespacing = CGFloat(fLinespacing)
guard linespacing >= NeoVimView.minLinespacing && linespacing <= NeoVimView.maxLinespacing else {
return NeoVimView.defaultLinespacing
}
return linespacing
}
}

View File

@ -5,44 +5,60 @@
import Foundation
protocol StandardPrefData {
enum ToolIdentifier: String {
init?(dict: [String: Any])
func dict() -> [String: Any]
case fileBrowser = "com.qvacua.vimr.tool.file-browser"
static let all = [ fileBrowser ]
}
struct ToolPrefData: StandardPrefData {
fileprivate static let identifier = "identifier"
fileprivate static let isVisible = "isVisible"
fileprivate static let location = "location"
fileprivate static let isVisible = "is-visible"
fileprivate static let dimension = "dimension"
let identifier: ToolIdentifier
let isVisible: Bool
let dimension: Float
static let defaults: [ToolIdentifier: ToolPrefData] = [
.fileBrowser: ToolPrefData(identifier: .fileBrowser, location: .left, isVisible: true, dimension: 200),
]
init(identifier: ToolIdentifier, isVisible: Bool, dimension: Float) {
var identifier: ToolIdentifier
var location: WorkspaceBarLocation
var isVisible: Bool
var dimension: CGFloat
init(identifier: ToolIdentifier, location: WorkspaceBarLocation, isVisible: Bool, dimension: CGFloat) {
self.identifier = identifier
self.location = location
self.isVisible = isVisible
self.dimension = dimension
}
func dict() -> [String: Any] {
return [
ToolPrefData.identifier: self.identifier,
ToolPrefData.identifier: self.identifier.rawValue,
ToolPrefData.location: self.location.rawValue,
ToolPrefData.isVisible: self.isVisible,
ToolPrefData.dimension: self.dimension,
ToolPrefData.dimension: Float(self.dimension),
]
}
init?(dict: [String: Any]) {
guard let identifier = dict[ToolPrefData.identifier] as? ToolIdentifier,
let isVisible = dict[ToolPrefData.isVisible] as? Bool,
let dimension = dict[ToolPrefData.dimension] as? Float
guard let identifierRawValue = dict[ToolPrefData.identifier] as? String,
let locationRawValue = dict[ToolPrefData.location] as? String,
let isVisible = PrefUtils.bool(from: dict, for: ToolPrefData.isVisible),
let fDimension = PrefUtils.float(from: dict, for: ToolPrefData.dimension)
else {
return nil
}
self.init(identifier: identifier, isVisible: isVisible, dimension: dimension)
guard let identifier = ToolIdentifier(rawValue: identifierRawValue),
let location = WorkspaceBarLocation(rawValue: locationRawValue)
else {
return nil
}
self.init(identifier: identifier, location: location, isVisible: isVisible, dimension: CGFloat(fDimension))
}
}

View File

@ -6,7 +6,7 @@
import Cocoa
import PureLayout
enum WorkspaceBarLocation {
enum WorkspaceBarLocation: String {
case top
case right
case bottom