2017-01-22 16:22:05 +03:00
|
|
|
/**
|
|
|
|
* Tae Won Ha - http://taewon.de - @hataewon
|
|
|
|
* See LICENSE
|
|
|
|
*/
|
2017-01-17 21:47:59 +03:00
|
|
|
|
|
|
|
import Foundation
|
|
|
|
import RxSwift
|
|
|
|
|
2017-03-01 00:04:12 +03:00
|
|
|
struct AppState: SerializableState {
|
2017-01-17 21:47:59 +03:00
|
|
|
|
2017-03-01 18:59:23 +03:00
|
|
|
static let `default` = AppState()
|
2017-01-17 21:47:59 +03:00
|
|
|
|
2017-02-27 23:17:40 +03:00
|
|
|
var openNewMainWindowOnLaunch = true
|
|
|
|
var openNewMainWindowOnReactivation = true
|
|
|
|
|
2017-02-28 13:10:04 +03:00
|
|
|
var useSnapshotUpdate = false
|
|
|
|
|
2017-02-28 00:45:26 +03:00
|
|
|
var preferencesOpen = Marked(false)
|
|
|
|
|
2017-03-01 18:59:23 +03:00
|
|
|
var mainWindowTemplate = MainWindow.State.default
|
2017-02-20 21:33:22 +03:00
|
|
|
var currentMainWindowUuid: String?
|
|
|
|
|
2017-01-22 16:22:05 +03:00
|
|
|
var mainWindows: [String: MainWindow.State] = [:]
|
2017-02-12 12:53:24 +03:00
|
|
|
var quitWhenNoMainWindow = false
|
2017-01-17 21:47:59 +03:00
|
|
|
|
2017-02-19 20:00:41 +03:00
|
|
|
let root = FileItem(URL(fileURLWithPath: "/", isDirectory: true))
|
|
|
|
|
2017-02-20 21:33:22 +03:00
|
|
|
var openQuickly = OpenQuicklyWindow.State.default
|
|
|
|
|
2017-03-01 18:59:23 +03:00
|
|
|
init() {
|
2017-02-24 00:15:35 +03:00
|
|
|
self.mainWindowTemplate.root = self.root
|
2017-02-20 21:33:22 +03:00
|
|
|
}
|
2017-03-01 00:04:12 +03:00
|
|
|
|
|
|
|
init?(dict: [String: Any]) {
|
2017-03-01 18:59:23 +03:00
|
|
|
guard let openOnLaunch = PrefUtils.bool(from: dict, for: Keys.openNewOnLaunch),
|
|
|
|
let openOnReactivation = PrefUtils.bool(from: dict, for: Keys.openNewOnReactivation),
|
|
|
|
let useSnapshot = PrefUtils.bool(from: dict, for: Keys.useSnapshotUpdateChannel)
|
|
|
|
else {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
self.openNewMainWindowOnLaunch = openOnLaunch
|
|
|
|
self.openNewMainWindowOnReactivation = openOnReactivation
|
|
|
|
self.useSnapshotUpdate = useSnapshot
|
|
|
|
|
|
|
|
let openQuicklyDict = dict[Keys.OpenQuickly.key] as? [String: Any] ?? [:]
|
|
|
|
self.openQuickly = OpenQuicklyWindow.State(dict: openQuicklyDict) ?? OpenQuicklyWindow.State.default
|
|
|
|
|
|
|
|
let mainWindowDict = dict[Keys.MainWindow.key] as? [String: Any] ?? [:]
|
|
|
|
self.mainWindowTemplate = MainWindow.State(dict: mainWindowDict) ?? MainWindow.State.default
|
2017-03-01 00:04:12 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func dict() -> [String: Any] {
|
|
|
|
return [
|
|
|
|
Keys.openNewOnLaunch: self.openNewMainWindowOnLaunch,
|
|
|
|
Keys.openNewOnReactivation: self.openNewMainWindowOnReactivation,
|
|
|
|
Keys.useSnapshotUpdateChannel: self.useSnapshotUpdate,
|
|
|
|
|
|
|
|
Keys.OpenQuickly.key: self.openQuickly.dict(),
|
|
|
|
|
|
|
|
Keys.MainWindow.key: self.mainWindowTemplate.dict(),
|
|
|
|
]
|
|
|
|
}
|
2017-02-20 21:33:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
extension OpenQuicklyWindow {
|
|
|
|
|
2017-03-01 00:04:12 +03:00
|
|
|
struct State: SerializableState {
|
2017-02-20 21:33:22 +03:00
|
|
|
|
|
|
|
static let `default` = State()
|
|
|
|
|
|
|
|
var flatFileItems = Observable<[FileItem]>.empty()
|
|
|
|
var cwd = FileUtils.userHomeUrl
|
2017-02-28 00:45:26 +03:00
|
|
|
var ignorePatterns = Set(["*/.git", "*.o", "*.d", "*.dia"].map(FileItemIgnorePattern.init))
|
2017-02-20 21:33:22 +03:00
|
|
|
var ignoreToken = Token()
|
|
|
|
|
|
|
|
var open = false
|
2017-03-01 00:04:12 +03:00
|
|
|
|
|
|
|
init() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
init?(dict: [String: Any]) {
|
|
|
|
guard let patternsString = PrefUtils.string(from: dict, for: Keys.OpenQuickly.ignorePatterns) else {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
self.ignorePatterns = FileItemIgnorePattern.from(string: patternsString)
|
|
|
|
}
|
|
|
|
|
|
|
|
func dict() -> [String: Any] {
|
|
|
|
return [
|
|
|
|
Keys.OpenQuickly.ignorePatterns: FileItemIgnorePattern.toString(self.ignorePatterns)
|
|
|
|
]
|
|
|
|
}
|
2017-01-17 21:47:59 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-06 00:39:55 +03:00
|
|
|
struct PreviewState {
|
2017-02-03 00:39:05 +03:00
|
|
|
|
2017-02-07 00:54:22 +03:00
|
|
|
static let `default` = PreviewState()
|
2017-02-06 20:36:57 +03:00
|
|
|
|
|
|
|
enum Status {
|
|
|
|
|
|
|
|
case none
|
|
|
|
case notSaved
|
|
|
|
case error
|
|
|
|
case markdown
|
|
|
|
}
|
|
|
|
|
|
|
|
var status = Status.none
|
2017-02-06 00:39:55 +03:00
|
|
|
|
|
|
|
var buffer: URL?
|
|
|
|
var html: URL?
|
|
|
|
var server: URL?
|
2017-02-07 00:54:22 +03:00
|
|
|
|
2017-02-12 13:24:15 +03:00
|
|
|
var updateDate: Date
|
2017-02-07 00:54:22 +03:00
|
|
|
|
2017-02-12 15:41:12 +03:00
|
|
|
var editorPosition = Marked(Position.beginning)
|
|
|
|
var previewPosition = Marked(Position.beginning)
|
2017-02-11 20:32:22 +03:00
|
|
|
var ignoreNextForward = false
|
2017-03-31 09:05:42 +03:00
|
|
|
var ignoreNextReverse = false
|
2017-02-12 18:40:49 +03:00
|
|
|
var forceNextReverse = false
|
2017-02-07 00:54:22 +03:00
|
|
|
|
|
|
|
init(status: Status = .none,
|
|
|
|
buffer: URL? = nil,
|
|
|
|
html: URL? = nil,
|
|
|
|
server: URL? = nil,
|
2017-02-28 00:45:26 +03:00
|
|
|
updateDate: Date = Date()) {
|
2017-02-07 00:54:22 +03:00
|
|
|
self.status = status
|
|
|
|
self.buffer = buffer
|
|
|
|
self.html = html
|
|
|
|
self.server = server
|
|
|
|
self.updateDate = updateDate
|
|
|
|
}
|
2017-02-03 00:39:05 +03:00
|
|
|
}
|
|
|
|
|
2017-03-24 19:47:08 +03:00
|
|
|
struct HtmlPreviewState {
|
|
|
|
|
|
|
|
static let `default` = HtmlPreviewState()
|
|
|
|
|
2017-04-02 21:45:12 +03:00
|
|
|
var htmlFile: URL?
|
2017-04-02 22:55:24 +03:00
|
|
|
var server: Marked<URL>?
|
2017-03-24 19:47:08 +03:00
|
|
|
}
|
|
|
|
|
2017-03-01 00:04:12 +03:00
|
|
|
struct AppearanceState: SerializableState {
|
2017-02-28 11:53:27 +03:00
|
|
|
|
|
|
|
static let `default` = AppearanceState()
|
|
|
|
|
|
|
|
var font = NSFont.userFixedPitchFont(ofSize: 13)!
|
|
|
|
var linespacing: CGFloat = 1
|
|
|
|
var usesLigatures = false
|
2017-03-01 00:04:12 +03:00
|
|
|
|
|
|
|
init() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
init?(dict: [String: Any]) {
|
|
|
|
guard let editorFontName = dict[Keys.Appearance.editorFontName] as? String,
|
|
|
|
let fEditorFontSize = PrefUtils.float(from: dict, for: Keys.Appearance.editorFontSize),
|
|
|
|
let fEditorLinespacing = PrefUtils.float(from: dict, for: Keys.Appearance.editorLinespacing),
|
|
|
|
let editorUsesLigatures = PrefUtils.bool(from: dict, for: Keys.Appearance.editorUsesLigatures)
|
|
|
|
else {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
self.font = PrefUtils.saneFont(editorFontName, fontSize: CGFloat(fEditorFontSize))
|
|
|
|
self.linespacing = CGFloat(fEditorLinespacing)
|
|
|
|
self.usesLigatures = editorUsesLigatures
|
|
|
|
}
|
|
|
|
|
|
|
|
func dict() -> [String: Any] {
|
|
|
|
return [
|
|
|
|
Keys.Appearance.editorFontName: self.font.fontName,
|
|
|
|
Keys.Appearance.editorFontSize: Float(self.font.pointSize),
|
|
|
|
Keys.Appearance.editorLinespacing: Float(self.linespacing),
|
|
|
|
Keys.Appearance.editorUsesLigatures: self.usesLigatures,
|
|
|
|
]
|
|
|
|
}
|
2017-02-28 11:53:27 +03:00
|
|
|
}
|
|
|
|
|
2017-01-17 21:47:59 +03:00
|
|
|
extension MainWindow {
|
|
|
|
|
2017-03-01 18:59:23 +03:00
|
|
|
struct State: SerializableState {
|
2017-01-17 21:47:59 +03:00
|
|
|
|
2017-02-04 17:34:13 +03:00
|
|
|
static let `default` = State(isAllToolsVisible: true, isToolButtonsVisible: true)
|
2017-01-17 21:47:59 +03:00
|
|
|
|
|
|
|
var isAllToolsVisible = true
|
|
|
|
var isToolButtonsVisible = true
|
|
|
|
|
2017-01-22 16:22:05 +03:00
|
|
|
////// transient
|
|
|
|
|
2017-02-24 00:15:35 +03:00
|
|
|
// must be replaced
|
|
|
|
var root = FileItem(URL(fileURLWithPath: "/", isDirectory: true))
|
2017-02-24 01:39:31 +03:00
|
|
|
var lastFileSystemUpdate = Marked(FileItem(URL(fileURLWithPath: "/", isDirectory: true)))
|
2017-02-24 00:15:35 +03:00
|
|
|
|
2017-03-01 00:04:12 +03:00
|
|
|
var tools = WorkspaceToolState.default
|
2017-04-10 21:38:17 +03:00
|
|
|
var orderedTools = WorkspaceToolState.orderedDefault
|
2017-02-28 17:52:48 +03:00
|
|
|
|
2017-02-06 00:39:55 +03:00
|
|
|
var preview = PreviewState.default
|
2017-04-02 21:45:12 +03:00
|
|
|
var htmlPreview = HtmlPreviewState.default
|
|
|
|
|
2017-02-07 00:54:22 +03:00
|
|
|
var previewTool = PreviewTool.State.default
|
|
|
|
|
2017-02-24 00:15:35 +03:00
|
|
|
var fileBrowserShowHidden = false
|
|
|
|
|
2017-01-22 16:22:05 +03:00
|
|
|
// neovim
|
2017-01-17 21:47:59 +03:00
|
|
|
var uuid = UUID().uuidString
|
2017-01-22 16:22:05 +03:00
|
|
|
var currentBuffer: NeoVimBuffer?
|
2017-01-17 21:47:59 +03:00
|
|
|
var buffers = [NeoVimBuffer]()
|
|
|
|
var cwd = FileUtils.userHomeUrl
|
|
|
|
|
|
|
|
var isDirty = false
|
|
|
|
|
2017-02-28 11:53:27 +03:00
|
|
|
var appearance = AppearanceState.default
|
2017-02-28 13:10:04 +03:00
|
|
|
var useInteractiveZsh = false
|
2017-01-17 21:47:59 +03:00
|
|
|
|
|
|
|
// transient^2
|
2017-03-30 01:36:57 +03:00
|
|
|
var close = false
|
2017-01-17 21:47:59 +03:00
|
|
|
|
2017-03-31 09:30:47 +03:00
|
|
|
// to be cleaned
|
|
|
|
var urlsToOpen = [URL: OpenMode]()
|
2017-03-31 20:32:01 +03:00
|
|
|
var viewToBeFocused: FocusableView? = FocusableView.neoVimView
|
2017-01-17 21:47:59 +03:00
|
|
|
|
2017-02-04 17:34:13 +03:00
|
|
|
init(isAllToolsVisible: Bool, isToolButtonsVisible: Bool) {
|
2017-01-17 21:47:59 +03:00
|
|
|
self.isAllToolsVisible = isAllToolsVisible
|
|
|
|
self.isToolButtonsVisible = isToolButtonsVisible
|
|
|
|
}
|
2017-03-01 00:04:12 +03:00
|
|
|
|
|
|
|
init?(dict: [String: Any]) {
|
|
|
|
guard let isAllToolsVisible = PrefUtils.bool(from: dict, for: Keys.MainWindow.allToolsVisible),
|
|
|
|
let isToolButtonsVisible = PrefUtils.bool(from: dict, for: Keys.MainWindow.toolButtonsVisible),
|
2017-04-10 21:38:17 +03:00
|
|
|
let orderedToolsAsString = dict[Keys.MainWindow.orderedTools] as? [String],
|
2017-03-01 00:04:12 +03:00
|
|
|
let isShowHidden = PrefUtils.bool(from: dict, for: Keys.MainWindow.isShowHidden)
|
|
|
|
else {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
self.isAllToolsVisible = isAllToolsVisible
|
|
|
|
self.isToolButtonsVisible = isToolButtonsVisible
|
|
|
|
|
|
|
|
let appearanceDict = dict[Keys.Appearance.key] as? [String: Any] ?? [:]
|
|
|
|
self.appearance = AppearanceState(dict: appearanceDict) ?? AppearanceState.default
|
|
|
|
|
2017-04-10 21:38:17 +03:00
|
|
|
self.orderedTools = orderedToolsAsString.flatMap { MainWindow.Tools(rawValue: $0) }
|
|
|
|
guard self.orderedTools.count == MainWindow.Tools.all.count
|
|
|
|
&& Set(self.orderedTools) == MainWindow.Tools.all
|
|
|
|
else {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-03-01 00:04:12 +03:00
|
|
|
let workspaceToolsDict = dict[Keys.WorkspaceTool.key] as? [String: [String: Any]] ?? [:]
|
|
|
|
let toolKeys = workspaceToolsDict.keys.flatMap { MainWindow.Tools(rawValue: $0) }
|
|
|
|
if MainWindow.Tools.all == Set(toolKeys) {
|
|
|
|
self.tools = Array(toolKeys).toDict { tool in
|
|
|
|
return WorkspaceToolState(dict: workspaceToolsDict[tool.rawValue]!) ?? WorkspaceToolState.default[tool]!
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let previewToolDict = dict[Keys.PreviewTool.key] as? [String: Any] ?? [:]
|
|
|
|
self.previewTool = PreviewTool.State(dict: previewToolDict) ?? PreviewTool.State.default
|
|
|
|
|
|
|
|
self.fileBrowserShowHidden = isShowHidden
|
|
|
|
}
|
|
|
|
|
|
|
|
func dict() -> [String: Any] {
|
|
|
|
return [
|
|
|
|
Keys.MainWindow.allToolsVisible: self.isAllToolsVisible,
|
|
|
|
Keys.MainWindow.toolButtonsVisible: self.isToolButtonsVisible,
|
|
|
|
|
|
|
|
Keys.Appearance.key: self.appearance.dict(),
|
|
|
|
Keys.WorkspaceTool.key: Array(self.tools.keys.map { $0.rawValue })
|
|
|
|
.toDict { self.tools[MainWindow.Tools(rawValue: $0)!]!.dict() },
|
|
|
|
|
2017-04-10 21:38:17 +03:00
|
|
|
Keys.MainWindow.orderedTools: self.orderedTools.map { $0.rawValue },
|
|
|
|
|
2017-03-01 00:04:12 +03:00
|
|
|
Keys.PreviewTool.key: self.previewTool.dict(),
|
|
|
|
|
|
|
|
Keys.MainWindow.isShowHidden: self.fileBrowserShowHidden,
|
|
|
|
]
|
|
|
|
}
|
2017-01-17 21:47:59 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-01 00:04:12 +03:00
|
|
|
struct WorkspaceToolState: SerializableState {
|
2017-02-28 17:52:48 +03:00
|
|
|
|
2017-03-01 00:04:12 +03:00
|
|
|
static let `default` = [
|
|
|
|
MainWindow.Tools.fileBrowser: WorkspaceToolState(location: .left, dimension: 200, open: true),
|
|
|
|
MainWindow.Tools.openedFilesList: WorkspaceToolState(location: .left, dimension: 200, open: false),
|
|
|
|
MainWindow.Tools.preview: WorkspaceToolState(location: .right, dimension: 250, open: false),
|
2017-03-24 19:47:08 +03:00
|
|
|
MainWindow.Tools.htmlPreview: WorkspaceToolState(location: .right, dimension: 500, open: true),
|
2017-03-01 00:04:12 +03:00
|
|
|
]
|
2017-02-28 17:52:48 +03:00
|
|
|
|
2017-04-10 21:38:17 +03:00
|
|
|
static let `orderedDefault` = [
|
|
|
|
MainWindow.Tools.fileBrowser,
|
|
|
|
MainWindow.Tools.openedFilesList,
|
|
|
|
MainWindow.Tools.preview,
|
|
|
|
]
|
|
|
|
|
2017-02-28 17:52:48 +03:00
|
|
|
var location = WorkspaceBarLocation.left
|
|
|
|
var dimension = CGFloat(200)
|
|
|
|
var open = false
|
2017-03-01 00:04:12 +03:00
|
|
|
|
|
|
|
init(location: WorkspaceBarLocation, dimension: CGFloat, open: Bool) {
|
|
|
|
self.location = location
|
|
|
|
self.dimension = dimension
|
|
|
|
self.open = open
|
|
|
|
}
|
|
|
|
|
|
|
|
init?(dict: [String: Any]) {
|
|
|
|
guard let locationRawValue = dict[Keys.WorkspaceTool.location] as? String,
|
|
|
|
let isOpen = PrefUtils.bool(from: dict, for: Keys.WorkspaceTool.open),
|
|
|
|
let fDimension = PrefUtils.float(from: dict, for: Keys.WorkspaceTool.dimension)
|
|
|
|
else {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
guard let location = PrefUtils.location(from: locationRawValue) else {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
self.location = location
|
|
|
|
self.dimension = CGFloat(fDimension)
|
|
|
|
self.open = isOpen
|
|
|
|
}
|
|
|
|
|
|
|
|
func dict() -> [String: Any] {
|
|
|
|
return [
|
|
|
|
Keys.WorkspaceTool.location: PrefUtils.locationAsString(for: self.location),
|
|
|
|
Keys.WorkspaceTool.open: self.open,
|
|
|
|
Keys.WorkspaceTool.dimension: Float(self.dimension),
|
|
|
|
]
|
|
|
|
}
|
2017-02-28 17:52:48 +03:00
|
|
|
}
|
|
|
|
|
2017-02-07 00:54:22 +03:00
|
|
|
extension PreviewTool {
|
|
|
|
|
2017-03-01 00:04:12 +03:00
|
|
|
struct State: SerializableState {
|
2017-02-07 00:54:22 +03:00
|
|
|
|
|
|
|
static let `default` = State()
|
|
|
|
|
|
|
|
var isForwardSearchAutomatically = false
|
|
|
|
var isReverseSearchAutomatically = false
|
|
|
|
var isRefreshOnWrite = true
|
2017-03-01 00:04:12 +03:00
|
|
|
|
|
|
|
init() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
init?(dict: [String: Any]) {
|
|
|
|
guard let isForward = PrefUtils.bool(from: dict, for: Keys.PreviewTool.forwardSearchAutomatically),
|
|
|
|
let isReverse = PrefUtils.bool(from: dict, for: Keys.PreviewTool.reverseSearchAutomatically),
|
|
|
|
let isRefreshOnWrite = PrefUtils.bool(from: dict, for: Keys.PreviewTool.refreshOnWrite)
|
|
|
|
else {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
self.isRefreshOnWrite = isRefreshOnWrite
|
|
|
|
self.isForwardSearchAutomatically = isForward
|
|
|
|
self.isReverseSearchAutomatically = isReverse
|
|
|
|
}
|
|
|
|
|
|
|
|
func dict() -> [String: Any] {
|
|
|
|
return [
|
|
|
|
Keys.PreviewTool.forwardSearchAutomatically: self.isForwardSearchAutomatically,
|
|
|
|
Keys.PreviewTool.reverseSearchAutomatically: self.isReverseSearchAutomatically,
|
|
|
|
Keys.PreviewTool.refreshOnWrite: self.isRefreshOnWrite,
|
|
|
|
]
|
|
|
|
}
|
2017-02-07 00:54:22 +03:00
|
|
|
}
|
|
|
|
}
|