mirror of
https://github.com/qvacua/vimr.git
synced 2024-11-28 02:54:31 +03:00
Experimenting further...
This commit is contained in:
parent
7a0610dee2
commit
ea569658e3
@ -17,14 +17,15 @@
|
||||
1929B3F5743967125F357C9F /* Matcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BEEB33113B0E33C3830F /* Matcher.swift */; };
|
||||
1929B4145AA81F006BAF3B5C /* PreviewService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BB8BCA48637156F92945 /* PreviewService.swift */; };
|
||||
1929B462CD4935AFF6D69457 /* FileItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B7CB4863F80230C32D3C /* FileItem.swift */; };
|
||||
1929B4B70926DE113E6BF990 /* PreviewTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BE37AA2843779CAFA76F /* PreviewTransformer.swift */; };
|
||||
1929B4FEE6EB56EF3F56B805 /* Context.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B34FC23D805A8B29E8F7 /* Context.swift */; };
|
||||
1929B53876E6952D378C2B30 /* ScoredFileItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BDF9EBAF1D9D44399045 /* ScoredFileItem.swift */; };
|
||||
1929B5CF6ECCBCC3FB5292CE /* HttpServerService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B0D767ED19EE1281ECD9 /* HttpServerService.swift */; };
|
||||
1929B6388EAF16C190B82955 /* FileItemIgnorePattern.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B69499B2569793350CEC /* FileItemIgnorePattern.swift */; };
|
||||
1929B67DA3EB21A631EF1DBB /* FileUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BA8AC40B901B20F20B71 /* FileUtils.swift */; };
|
||||
1929B728262BAA14FC93F6AC /* NeoVimView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BF00B466B40629C2AABE /* NeoVimView.swift */; };
|
||||
1929B73E5EC0B108B83F82EB /* FileItemService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B3A98687DF171307AAC8 /* FileItemService.swift */; };
|
||||
1929B7A2F2B423AA9740FD45 /* FileUtilsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B5D977261F1EBFA9E8F1 /* FileUtilsTest.swift */; };
|
||||
1929B8FB248D71BF88A35761 /* PreviewTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B6C6C7792B05164B0216 /* PreviewTool.swift */; };
|
||||
1929B93DBAD09835E428F610 /* PrefPane.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BB251F74BEFC82CEEF84 /* PrefPane.swift */; };
|
||||
1929BA120290D6A2A61A4468 /* ArrayCommonsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B477E1E62433BC48E10B /* ArrayCommonsTest.swift */; };
|
||||
1929BA3BB94B77E9AE051FE5 /* PreviewComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B8DA5AA33536F0082200 /* PreviewComponent.swift */; };
|
||||
@ -289,7 +290,6 @@
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
1929B02440BC99C42F9EBD45 /* NetUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NetUtils.m; sourceTree = "<group>"; };
|
||||
1929B0D767ED19EE1281ECD9 /* HttpServerService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpServerService.swift; sourceTree = "<group>"; };
|
||||
1929B0EEBE4A765934AF8335 /* DataWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataWrapper.h; sourceTree = "<group>"; };
|
||||
1929B15B7EDC9B0F40E5E95C /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Logging.h; sourceTree = "<group>"; };
|
||||
1929B1A51F076E088EF4CCA4 /* server_globals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = server_globals.h; sourceTree = "<group>"; };
|
||||
@ -304,6 +304,7 @@
|
||||
1929B5C3F2F1CA4113DABFFD /* CocoaCategories.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CocoaCategories.m; sourceTree = "<group>"; };
|
||||
1929B5D977261F1EBFA9E8F1 /* FileUtilsTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileUtilsTest.swift; sourceTree = "<group>"; };
|
||||
1929B69499B2569793350CEC /* FileItemIgnorePattern.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileItemIgnorePattern.swift; sourceTree = "<group>"; };
|
||||
1929B6C6C7792B05164B0216 /* PreviewTool.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreviewTool.swift; sourceTree = "<group>"; };
|
||||
1929B7A68B7109CEFAF105E8 /* AppDelegateTransformer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegateTransformer.swift; sourceTree = "<group>"; };
|
||||
1929B7CB4863F80230C32D3C /* FileItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileItem.swift; sourceTree = "<group>"; };
|
||||
1929B8DA5AA33536F0082200 /* PreviewComponent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreviewComponent.swift; sourceTree = "<group>"; };
|
||||
@ -323,6 +324,7 @@
|
||||
1929BD83A13BF133741766CC /* MainWindowTransformer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainWindowTransformer.swift; sourceTree = "<group>"; };
|
||||
1929BD8CBADC191CF8C85309 /* MainWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainWindow.swift; sourceTree = "<group>"; };
|
||||
1929BDF9EBAF1D9D44399045 /* ScoredFileItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScoredFileItem.swift; sourceTree = "<group>"; };
|
||||
1929BE37AA2843779CAFA76F /* PreviewTransformer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreviewTransformer.swift; sourceTree = "<group>"; };
|
||||
1929BE69CF9AB1A10D0DD4F2 /* CocoaCategories.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CocoaCategories.h; sourceTree = "<group>"; };
|
||||
1929BEEB33113B0E33C3830F /* Matcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Matcher.swift; sourceTree = "<group>"; };
|
||||
1929BF00B466B40629C2AABE /* NeoVimView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NeoVimView.swift; sourceTree = "<group>"; };
|
||||
@ -517,13 +519,14 @@
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
1929B32401E8914DE9BF76CA /* UI */ = {
|
||||
1929B32401E8914DE9BF76CA /* Components */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1929BD8CBADC191CF8C85309 /* MainWindow.swift */,
|
||||
1929BD4149D5A25C82064DD8 /* UiRoot.swift */,
|
||||
1929B6C6C7792B05164B0216 /* PreviewTool.swift */,
|
||||
);
|
||||
name = UI;
|
||||
name = Components;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1929B41F745CDCDFE09ACDCF /* resources */ = {
|
||||
@ -540,6 +543,7 @@
|
||||
1929B7A68B7109CEFAF105E8 /* AppDelegateTransformer.swift */,
|
||||
1929B457B9D0FA4D21F3751E /* UiRootTransformer.swift */,
|
||||
1929BD83A13BF133741766CC /* MainWindowTransformer.swift */,
|
||||
1929BE37AA2843779CAFA76F /* PreviewTransformer.swift */,
|
||||
);
|
||||
name = Transformers;
|
||||
sourceTree = "<group>";
|
||||
@ -559,7 +563,7 @@
|
||||
children = (
|
||||
1929BB6608B4F0E037CA0F4C /* States.swift */,
|
||||
1929B34FC23D805A8B29E8F7 /* Context.swift */,
|
||||
1929B32401E8914DE9BF76CA /* UI */,
|
||||
1929B32401E8914DE9BF76CA /* Components */,
|
||||
1929B5E773BDB3B4EE9D00C1 /* Transformers */,
|
||||
);
|
||||
name = Redesign;
|
||||
@ -858,7 +862,6 @@
|
||||
4BDF50101D760AB700D8FBC3 /* Commons */,
|
||||
4B6423941D8EFD6100FC78C8 /* Workspace */,
|
||||
4B97E2CF1D33F92200FC0660 /* resources */,
|
||||
1929B0D767ED19EE1281ECD9 /* HttpServerService.swift */,
|
||||
1929BA652D3B88FC071531EC /* Redesign */,
|
||||
);
|
||||
path = VimR;
|
||||
@ -1354,7 +1357,6 @@
|
||||
1929B4145AA81F006BAF3B5C /* PreviewService.swift in Sources */,
|
||||
1929BD52275A6570C666A7BA /* PreviewRenderer.swift in Sources */,
|
||||
1929BD2F41D93ADFF43C1C98 /* NetUtils.m in Sources */,
|
||||
1929B5CF6ECCBCC3FB5292CE /* HttpServerService.swift in Sources */,
|
||||
1929BE0DAEE9664C5BCFA211 /* States.swift in Sources */,
|
||||
1929B4FEE6EB56EF3F56B805 /* Context.swift in Sources */,
|
||||
1929B0FF696312F754BC96E2 /* MainWindow.swift in Sources */,
|
||||
@ -1362,6 +1364,8 @@
|
||||
1929BAFF1E011321D3186EE6 /* UiRoot.swift in Sources */,
|
||||
1929B29B95AD176D57942E08 /* UiRootTransformer.swift in Sources */,
|
||||
1929BB4A9B2FA42A64CCCC76 /* MainWindowTransformer.swift in Sources */,
|
||||
1929B8FB248D71BF88A35761 /* PreviewTool.swift in Sources */,
|
||||
1929B4B70926DE113E6BF990 /* PreviewTransformer.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -50,10 +50,10 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
fileprivate var launching = true
|
||||
|
||||
override init() {
|
||||
let source = self.stateContext.stateSource.mapOmittingNil { $0 as? MainWindowStates }
|
||||
let source = self.stateContext.stateSource
|
||||
self.uiRoot = UiRoot(source: source,
|
||||
emitter: self.stateContext.actionEmitter,
|
||||
state: AppState.default.mainWindows)
|
||||
state: AppState.default) // FIXME
|
||||
|
||||
|
||||
self.actionSink = self.actionSubject.asObservable()
|
||||
|
@ -1,14 +1,14 @@
|
||||
//
|
||||
// Created by Tae Won Ha on 1/16/17.
|
||||
// Copyright (c) 2017 Tae Won Ha. All rights reserved.
|
||||
//
|
||||
/**
|
||||
* Tae Won Ha - http://taewon.de - @hataewon
|
||||
* See LICENSE
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import RxSwift
|
||||
|
||||
class AppDelegateTransformer: Transformer {
|
||||
|
||||
typealias Pair = StateActionPair<MainWindowStates, AppDelegate.Action>
|
||||
typealias Pair = StateActionPair<AppState, AppDelegate.Action>
|
||||
|
||||
func transform(_ source: Observable<Pair>) -> Observable<Pair> {
|
||||
return source.map { pair in
|
||||
@ -17,12 +17,13 @@ class AppDelegateTransformer: Transformer {
|
||||
case let .newMainWindow(urls, cwd):
|
||||
var state = pair.state
|
||||
|
||||
var mainWindow = state.last
|
||||
var mainWindow = state.currentMainWindow
|
||||
mainWindow.uuid = UUID().uuidString
|
||||
mainWindow.serverBaseUrl = state.baseServerUrl.appendingPathComponent("\(mainWindow.uuid)")
|
||||
mainWindow.urlsToOpen = urls.toDict { url in MainWindow.OpenMode.default }
|
||||
mainWindow.cwd = cwd
|
||||
|
||||
state.current[mainWindow.uuid] = mainWindow
|
||||
state.mainWindows[mainWindow.uuid] = mainWindow
|
||||
|
||||
return StateActionPair(state: state, action: pair.action)
|
||||
|
||||
|
@ -1,22 +1,11 @@
|
||||
//
|
||||
// Created by Tae Won Ha on 1/16/17.
|
||||
// Copyright (c) 2017 Tae Won Ha. All rights reserved.
|
||||
//
|
||||
/**
|
||||
* Tae Won Ha - http://taewon.de - @hataewon
|
||||
* See LICENSE
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import RxSwift
|
||||
|
||||
class DummyService: Transformer {
|
||||
|
||||
typealias State = MainWindowStates
|
||||
typealias Action = AppDelegate.Action
|
||||
|
||||
func transform(_ source: Observable<StateActionPair<State, Action>>) -> Observable<StateActionPair<State, Action>> {
|
||||
NSLog("\(#function) dummy transform")
|
||||
return source
|
||||
}
|
||||
}
|
||||
|
||||
class StateContext {
|
||||
|
||||
let stateSource: Observable<Any>
|
||||
@ -26,57 +15,56 @@ class StateContext {
|
||||
self.stateSource = self.stateSubject.asObservable()
|
||||
let actionSource = self.actionEmitter.observable
|
||||
|
||||
actionSource
|
||||
.mapOmittingNil { $0 as? AppDelegate.Action }
|
||||
.map { StateActionPair(state: self.appState.mainWindows, action: $0) }
|
||||
.transform(by: [appDelegateTransformer])
|
||||
.map { $0.state }
|
||||
.subscribe(onNext: { state in
|
||||
self.appState.mainWindows = state
|
||||
self.stateSubject.onNext(state)
|
||||
})
|
||||
.addDisposableTo(self.disposeBag)
|
||||
self.previewTransformer = PreviewTransformer(port: in_port_t(self.appState.baseServerUrl.port ?? 0))
|
||||
|
||||
actionSource
|
||||
.mapOmittingNil { $0 as? UuidAction<MainWindow.Action> }
|
||||
.map { StateActionPair(state: self.appState.mainWindows, action: $0) }
|
||||
.transform(by: self.uiRootTransformer)
|
||||
.map { $0.state }
|
||||
Observable
|
||||
.of(
|
||||
actionSource
|
||||
.mapOmittingNil { $0 as? AppDelegate.Action }
|
||||
.map { StateActionPair(state: self.appState, action: $0, modified: false) }
|
||||
.transform(by: self.appDelegateTransformer)
|
||||
.filter { $0.modified }
|
||||
.map { $0.state },
|
||||
actionSource
|
||||
.mapOmittingNil { $0 as? UuidAction<MainWindow.Action> }
|
||||
.map { StateActionPair(state: self.appState, action: $0, modified: false) }
|
||||
.transform(by: self.uiRootTransformer)
|
||||
.filter { $0.modified }
|
||||
.map { $0.state }
|
||||
)
|
||||
.merge()
|
||||
.subscribe(onNext: { state in
|
||||
self.appState.mainWindows = state
|
||||
self.stateSubject.onNext(state)
|
||||
self.appState = state
|
||||
self.stateSubject.onNext(self.appState)
|
||||
})
|
||||
.addDisposableTo(self.disposeBag)
|
||||
|
||||
actionSource
|
||||
.mapOmittingNil { $0 as? UuidAction<MainWindow.Action> }
|
||||
.mapOmittingNil { action in
|
||||
guard let mainWindowState = self.appState.mainWindows.current[action.uuid] else {
|
||||
guard let mainWindowState = self.appState.mainWindows[action.uuid] else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return StateActionPair(state: UuidState(uuid: action.uuid, state: mainWindowState), action: action.payload)
|
||||
return StateActionPair(state: UuidState(uuid: action.uuid, state: mainWindowState),
|
||||
action: action.payload,
|
||||
modified: false)
|
||||
}
|
||||
.transform(by: self.mainWindowTransformer)
|
||||
.transform(by: self.previewTransformer)
|
||||
.filter { $0.modified }
|
||||
.subscribe(onNext: { pair in
|
||||
self.appState.mainWindows.current[pair.state.uuid] = pair.state.payload
|
||||
self.appState.mainWindows[pair.state.uuid] = pair.state.payload
|
||||
self.stateSubject.onNext(pair.state)
|
||||
})
|
||||
.addDisposableTo(self.disposeBag)
|
||||
|
||||
actionSource
|
||||
.subscribe(onNext: { action in
|
||||
NSLog("ACTION: \(action)")
|
||||
})
|
||||
.addDisposableTo(self.disposeBag)
|
||||
stateSource
|
||||
.subscribe(onNext: { state in
|
||||
NSLog("STATE : \(self.appState.mainWindows.current)")
|
||||
})
|
||||
.addDisposableTo(self.disposeBag)
|
||||
actionSource.debug().subscribe().addDisposableTo(self.disposeBag)
|
||||
stateSource.debug().subscribe().addDisposableTo(self.disposeBag)
|
||||
}
|
||||
|
||||
fileprivate let stateSubject = PublishSubject<Any>()
|
||||
fileprivate let scheduler = SerialDispatchQueueScheduler(qos: .userInitiated)
|
||||
fileprivate let disposeBag = DisposeBag()
|
||||
|
||||
fileprivate var appState = AppState.default
|
||||
@ -84,15 +72,16 @@ class StateContext {
|
||||
fileprivate let appDelegateTransformer = AppDelegateTransformer()
|
||||
fileprivate let uiRootTransformer = UiRootTransformer()
|
||||
fileprivate let mainWindowTransformer = MainWindowTransformer()
|
||||
fileprivate let previewTransformer: PreviewTransformer
|
||||
}
|
||||
|
||||
extension Observable {
|
||||
|
||||
fileprivate func transform<T:Transformer>(by transformers: [T]) -> Observable<Element> where T.Pair == Element {
|
||||
fileprivate func transform<T:Transformer>(by transformers: [T]) -> Observable<Element> where T.Element == Element {
|
||||
return transformers.reduce(self) { (prevSource, transformer) in transformer.transform(prevSource) }
|
||||
}
|
||||
|
||||
fileprivate func transform<T:Transformer>(by transformer: T) -> Observable<Element> where T.Pair == Element {
|
||||
fileprivate func transform<T:Transformer>(by transformer: T) -> Observable<Element> where T.Element == Element {
|
||||
return transformer.transform(self)
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +0,0 @@
|
||||
//
|
||||
// Created by Tae Won Ha on 1/13/17.
|
||||
// Copyright (c) 2017 Tae Won Ha. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Swifter
|
||||
|
||||
class HttpServerService {
|
||||
|
||||
fileprivate let server = HttpServer()
|
||||
|
||||
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
//
|
||||
// Created by Tae Won Ha on 1/16/17.
|
||||
// Copyright (c) 2017 Tae Won Ha. All rights reserved.
|
||||
//
|
||||
/**
|
||||
* Tae Won Ha - http://taewon.de - @hataewon
|
||||
* See LICENSE
|
||||
*/
|
||||
|
||||
import Cocoa
|
||||
import RxSwift
|
||||
@ -12,7 +12,28 @@ protocol UiComponent {
|
||||
|
||||
associatedtype StateType
|
||||
|
||||
init(source: Observable<StateType>, emitter: ActionEmitter, state: StateType)
|
||||
init(source: StateSource, emitter: ActionEmitter, state: StateType)
|
||||
}
|
||||
|
||||
class Debouncer<T> {
|
||||
|
||||
let observable: Observable<T>
|
||||
|
||||
init(interval: RxTimeInterval) {
|
||||
self.observable = self.subject.throttle(interval, latest: true, scheduler: self.scheduler)
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.subject.onCompleted()
|
||||
}
|
||||
|
||||
func call(_ element: T) {
|
||||
self.subject.onNext(element)
|
||||
}
|
||||
|
||||
fileprivate let subject = PublishSubject<T>()
|
||||
fileprivate let scheduler = SerialDispatchQueueScheduler(qos: .userInteractive)
|
||||
fileprivate let disposeBag = DisposeBag()
|
||||
}
|
||||
|
||||
class MainWindow: NSObject,
|
||||
@ -27,8 +48,13 @@ class MainWindow: NSObject,
|
||||
case cd(to: URL)
|
||||
case setBufferList([NeoVimBuffer])
|
||||
|
||||
case setCurrentBuffer(NeoVimBuffer)
|
||||
|
||||
case becomeKey
|
||||
|
||||
case scroll(to: Position)
|
||||
case setCursor(to: Position)
|
||||
|
||||
case close
|
||||
}
|
||||
|
||||
@ -41,7 +67,7 @@ class MainWindow: NSObject,
|
||||
case verticalSplit
|
||||
}
|
||||
|
||||
required init(source: Observable<StateType>, emitter: ActionEmitter, state: StateType) {
|
||||
required init(source: StateSource, emitter: ActionEmitter, state: StateType) {
|
||||
self.uuid = state.uuid
|
||||
self.emitter = emitter
|
||||
|
||||
@ -54,11 +80,27 @@ class MainWindow: NSObject,
|
||||
self.windowController = NSWindowController(windowNibName: "MainWindow")
|
||||
|
||||
super.init()
|
||||
|
||||
self.scrollDebouncer.observable
|
||||
.subscribe(onNext: { [unowned self] action in
|
||||
self.emitter.emit(self.uuidAction(for: action))
|
||||
})
|
||||
.addDisposableTo(self.disposeBag)
|
||||
|
||||
self.cursorDebouncer.observable
|
||||
.subscribe(onNext: { [unowned self] action in
|
||||
self.emitter.emit(self.uuidAction(for: action))
|
||||
})
|
||||
.addDisposableTo(self.disposeBag)
|
||||
|
||||
self.addViews()
|
||||
|
||||
self.windowController.window?.delegate = self
|
||||
|
||||
source
|
||||
.mapOmittingNil { $0 as? UuidState<StateType> }
|
||||
.filter { $0.uuid == state.uuid }
|
||||
.debug()
|
||||
.observeOn(MainScheduler.instance)
|
||||
.subscribe(onNext: { [unowned self] state in
|
||||
})
|
||||
@ -123,6 +165,13 @@ class MainWindow: NSObject,
|
||||
|
||||
fileprivate let workspace: Workspace
|
||||
fileprivate let neoVimView: NeoVimView
|
||||
|
||||
fileprivate let scrollDebouncer = Debouncer<Action>(interval: 0.75)
|
||||
fileprivate let cursorDebouncer = Debouncer<Action>(interval: 0.75)
|
||||
|
||||
fileprivate func uuidAction(for action: Action) -> UuidAction<Action> {
|
||||
return UuidAction(uuid: self.uuid, action: action)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - NeoVimViewDelegate
|
||||
@ -141,24 +190,24 @@ extension MainWindow {
|
||||
}
|
||||
|
||||
func cwdChanged() {
|
||||
self.emitter.emit(UuidAction(uuid: self.uuid, action: Action.cd(to: self.neoVimView.cwd)))
|
||||
self.emitter.emit(self.uuidAction(for: .cd(to: self.neoVimView.cwd)))
|
||||
}
|
||||
|
||||
func bufferListChanged() {
|
||||
let buffers = self.neoVimView.allBuffers()
|
||||
self.emitter.emit(UuidAction(uuid: self.uuid, action: Action.setBufferList(buffers)))
|
||||
self.emitter.emit(self.uuidAction(for: .setBufferList(buffers)))
|
||||
}
|
||||
|
||||
func currentBufferChanged(_ currentBuffer: NeoVimBuffer) {
|
||||
// self.publish(event: MainWindowAction.currentBufferChanged(mainWindow: self, buffer: currentBuffer))
|
||||
self.emitter.emit(self.uuidAction(for: .setCurrentBuffer(currentBuffer)))
|
||||
}
|
||||
|
||||
func tabChanged() {
|
||||
// guard let currentBuffer = self.neoVimView.currentBuffer() else {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// self.publish(event: MainWindowAction.currentBufferChanged(mainWindow: self, buffer: currentBuffer))
|
||||
guard let currentBuffer = self.neoVimView.currentBuffer() else {
|
||||
return
|
||||
}
|
||||
|
||||
self.currentBufferChanged(currentBuffer)
|
||||
}
|
||||
|
||||
func ipcBecameInvalid(reason: String) {
|
||||
@ -174,11 +223,11 @@ extension MainWindow {
|
||||
}
|
||||
|
||||
func scroll() {
|
||||
// self.scrollFlow.publish(event: ScrollAction.scroll(to: self.neoVimView.currentPosition))
|
||||
self.scrollDebouncer.call(.scroll(to: self.neoVimView.currentPosition))
|
||||
}
|
||||
|
||||
func cursor(to position: Position) {
|
||||
// self.scrollFlow.publish(event: ScrollAction.cursor(to: self.neoVimView.currentPosition))
|
||||
self.cursorDebouncer.call(.setCursor(to: position))
|
||||
}
|
||||
}
|
||||
|
||||
@ -186,11 +235,11 @@ extension MainWindow {
|
||||
extension MainWindow {
|
||||
|
||||
func windowDidBecomeKey(_: Notification) {
|
||||
self.emitter.emit(UuidAction(uuid: self.uuid, action: Action.becomeKey))
|
||||
self.emitter.emit(self.uuidAction(for: .becomeKey))
|
||||
}
|
||||
|
||||
func windowWillClose(_: Notification) {
|
||||
self.emitter.emit(UuidAction(uuid: self.uuid, action: Action.close))
|
||||
self.emitter.emit(self.uuidAction(for: .close))
|
||||
}
|
||||
|
||||
func windowShouldClose(_: Any) -> Bool {
|
||||
|
@ -110,7 +110,7 @@ class MainWindowComponent: WindowComponent,
|
||||
|
||||
fileprivate let scrollFlow: EmbeddableComponent
|
||||
|
||||
fileprivate let httpServer: HttpServer;
|
||||
fileprivate let httpServer: Swifter.HttpServer;
|
||||
|
||||
// MARK: - API
|
||||
var uuid: String {
|
||||
@ -144,7 +144,7 @@ class MainWindowComponent: WindowComponent,
|
||||
fileItemService: FileItemService,
|
||||
cwd: URL,
|
||||
urls: [URL] = [],
|
||||
httpServer: HttpServer,
|
||||
httpServer: Swifter.HttpServer,
|
||||
initialData: PrefData)
|
||||
{
|
||||
self.neoVimView = NeoVimView(frame: CGRect.zero,
|
||||
|
@ -20,7 +20,7 @@ class MainWindowManager: StandardFlow {
|
||||
fileprivate let fileItemService: FileItemService
|
||||
fileprivate var data: PrefData
|
||||
|
||||
fileprivate let httpServer = HttpServer() // TODO: WRONG: service-ify this!!!
|
||||
fileprivate let httpServer = Swifter.HttpServer() // TODO: WRONG: service-ify this!!!
|
||||
|
||||
init(source: Observable<Any>, fileItemService: FileItemService, initialData: PrefData) {
|
||||
self.fileItemService = fileItemService
|
||||
|
@ -1,7 +1,7 @@
|
||||
//
|
||||
// Created by Tae Won Ha on 1/17/17.
|
||||
// Copyright (c) 2017 Tae Won Ha. All rights reserved.
|
||||
//
|
||||
/**
|
||||
* Tae Won Ha - http://taewon.de - @hataewon
|
||||
* See LICENSE
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import RxSwift
|
||||
@ -27,8 +27,11 @@ class MainWindowTransformer: Transformer {
|
||||
.forEach { state.urlsToOpen.removeValue(forKey: $0) }
|
||||
state.buffers = buffers
|
||||
|
||||
case let .setCurrentBuffer(buffer):
|
||||
state.currentBuffer = buffer
|
||||
|
||||
default:
|
||||
break
|
||||
return pair
|
||||
|
||||
}
|
||||
|
||||
|
@ -149,7 +149,7 @@ class MarkdownRenderer: NSObject, Flow, PreviewRenderer {
|
||||
fileprivate var currentPreviewPosition = Position(row: 1, column: 1)
|
||||
|
||||
fileprivate let uuid = UUID().uuidString
|
||||
fileprivate let httpServer: HttpServer
|
||||
fileprivate let httpServer: Swifter.HttpServer
|
||||
fileprivate let port: Int
|
||||
fileprivate let tempDir = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
|
||||
|
||||
@ -171,7 +171,7 @@ class MarkdownRenderer: NSObject, Flow, PreviewRenderer {
|
||||
let toolbar: NSView? = NSView(forAutoLayout: ())
|
||||
let menuItems: [NSMenuItem]?
|
||||
|
||||
init(source: Observable<Any>, scrollSource: Observable<Any>, httpServer: HttpServer, initialData: PrefData) {
|
||||
init(source: Observable<Any>, scrollSource: Observable<Any>, httpServer: Swifter.HttpServer, initialData: PrefData) {
|
||||
NSLog("\(#function) \(uuid)")
|
||||
NSLog("\(#function) \(self.tempDir)")
|
||||
|
||||
|
@ -113,7 +113,7 @@ class PreviewComponent: NSView, ViewComponent, ToolDataHolder, WKNavigationDeleg
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate let httpServer: HttpServer
|
||||
fileprivate let httpServer: Swifter.HttpServer
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
@ -140,7 +140,7 @@ class PreviewComponent: NSView, ViewComponent, ToolDataHolder, WKNavigationDeleg
|
||||
return self
|
||||
}
|
||||
|
||||
init(source: Observable<Any>, scrollSource: Observable<Any>, httpServer: HttpServer, initialData: PrefData) {
|
||||
init(source: Observable<Any>, scrollSource: Observable<Any>, httpServer: Swifter.HttpServer, initialData: PrefData) {
|
||||
self.flow = EmbeddableComponent(source: source)
|
||||
self.httpServer = httpServer
|
||||
|
||||
|
52
VimR/PreviewTool.swift
Normal file
52
VimR/PreviewTool.swift
Normal file
@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Tae Won Ha - http://taewon.de - @hataewon
|
||||
* See LICENSE
|
||||
*/
|
||||
|
||||
import Cocoa
|
||||
import RxSwift
|
||||
import PureLayout
|
||||
import WebKit
|
||||
import Swifter
|
||||
|
||||
class PreviewTool: NSView, UiComponent, WKNavigationDelegate {
|
||||
|
||||
// enum Action {
|
||||
//
|
||||
// case automaticRefresh(url: URL)
|
||||
// case reverseSearch(to: Position)
|
||||
// case scroll(to: Position)
|
||||
// }
|
||||
|
||||
typealias StateType = MainWindow.State
|
||||
|
||||
static let basePath = "tools/previews"
|
||||
|
||||
required init(source: StateSource, emitter: ActionEmitter, state: StateType) {
|
||||
self.webview.configureForAutoLayout()
|
||||
|
||||
super.init(frame: .zero)
|
||||
self.configureForAutoLayout()
|
||||
|
||||
self.webview.navigationDelegate = self
|
||||
self.webview.loadHTMLString("", baseURL: nil)
|
||||
|
||||
self.addViews()
|
||||
}
|
||||
|
||||
fileprivate func addViews() {
|
||||
self.addSubview(self.webview)
|
||||
self.webview.autoPinEdgesToSuperviewEdges()
|
||||
}
|
||||
|
||||
func webView(_: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
|
||||
NSLog("ERROR preview component's webview: \(error)")
|
||||
}
|
||||
|
||||
fileprivate let webview = WKWebView(frame: .zero, configuration: WKWebViewConfiguration())
|
||||
fileprivate var isOpen = false
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
40
VimR/PreviewTransformer.swift
Normal file
40
VimR/PreviewTransformer.swift
Normal file
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Tae Won Ha - http://taewon.de - @hataewon
|
||||
* See LICENSE
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import CocoaMarkdown
|
||||
import RxSwift
|
||||
import Swifter
|
||||
|
||||
// Currently supports only markdown
|
||||
class PreviewTransformer: Transformer {
|
||||
|
||||
typealias Pair = StateActionPair<UuidState<MainWindow.State>, MainWindow.Action>
|
||||
|
||||
init(port: in_port_t) {
|
||||
try? self.server.start(port)
|
||||
}
|
||||
|
||||
func transform(_ source: Observable<Pair>) -> Observable<Pair> {
|
||||
return source.map { pair in
|
||||
var state = pair.state.payload
|
||||
|
||||
switch pair.action {
|
||||
|
||||
case let .setCurrentBuffer(buffer):
|
||||
NSLog("\(#file) !!!!!!!!!!!!!!!!!")
|
||||
break
|
||||
|
||||
default:
|
||||
return pair
|
||||
}
|
||||
|
||||
return StateActionPair(state: UuidState(uuid: pair.state.uuid, state: state), action: pair.action)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate let extensions = Set(["md", "markdown"])
|
||||
fileprivate let server = Swifter.HttpServer()
|
||||
}
|
@ -1,12 +1,13 @@
|
||||
//
|
||||
// Created by Tae Won Ha on 1/13/17.
|
||||
// Copyright (c) 2017 Tae Won Ha. All rights reserved.
|
||||
//
|
||||
/**
|
||||
* Tae Won Ha - http://taewon.de - @hataewon
|
||||
* See LICENSE
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import RxSwift
|
||||
|
||||
typealias ActionEmitter = Emitter<Any>
|
||||
typealias StateSource = Observable<Any>
|
||||
|
||||
class Emitter<T> {
|
||||
|
||||
@ -30,10 +31,12 @@ class Emitter<T> {
|
||||
|
||||
class StateActionPair<S, A> {
|
||||
|
||||
let modified: Bool
|
||||
let state: S
|
||||
let action: A
|
||||
|
||||
init(state: S, action: A) {
|
||||
init(state: S, action: A, modified: Bool = true) {
|
||||
self.modified = modified
|
||||
self.state = state
|
||||
self.action = action
|
||||
}
|
||||
@ -69,265 +72,55 @@ class UuidState<S>: CustomStringConvertible {
|
||||
}
|
||||
}
|
||||
|
||||
protocol Transformer {
|
||||
protocol Morpher {
|
||||
|
||||
associatedtype Pair
|
||||
associatedtype In
|
||||
associatedtype Out
|
||||
|
||||
func transform(_ source: Observable<Pair>) -> Observable<Pair>
|
||||
func transform(_ source: Observable<In>) -> Observable<Out>
|
||||
}
|
||||
|
||||
protocol PersistableState {
|
||||
protocol Transformer: Morpher {
|
||||
|
||||
init?(dict: [String: Any])
|
||||
associatedtype Element
|
||||
|
||||
func dict() -> [String: Any]
|
||||
typealias In = Element
|
||||
typealias Out = Element
|
||||
|
||||
func transform(_ source: Observable<Element>) -> Observable<Element>
|
||||
}
|
||||
|
||||
struct MainWindowStates: PersistableState {
|
||||
struct AppState {
|
||||
|
||||
var last: MainWindow.State
|
||||
static let `default` = AppState(mainWindow: MainWindow.State.default)
|
||||
|
||||
var current: [String: MainWindow.State]
|
||||
var currentMainWindow: MainWindow.State
|
||||
var mainWindows: [String: MainWindow.State] = [:]
|
||||
|
||||
init(last: MainWindow.State) {
|
||||
self.last = last
|
||||
self.current = [:]
|
||||
let baseServerUrl: URL
|
||||
|
||||
init(mainWindow: MainWindow.State) {
|
||||
self.baseServerUrl = URL(string: "http://localhost:\(NetUtils.openPort())")!
|
||||
self.currentMainWindow = mainWindow
|
||||
}
|
||||
|
||||
init?(dict: [String: Any]) {
|
||||
guard let lastDict: [String: Any] = PrefUtils.value(from: dict, for: MainWindowStates.last) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let last = MainWindow.State(dict: lastDict) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.init(last: last)
|
||||
}
|
||||
|
||||
func dict() -> [String: Any] {
|
||||
return [
|
||||
MainWindowStates.last: self.last.dict(),
|
||||
]
|
||||
}
|
||||
|
||||
fileprivate static let last = "last"
|
||||
}
|
||||
|
||||
struct AppState: PersistableState {
|
||||
|
||||
static let `default` = AppState(general: GeneralPrefState.default,
|
||||
appearance: AppearancePrefState.default,
|
||||
advanced: AdvancedPrefState.default,
|
||||
mainWindow: MainWindow.State.default)
|
||||
|
||||
var general: GeneralPrefState
|
||||
var appearance: AppearancePrefState
|
||||
var advanced: AdvancedPrefState
|
||||
|
||||
var mainWindows: MainWindowStates
|
||||
|
||||
init(general: GeneralPrefState,
|
||||
appearance: AppearancePrefState,
|
||||
advanced: AdvancedPrefState,
|
||||
mainWindow: MainWindow.State) {
|
||||
self.general = general
|
||||
self.appearance = appearance
|
||||
self.advanced = advanced
|
||||
self.mainWindows = MainWindowStates(last: mainWindow)
|
||||
}
|
||||
|
||||
init?(dict: [String: Any]) {
|
||||
guard let generalDict: [String: Any] = PrefUtils.value(from: dict, for: AppState.general),
|
||||
let appearanceDict: [String: Any] = PrefUtils.value(from: dict, for: AppState.appearance),
|
||||
let advancedDict: [String: Any] = PrefUtils.value(from: dict, for: AppState.advanced),
|
||||
let mainWindowDict: [String: Any] = PrefUtils.value(from: dict, for: AppState.mainWindow)
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let general = GeneralPrefState(dict: generalDict),
|
||||
let appearance = AppearancePrefState(dict: appearanceDict),
|
||||
let advanced = AdvancedPrefState(dict: advancedDict),
|
||||
let mainWindow = MainWindow.State(dict: mainWindowDict)
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.init(general: general, appearance: appearance, advanced: advanced, mainWindow: mainWindow)
|
||||
}
|
||||
|
||||
func dict() -> [String: Any] {
|
||||
return [
|
||||
AppState.general: self.general.dict(),
|
||||
AppState.appearance: self.appearance.dict(),
|
||||
AppState.advanced: self.advanced.dict(),
|
||||
AppState.mainWindow: self.mainWindows.dict(),
|
||||
]
|
||||
}
|
||||
|
||||
fileprivate static let general = "general"
|
||||
fileprivate static let appearance = "appearance"
|
||||
fileprivate static let advanced = "advanced"
|
||||
fileprivate static let mainWindow = "mainWindow"
|
||||
}
|
||||
|
||||
struct GeneralPrefState: Equatable, PersistableState {
|
||||
|
||||
static func ==(left: GeneralPrefState, right: GeneralPrefState) -> Bool {
|
||||
return left.openNewWindowWhenLaunching == right.openNewWindowWhenLaunching
|
||||
&& left.openNewWindowOnReactivation == right.openNewWindowOnReactivation
|
||||
&& left.ignorePatterns == right.ignorePatterns
|
||||
}
|
||||
|
||||
static let `default` = GeneralPrefState(openNewWindowWhenLaunching: true,
|
||||
openNewWindowOnReactivation: true,
|
||||
ignorePatterns: GeneralPrefState.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: GeneralPrefState.openNewWindowWhenLaunching),
|
||||
let openNewWinOnReactivation = PrefUtils.bool(from: dict, for: GeneralPrefState.openNewWindowOnReactivation),
|
||||
let ignorePatternsStr = dict[GeneralPrefState.ignorePatterns] as? String
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.init(openNewWindowWhenLaunching: openNewWinWhenLaunching,
|
||||
openNewWindowOnReactivation: openNewWinOnReactivation,
|
||||
ignorePatterns: PrefUtils.ignorePatterns(fromString: ignorePatternsStr))
|
||||
}
|
||||
|
||||
func dict() -> [String: Any] {
|
||||
return [
|
||||
GeneralPrefState.openNewWindowWhenLaunching: self.openNewWindowWhenLaunching,
|
||||
GeneralPrefState.openNewWindowOnReactivation: self.openNewWindowOnReactivation,
|
||||
GeneralPrefState.ignorePatterns: PrefUtils.ignorePatternString(fromSet: self.ignorePatterns),
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
fileprivate static let defaultIgnorePatterns = Set(
|
||||
["*/.git", "*.o", "*.d", "*.dia"].map(FileItemIgnorePattern.init)
|
||||
)
|
||||
fileprivate static let openNewWindowWhenLaunching = "open-new-window-when-launching"
|
||||
fileprivate static let openNewWindowOnReactivation = "open-new-window-on-reactivation"
|
||||
fileprivate static let ignorePatterns = "ignore-patterns"
|
||||
}
|
||||
|
||||
struct AppearancePrefState: Equatable, PersistableState {
|
||||
|
||||
static func ==(left: AppearancePrefState, right: AppearancePrefState) -> Bool {
|
||||
return left.editorUsesLigatures == right.editorUsesLigatures
|
||||
&& left.editorFont.isEqual(to: right.editorFont)
|
||||
&& left.editorLinespacing == right.editorLinespacing
|
||||
}
|
||||
|
||||
static let `default` = AppearancePrefState(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[AppearancePrefState.editorFontName] as? String,
|
||||
let fEditorFontSize = PrefUtils.float(from: dict, for: AppearancePrefState.editorFontSize),
|
||||
let fEditorLinespacing = PrefUtils.float(from: dict, for: AppearancePrefState.editorLinespacing),
|
||||
let editorUsesLigatures = PrefUtils.bool(from: dict, for: AppearancePrefState.editorUsesLigatures)
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.init(editorFont: PrefUtils.saneFont(editorFontName, fontSize: CGFloat(fEditorFontSize)),
|
||||
editorLinespacing: CGFloat(fEditorLinespacing),
|
||||
editorUsesLigatures: editorUsesLigatures)
|
||||
}
|
||||
|
||||
func dict() -> [String: Any] {
|
||||
return [
|
||||
AppearancePrefState.editorFontName: self.editorFont.fontName,
|
||||
AppearancePrefState.editorFontSize: Float(self.editorFont.pointSize),
|
||||
AppearancePrefState.editorLinespacing: Float(self.editorLinespacing),
|
||||
AppearancePrefState.editorUsesLigatures: self.editorUsesLigatures,
|
||||
]
|
||||
}
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
struct AdvancedPrefState: Equatable, PersistableState {
|
||||
|
||||
static func ==(left: AdvancedPrefState, right: AdvancedPrefState) -> Bool {
|
||||
return left.useSnapshotUpdateChannel == right.useSnapshotUpdateChannel
|
||||
&& left.useInteractiveZsh == right.useInteractiveZsh
|
||||
}
|
||||
|
||||
static let `default` = AdvancedPrefState(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: AdvancedPrefState.useSnapshotUpdateChannel),
|
||||
let useInteractiveZsh = PrefUtils.bool(from: dict, for: AdvancedPrefState.useInteractiveZsh)
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.init(useSnapshotUpdateChannel: useSnapshot, useInteractiveZsh: useInteractiveZsh)
|
||||
}
|
||||
|
||||
func dict() -> [String: Any] {
|
||||
return [
|
||||
AdvancedPrefState.useSnapshotUpdateChannel: self.useSnapshotUpdateChannel,
|
||||
AdvancedPrefState.useInteractiveZsh: self.useInteractiveZsh,
|
||||
]
|
||||
}
|
||||
|
||||
fileprivate static let useSnapshotUpdateChannel = "use-snapshot-update-channel"
|
||||
fileprivate static let useInteractiveZsh = "use-interactive-zsh"
|
||||
}
|
||||
|
||||
extension MainWindow {
|
||||
|
||||
struct State: PersistableState {
|
||||
struct State {
|
||||
|
||||
static let `default` = State(isAllToolsVisible: true,
|
||||
static let `default` = State(serverBaseUrl: URL(string: "http://localhost/dummy")!,
|
||||
isAllToolsVisible: true,
|
||||
isToolButtonsVisible: true)
|
||||
|
||||
var isAllToolsVisible = true
|
||||
var isToolButtonsVisible = true
|
||||
|
||||
// transient
|
||||
////// transient
|
||||
|
||||
// neovim
|
||||
var uuid = UUID().uuidString
|
||||
var currentBuffer: NeoVimBuffer?
|
||||
var buffers = [NeoVimBuffer]()
|
||||
var cwd = FileUtils.userHomeUrl
|
||||
|
||||
@ -338,291 +131,16 @@ extension MainWindow {
|
||||
var isUseLigatures = false
|
||||
var isUseInteractiveZsh = false
|
||||
|
||||
var serverBaseUrl: URL
|
||||
|
||||
// transient^2
|
||||
var urlsToOpen = [URL: OpenMode]()
|
||||
|
||||
init(isAllToolsVisible: Bool, isToolButtonsVisible: Bool) {
|
||||
init(serverBaseUrl: URL, isAllToolsVisible: Bool, isToolButtonsVisible: Bool) {
|
||||
self.serverBaseUrl = serverBaseUrl
|
||||
self.isAllToolsVisible = isAllToolsVisible
|
||||
self.isToolButtonsVisible = isToolButtonsVisible
|
||||
}
|
||||
|
||||
init?(dict: [String: Any]) {
|
||||
guard let isAllToolsVisible = PrefUtils.bool(from: dict, for: State.isAllToolsVisible),
|
||||
let isToolButtonsVisible = PrefUtils.bool(from: dict, for: State.isToolButtonsVisible)
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.init(isAllToolsVisible: isAllToolsVisible, isToolButtonsVisible: isToolButtonsVisible)
|
||||
}
|
||||
|
||||
func dict() -> [String: Any] {
|
||||
return [
|
||||
State.isAllToolsVisible: self.isAllToolsVisible,
|
||||
State.isToolButtonsVisible: self.isToolButtonsVisible,
|
||||
]
|
||||
}
|
||||
|
||||
fileprivate static let isAllToolsVisible = "is-all-tools-visible"
|
||||
fileprivate static let isToolButtonsVisible = "is-tool-buttons-visible"
|
||||
}
|
||||
}
|
||||
|
||||
//struct ToolsState: PersistableState {
|
||||
//
|
||||
// static let `default` = ToolsState(fileBrowser: FileBrowserComponent.State.default,
|
||||
// bufferList: BufferListComponent.State.default,
|
||||
// preview: PreviewComponent.State.default)
|
||||
//
|
||||
// var fileBrowser: FileBrowserComponent.State
|
||||
// var bufferList: BufferListComponent.State
|
||||
// var preview: PreviewComponent.State
|
||||
//
|
||||
// init(fileBrowser: FileBrowserComponent.State,
|
||||
// bufferList: BufferListComponent.State,
|
||||
// preview: PreviewComponent.State) {
|
||||
// self.fileBrowser = fileBrowser
|
||||
// self.bufferList = bufferList
|
||||
// self.preview = preview
|
||||
// }
|
||||
//
|
||||
// init?(dict: [String: Any]) {
|
||||
// guard let fileBrowserDict = dict[FileBrowserComponent.identifier] as? [String: Any],
|
||||
// let bufferListDict = dict[BufferListComponent.identifier] as? [String: Any],
|
||||
// let previewDict = dict[PreviewComponent.identifier] as? [String: Any]
|
||||
// else {
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// guard let fileBrowser = FileBrowserComponent.State(dict: fileBrowserDict),
|
||||
// let bufferList = BufferListComponent.State(dict: bufferListDict),
|
||||
// let preview = PreviewComponent.State(dict: previewDict)
|
||||
// else {
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// self.init(fileBrowser: fileBrowser, bufferList: bufferList, preview: preview)
|
||||
// }
|
||||
//
|
||||
// func dict() -> [String: Any] {
|
||||
// return [
|
||||
// FileBrowserComponent.identifier: self.fileBrowser.dict(),
|
||||
// BufferListComponent.identifier: self.bufferList.dict(),
|
||||
// PreviewComponent.identifier: self.preview.dict(),
|
||||
// ]
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//struct ToolState: PersistableState {
|
||||
//
|
||||
// static let identifier = "tool-state"
|
||||
// static let `default` = ToolState(location: .left, isVisible: false, dimension: 200)
|
||||
//
|
||||
// var location: WorkspaceBarLocation
|
||||
// var isVisible: Bool
|
||||
// var dimension: CGFloat
|
||||
//
|
||||
// init(location: WorkspaceBarLocation, isVisible: Bool, dimension: CGFloat) {
|
||||
// self.location = location
|
||||
// self.isVisible = isVisible
|
||||
// self.dimension = dimension
|
||||
// }
|
||||
//
|
||||
// init?(dict: [String: Any]) {
|
||||
// guard let locationRawValue = dict[ToolState.location] as? String,
|
||||
// let isVisible = PrefUtils.bool(from: dict, for: ToolState.isVisible),
|
||||
// let fDimension = PrefUtils.float(from: dict, for: ToolState.dimension)
|
||||
// else {
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// guard let location = PrefUtils.location(from: locationRawValue) else {
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// self.init(location: location, isVisible: isVisible, dimension: CGFloat(fDimension))
|
||||
// }
|
||||
//
|
||||
// func dict() -> [String: Any] {
|
||||
// return [
|
||||
// ToolState.location: PrefUtils.locationAsString(for: self.location),
|
||||
// ToolState.isVisible: self.isVisible,
|
||||
// ToolState.dimension: Float(self.dimension),
|
||||
// ]
|
||||
// }
|
||||
//
|
||||
// fileprivate static let location = "location"
|
||||
// fileprivate static let isVisible = "is-visible"
|
||||
// fileprivate static let dimension = "dimension"
|
||||
//}
|
||||
//
|
||||
//extension FileBrowserComponent {
|
||||
//
|
||||
// struct State: PersistableState {
|
||||
//
|
||||
// static let `default` = State(isShowHidden: false, toolState: ToolState.default)
|
||||
//
|
||||
// var isShowHidden: Bool
|
||||
// var toolState: ToolState
|
||||
//
|
||||
// init(isShowHidden: Bool, toolState: ToolState) {
|
||||
// self.isShowHidden = isShowHidden
|
||||
// self.toolState = toolState
|
||||
// }
|
||||
//
|
||||
// init?(dict: [String: Any]) {
|
||||
// guard let isShowHidden = PrefUtils.bool(from: dict, for: State.isShowHidden),
|
||||
// let toolStateDict = dict[ToolState.identifier] as? [String: Any]
|
||||
// else {
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// guard let toolState = ToolState(dict: toolStateDict) else {
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// self.init(isShowHidden: isShowHidden, toolState: toolState)
|
||||
// }
|
||||
//
|
||||
// func dict() -> [String: Any] {
|
||||
// return [
|
||||
// ToolState.identifier: self.toolState,
|
||||
// State.isShowHidden: self.isShowHidden,
|
||||
// ]
|
||||
// }
|
||||
//
|
||||
// fileprivate static let isShowHidden = "is-show-hidden"
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//extension BufferListComponent {
|
||||
//
|
||||
// struct State: PersistableState {
|
||||
//
|
||||
// static let `default` = State(toolState: ToolState.default)
|
||||
//
|
||||
// var toolState: ToolState
|
||||
//
|
||||
// init(toolState: ToolState) {
|
||||
// self.toolState = toolState
|
||||
// }
|
||||
//
|
||||
// init?(dict: [String: Any]) {
|
||||
// guard let toolStateDict = dict[ToolState.identifier] as? [String: Any] else {
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// guard let toolState = ToolState(dict: toolStateDict) else {
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// self.init(toolState: toolState)
|
||||
// }
|
||||
//
|
||||
// func dict() -> [String: Any] {
|
||||
// return [
|
||||
// ToolState.identifier: self.toolState,
|
||||
// ]
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//extension PreviewComponent {
|
||||
//
|
||||
// struct State: PersistableState {
|
||||
//
|
||||
// static let `default` = State(markdown: MarkdownRenderer.State.default, toolState: ToolState.default)
|
||||
//
|
||||
// var markdown: MarkdownRenderer.State
|
||||
// var toolState: ToolState
|
||||
//
|
||||
// init(markdown: MarkdownRenderer.State, toolState: ToolState) {
|
||||
// self.markdown = markdown
|
||||
// self.toolState = toolState
|
||||
// }
|
||||
//
|
||||
// init?(dict: [String: Any]) {
|
||||
// guard let markdownDict = dict[MarkdownRenderer.identifier] as? [String: Any],
|
||||
// let toolStateDict = dict[ToolState.identifier] as? [String: Any]
|
||||
// else {
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// guard let markdown = MarkdownRenderer.State(dict: markdownDict) else {
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// guard let toolState = ToolState(dict: toolStateDict) else {
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// self.init(markdown: markdown, toolState: toolState)
|
||||
// }
|
||||
//
|
||||
// func dict() -> [String: Any] {
|
||||
// return [
|
||||
// ToolState.identifier: self.toolState,
|
||||
// MarkdownRenderer.identifier: self.markdown.dict(),
|
||||
// ]
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//extension MarkdownRenderer {
|
||||
//
|
||||
// struct State: PersistableState {
|
||||
//
|
||||
// static let `default` = State(isForwardSearchAutomatically: false,
|
||||
// isReverseSearchAutomatically: false,
|
||||
// isRefreshOnWrite: true,
|
||||
// renderTime: Date.distantPast)
|
||||
//
|
||||
// var isForwardSearchAutomatically: Bool
|
||||
// var isReverseSearchAutomatically: Bool
|
||||
// var isRefreshOnWrite: Bool
|
||||
//
|
||||
// // transient
|
||||
// var renderTime: Date
|
||||
//
|
||||
// init(isForwardSearchAutomatically: Bool,
|
||||
// isReverseSearchAutomatically: Bool,
|
||||
// isRefreshOnWrite: Bool,
|
||||
// renderTime: Date) {
|
||||
// self.isForwardSearchAutomatically = isForwardSearchAutomatically
|
||||
// self.isReverseSearchAutomatically = isReverseSearchAutomatically
|
||||
// self.isRefreshOnWrite = isRefreshOnWrite
|
||||
// self.renderTime = renderTime
|
||||
// }
|
||||
//
|
||||
// init?(dict: [String: Any]) {
|
||||
// guard let isForward = PrefUtils.bool(from: dict, for: State.isForwardSearchAutomatically) else {
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// guard let isReverse = PrefUtils.bool(from: dict, for: State.isReverseSearchAutomatically) else {
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// guard let isRefreshOnWrite = PrefUtils.bool(from: dict, for: State.isRefreshOnWrite) else {
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// self.init(isForwardSearchAutomatically: isForward,
|
||||
// isReverseSearchAutomatically: isReverse,
|
||||
// isRefreshOnWrite: isRefreshOnWrite,
|
||||
// renderTime: Date.distantPast)
|
||||
// }
|
||||
//
|
||||
// func dict() -> [String: Any] {
|
||||
// return [
|
||||
// State.isForwardSearchAutomatically: self.isForwardSearchAutomatically,
|
||||
// State.isReverseSearchAutomatically: self.isReverseSearchAutomatically,
|
||||
// State.isRefreshOnWrite: self.isRefreshOnWrite,
|
||||
// ]
|
||||
// }
|
||||
//
|
||||
// fileprivate static let isForwardSearchAutomatically = "is-forward-search-automatically"
|
||||
// fileprivate static let isReverseSearchAutomatically = "is-reverse-search-automatically"
|
||||
// fileprivate static let isRefreshOnWrite = "is-refresh-on-write"
|
||||
// }
|
||||
//}
|
||||
|
@ -1,28 +1,29 @@
|
||||
//
|
||||
// Created by Tae Won Ha on 1/16/17.
|
||||
// Copyright (c) 2017 Tae Won Ha. All rights reserved.
|
||||
//
|
||||
/**
|
||||
* Tae Won Ha - http://taewon.de - @hataewon
|
||||
* See LICENSE
|
||||
*/
|
||||
|
||||
import Cocoa
|
||||
import RxSwift
|
||||
|
||||
class UiRoot: UiComponent {
|
||||
|
||||
typealias StateType = MainWindowStates
|
||||
typealias StateType = AppState
|
||||
|
||||
required init(source: Observable<StateType>, emitter: ActionEmitter, state: StateType) {
|
||||
required init(source: StateSource, emitter: ActionEmitter, state: StateType) {
|
||||
self.source = source
|
||||
self.emitter = emitter
|
||||
|
||||
source
|
||||
.mapOmittingNil { $0 as? StateType }
|
||||
.observeOn(MainScheduler.instance)
|
||||
.subscribe(onNext: { [unowned self] state in
|
||||
let keys = Set(self.mainWindows.keys)
|
||||
let keysInState = Set(state.current.keys)
|
||||
let keysInState = Set(state.mainWindows.keys)
|
||||
|
||||
keysInState
|
||||
.subtracting(self.mainWindows.keys)
|
||||
.flatMap { state.current[$0] }
|
||||
.flatMap { state.mainWindows[$0] }
|
||||
.forEach(self.createNewMainWindow)
|
||||
|
||||
keys
|
||||
@ -36,15 +37,13 @@ class UiRoot: UiComponent {
|
||||
}
|
||||
|
||||
fileprivate func createNewMainWindow(with state: MainWindow.State) {
|
||||
let mainWindow = MainWindow(source: self.source.mapOmittingNil { $0.current[state.uuid] },
|
||||
emitter: self.emitter,
|
||||
state: state)
|
||||
let mainWindow = MainWindow(source: source, emitter: self.emitter, state: state)
|
||||
self.mainWindows[state.uuid] = mainWindow
|
||||
|
||||
mainWindow.show()
|
||||
}
|
||||
|
||||
fileprivate let source: Observable<StateType>
|
||||
fileprivate let source: StateSource
|
||||
fileprivate let emitter: ActionEmitter
|
||||
fileprivate let disposeBag = DisposeBag()
|
||||
|
||||
|
@ -1,34 +1,34 @@
|
||||
//
|
||||
// Created by Tae Won Ha on 1/17/17.
|
||||
// Copyright (c) 2017 Tae Won Ha. All rights reserved.
|
||||
//
|
||||
/**
|
||||
* Tae Won Ha - http://taewon.de - @hataewon
|
||||
* See LICENSE
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import RxSwift
|
||||
|
||||
class UiRootTransformer: Transformer {
|
||||
|
||||
typealias Pair = StateActionPair<MainWindowStates, UuidAction<MainWindow.Action>>
|
||||
typealias Pair = StateActionPair<AppState, UuidAction<MainWindow.Action>>
|
||||
|
||||
func transform(_ source: Observable<Pair>) -> Observable<Pair> {
|
||||
return source.map { pair in
|
||||
var state = pair.state
|
||||
var appState = pair.state
|
||||
let uuid = pair.action.uuid
|
||||
|
||||
switch pair.action.payload {
|
||||
|
||||
case .becomeKey:
|
||||
state.last = state.current[uuid] ?? state.last
|
||||
appState.currentMainWindow = appState.mainWindows[uuid] ?? appState.currentMainWindow
|
||||
|
||||
case .close:
|
||||
state.current.removeValue(forKey: uuid)
|
||||
appState.mainWindows.removeValue(forKey: uuid)
|
||||
|
||||
default:
|
||||
break
|
||||
return pair
|
||||
|
||||
}
|
||||
|
||||
return StateActionPair(state: state, action: pair.action)
|
||||
return StateActionPair(state: appState, action: pair.action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user