1
1
mirror of https://github.com/qvacua/vimr.git synced 2024-11-28 02:54:31 +03:00

Experimenting further...

This commit is contained in:
Tae Won Ha 2017-01-22 14:22:05 +01:00
parent 7a0610dee2
commit ea569658e3
No known key found for this signature in database
GPG Key ID: E40743465B5B8B44
16 changed files with 290 additions and 649 deletions

View File

@ -17,14 +17,15 @@
1929B3F5743967125F357C9F /* Matcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BEEB33113B0E33C3830F /* Matcher.swift */; }; 1929B3F5743967125F357C9F /* Matcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BEEB33113B0E33C3830F /* Matcher.swift */; };
1929B4145AA81F006BAF3B5C /* PreviewService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BB8BCA48637156F92945 /* PreviewService.swift */; }; 1929B4145AA81F006BAF3B5C /* PreviewService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BB8BCA48637156F92945 /* PreviewService.swift */; };
1929B462CD4935AFF6D69457 /* FileItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B7CB4863F80230C32D3C /* FileItem.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 */; }; 1929B4FEE6EB56EF3F56B805 /* Context.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B34FC23D805A8B29E8F7 /* Context.swift */; };
1929B53876E6952D378C2B30 /* ScoredFileItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BDF9EBAF1D9D44399045 /* ScoredFileItem.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 */; }; 1929B6388EAF16C190B82955 /* FileItemIgnorePattern.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B69499B2569793350CEC /* FileItemIgnorePattern.swift */; };
1929B67DA3EB21A631EF1DBB /* FileUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BA8AC40B901B20F20B71 /* FileUtils.swift */; }; 1929B67DA3EB21A631EF1DBB /* FileUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BA8AC40B901B20F20B71 /* FileUtils.swift */; };
1929B728262BAA14FC93F6AC /* NeoVimView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BF00B466B40629C2AABE /* NeoVimView.swift */; }; 1929B728262BAA14FC93F6AC /* NeoVimView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BF00B466B40629C2AABE /* NeoVimView.swift */; };
1929B73E5EC0B108B83F82EB /* FileItemService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B3A98687DF171307AAC8 /* FileItemService.swift */; }; 1929B73E5EC0B108B83F82EB /* FileItemService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B3A98687DF171307AAC8 /* FileItemService.swift */; };
1929B7A2F2B423AA9740FD45 /* FileUtilsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B5D977261F1EBFA9E8F1 /* FileUtilsTest.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 */; }; 1929B93DBAD09835E428F610 /* PrefPane.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BB251F74BEFC82CEEF84 /* PrefPane.swift */; };
1929BA120290D6A2A61A4468 /* ArrayCommonsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B477E1E62433BC48E10B /* ArrayCommonsTest.swift */; }; 1929BA120290D6A2A61A4468 /* ArrayCommonsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B477E1E62433BC48E10B /* ArrayCommonsTest.swift */; };
1929BA3BB94B77E9AE051FE5 /* PreviewComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B8DA5AA33536F0082200 /* PreviewComponent.swift */; }; 1929BA3BB94B77E9AE051FE5 /* PreviewComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B8DA5AA33536F0082200 /* PreviewComponent.swift */; };
@ -289,7 +290,6 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
1929B02440BC99C42F9EBD45 /* NetUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NetUtils.m; sourceTree = "<group>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 1929BF00B466B40629C2AABE /* NeoVimView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NeoVimView.swift; sourceTree = "<group>"; };
@ -517,13 +519,14 @@
/* End PBXFrameworksBuildPhase section */ /* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
1929B32401E8914DE9BF76CA /* UI */ = { 1929B32401E8914DE9BF76CA /* Components */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
1929BD8CBADC191CF8C85309 /* MainWindow.swift */, 1929BD8CBADC191CF8C85309 /* MainWindow.swift */,
1929BD4149D5A25C82064DD8 /* UiRoot.swift */, 1929BD4149D5A25C82064DD8 /* UiRoot.swift */,
1929B6C6C7792B05164B0216 /* PreviewTool.swift */,
); );
name = UI; name = Components;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
1929B41F745CDCDFE09ACDCF /* resources */ = { 1929B41F745CDCDFE09ACDCF /* resources */ = {
@ -540,6 +543,7 @@
1929B7A68B7109CEFAF105E8 /* AppDelegateTransformer.swift */, 1929B7A68B7109CEFAF105E8 /* AppDelegateTransformer.swift */,
1929B457B9D0FA4D21F3751E /* UiRootTransformer.swift */, 1929B457B9D0FA4D21F3751E /* UiRootTransformer.swift */,
1929BD83A13BF133741766CC /* MainWindowTransformer.swift */, 1929BD83A13BF133741766CC /* MainWindowTransformer.swift */,
1929BE37AA2843779CAFA76F /* PreviewTransformer.swift */,
); );
name = Transformers; name = Transformers;
sourceTree = "<group>"; sourceTree = "<group>";
@ -559,7 +563,7 @@
children = ( children = (
1929BB6608B4F0E037CA0F4C /* States.swift */, 1929BB6608B4F0E037CA0F4C /* States.swift */,
1929B34FC23D805A8B29E8F7 /* Context.swift */, 1929B34FC23D805A8B29E8F7 /* Context.swift */,
1929B32401E8914DE9BF76CA /* UI */, 1929B32401E8914DE9BF76CA /* Components */,
1929B5E773BDB3B4EE9D00C1 /* Transformers */, 1929B5E773BDB3B4EE9D00C1 /* Transformers */,
); );
name = Redesign; name = Redesign;
@ -858,7 +862,6 @@
4BDF50101D760AB700D8FBC3 /* Commons */, 4BDF50101D760AB700D8FBC3 /* Commons */,
4B6423941D8EFD6100FC78C8 /* Workspace */, 4B6423941D8EFD6100FC78C8 /* Workspace */,
4B97E2CF1D33F92200FC0660 /* resources */, 4B97E2CF1D33F92200FC0660 /* resources */,
1929B0D767ED19EE1281ECD9 /* HttpServerService.swift */,
1929BA652D3B88FC071531EC /* Redesign */, 1929BA652D3B88FC071531EC /* Redesign */,
); );
path = VimR; path = VimR;
@ -1354,7 +1357,6 @@
1929B4145AA81F006BAF3B5C /* PreviewService.swift in Sources */, 1929B4145AA81F006BAF3B5C /* PreviewService.swift in Sources */,
1929BD52275A6570C666A7BA /* PreviewRenderer.swift in Sources */, 1929BD52275A6570C666A7BA /* PreviewRenderer.swift in Sources */,
1929BD2F41D93ADFF43C1C98 /* NetUtils.m in Sources */, 1929BD2F41D93ADFF43C1C98 /* NetUtils.m in Sources */,
1929B5CF6ECCBCC3FB5292CE /* HttpServerService.swift in Sources */,
1929BE0DAEE9664C5BCFA211 /* States.swift in Sources */, 1929BE0DAEE9664C5BCFA211 /* States.swift in Sources */,
1929B4FEE6EB56EF3F56B805 /* Context.swift in Sources */, 1929B4FEE6EB56EF3F56B805 /* Context.swift in Sources */,
1929B0FF696312F754BC96E2 /* MainWindow.swift in Sources */, 1929B0FF696312F754BC96E2 /* MainWindow.swift in Sources */,
@ -1362,6 +1364,8 @@
1929BAFF1E011321D3186EE6 /* UiRoot.swift in Sources */, 1929BAFF1E011321D3186EE6 /* UiRoot.swift in Sources */,
1929B29B95AD176D57942E08 /* UiRootTransformer.swift in Sources */, 1929B29B95AD176D57942E08 /* UiRootTransformer.swift in Sources */,
1929BB4A9B2FA42A64CCCC76 /* MainWindowTransformer.swift in Sources */, 1929BB4A9B2FA42A64CCCC76 /* MainWindowTransformer.swift in Sources */,
1929B8FB248D71BF88A35761 /* PreviewTool.swift in Sources */,
1929B4B70926DE113E6BF990 /* PreviewTransformer.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };

View File

@ -50,10 +50,10 @@ class AppDelegate: NSObject, NSApplicationDelegate {
fileprivate var launching = true fileprivate var launching = true
override init() { override init() {
let source = self.stateContext.stateSource.mapOmittingNil { $0 as? MainWindowStates } let source = self.stateContext.stateSource
self.uiRoot = UiRoot(source: source, self.uiRoot = UiRoot(source: source,
emitter: self.stateContext.actionEmitter, emitter: self.stateContext.actionEmitter,
state: AppState.default.mainWindows) state: AppState.default) // FIXME
self.actionSink = self.actionSubject.asObservable() self.actionSink = self.actionSubject.asObservable()

View File

@ -1,14 +1,14 @@
// /**
// Created by Tae Won Ha on 1/16/17. * Tae Won Ha - http://taewon.de - @hataewon
// Copyright (c) 2017 Tae Won Ha. All rights reserved. * See LICENSE
// */
import Foundation import Foundation
import RxSwift import RxSwift
class AppDelegateTransformer: Transformer { class AppDelegateTransformer: Transformer {
typealias Pair = StateActionPair<MainWindowStates, AppDelegate.Action> typealias Pair = StateActionPair<AppState, AppDelegate.Action>
func transform(_ source: Observable<Pair>) -> Observable<Pair> { func transform(_ source: Observable<Pair>) -> Observable<Pair> {
return source.map { pair in return source.map { pair in
@ -17,12 +17,13 @@ class AppDelegateTransformer: Transformer {
case let .newMainWindow(urls, cwd): case let .newMainWindow(urls, cwd):
var state = pair.state var state = pair.state
var mainWindow = state.last var mainWindow = state.currentMainWindow
mainWindow.uuid = UUID().uuidString mainWindow.uuid = UUID().uuidString
mainWindow.serverBaseUrl = state.baseServerUrl.appendingPathComponent("\(mainWindow.uuid)")
mainWindow.urlsToOpen = urls.toDict { url in MainWindow.OpenMode.default } mainWindow.urlsToOpen = urls.toDict { url in MainWindow.OpenMode.default }
mainWindow.cwd = cwd mainWindow.cwd = cwd
state.current[mainWindow.uuid] = mainWindow state.mainWindows[mainWindow.uuid] = mainWindow
return StateActionPair(state: state, action: pair.action) return StateActionPair(state: state, action: pair.action)

View File

@ -1,22 +1,11 @@
// /**
// Created by Tae Won Ha on 1/16/17. * Tae Won Ha - http://taewon.de - @hataewon
// Copyright (c) 2017 Tae Won Ha. All rights reserved. * See LICENSE
// */
import Foundation import Foundation
import RxSwift 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 { class StateContext {
let stateSource: Observable<Any> let stateSource: Observable<Any>
@ -26,57 +15,56 @@ class StateContext {
self.stateSource = self.stateSubject.asObservable() self.stateSource = self.stateSubject.asObservable()
let actionSource = self.actionEmitter.observable let actionSource = self.actionEmitter.observable
actionSource self.previewTransformer = PreviewTransformer(port: in_port_t(self.appState.baseServerUrl.port ?? 0))
.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)
actionSource Observable
.mapOmittingNil { $0 as? UuidAction<MainWindow.Action> } .of(
.map { StateActionPair(state: self.appState.mainWindows, action: $0) } actionSource
.transform(by: self.uiRootTransformer) .mapOmittingNil { $0 as? AppDelegate.Action }
.map { $0.state } .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 .subscribe(onNext: { state in
self.appState.mainWindows = state self.appState = state
self.stateSubject.onNext(state) self.stateSubject.onNext(self.appState)
}) })
.addDisposableTo(self.disposeBag) .addDisposableTo(self.disposeBag)
actionSource actionSource
.mapOmittingNil { $0 as? UuidAction<MainWindow.Action> } .mapOmittingNil { $0 as? UuidAction<MainWindow.Action> }
.mapOmittingNil { action in .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 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.mainWindowTransformer)
.transform(by: self.previewTransformer)
.filter { $0.modified }
.subscribe(onNext: { pair in .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) self.stateSubject.onNext(pair.state)
}) })
.addDisposableTo(self.disposeBag) .addDisposableTo(self.disposeBag)
actionSource actionSource.debug().subscribe().addDisposableTo(self.disposeBag)
.subscribe(onNext: { action in stateSource.debug().subscribe().addDisposableTo(self.disposeBag)
NSLog("ACTION: \(action)")
})
.addDisposableTo(self.disposeBag)
stateSource
.subscribe(onNext: { state in
NSLog("STATE : \(self.appState.mainWindows.current)")
})
.addDisposableTo(self.disposeBag)
} }
fileprivate let stateSubject = PublishSubject<Any>() fileprivate let stateSubject = PublishSubject<Any>()
fileprivate let scheduler = SerialDispatchQueueScheduler(qos: .userInitiated)
fileprivate let disposeBag = DisposeBag() fileprivate let disposeBag = DisposeBag()
fileprivate var appState = AppState.default fileprivate var appState = AppState.default
@ -84,15 +72,16 @@ class StateContext {
fileprivate let appDelegateTransformer = AppDelegateTransformer() fileprivate let appDelegateTransformer = AppDelegateTransformer()
fileprivate let uiRootTransformer = UiRootTransformer() fileprivate let uiRootTransformer = UiRootTransformer()
fileprivate let mainWindowTransformer = MainWindowTransformer() fileprivate let mainWindowTransformer = MainWindowTransformer()
fileprivate let previewTransformer: PreviewTransformer
} }
extension Observable { 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) } 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) return transformer.transform(self)
} }
} }

View File

@ -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()
}

View File

@ -1,7 +1,7 @@
// /**
// Created by Tae Won Ha on 1/16/17. * Tae Won Ha - http://taewon.de - @hataewon
// Copyright (c) 2017 Tae Won Ha. All rights reserved. * See LICENSE
// */
import Cocoa import Cocoa
import RxSwift import RxSwift
@ -12,7 +12,28 @@ protocol UiComponent {
associatedtype StateType 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, class MainWindow: NSObject,
@ -27,8 +48,13 @@ class MainWindow: NSObject,
case cd(to: URL) case cd(to: URL)
case setBufferList([NeoVimBuffer]) case setBufferList([NeoVimBuffer])
case setCurrentBuffer(NeoVimBuffer)
case becomeKey case becomeKey
case scroll(to: Position)
case setCursor(to: Position)
case close case close
} }
@ -41,7 +67,7 @@ class MainWindow: NSObject,
case verticalSplit case verticalSplit
} }
required init(source: Observable<StateType>, emitter: ActionEmitter, state: StateType) { required init(source: StateSource, emitter: ActionEmitter, state: StateType) {
self.uuid = state.uuid self.uuid = state.uuid
self.emitter = emitter self.emitter = emitter
@ -54,11 +80,27 @@ class MainWindow: NSObject,
self.windowController = NSWindowController(windowNibName: "MainWindow") self.windowController = NSWindowController(windowNibName: "MainWindow")
super.init() 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.addViews()
self.windowController.window?.delegate = self self.windowController.window?.delegate = self
source source
.mapOmittingNil { $0 as? UuidState<StateType> }
.filter { $0.uuid == state.uuid }
.debug()
.observeOn(MainScheduler.instance) .observeOn(MainScheduler.instance)
.subscribe(onNext: { [unowned self] state in .subscribe(onNext: { [unowned self] state in
}) })
@ -123,6 +165,13 @@ class MainWindow: NSObject,
fileprivate let workspace: Workspace fileprivate let workspace: Workspace
fileprivate let neoVimView: NeoVimView 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 // MARK: - NeoVimViewDelegate
@ -141,24 +190,24 @@ extension MainWindow {
} }
func cwdChanged() { 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() { func bufferListChanged() {
let buffers = self.neoVimView.allBuffers() 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) { func currentBufferChanged(_ currentBuffer: NeoVimBuffer) {
// self.publish(event: MainWindowAction.currentBufferChanged(mainWindow: self, buffer: currentBuffer)) self.emitter.emit(self.uuidAction(for: .setCurrentBuffer(currentBuffer)))
} }
func tabChanged() { func tabChanged() {
// guard let currentBuffer = self.neoVimView.currentBuffer() else { guard let currentBuffer = self.neoVimView.currentBuffer() else {
// return return
// } }
//
// self.publish(event: MainWindowAction.currentBufferChanged(mainWindow: self, buffer: currentBuffer)) self.currentBufferChanged(currentBuffer)
} }
func ipcBecameInvalid(reason: String) { func ipcBecameInvalid(reason: String) {
@ -174,11 +223,11 @@ extension MainWindow {
} }
func scroll() { 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) { 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 { extension MainWindow {
func windowDidBecomeKey(_: Notification) { func windowDidBecomeKey(_: Notification) {
self.emitter.emit(UuidAction(uuid: self.uuid, action: Action.becomeKey)) self.emitter.emit(self.uuidAction(for: .becomeKey))
} }
func windowWillClose(_: Notification) { func windowWillClose(_: Notification) {
self.emitter.emit(UuidAction(uuid: self.uuid, action: Action.close)) self.emitter.emit(self.uuidAction(for: .close))
} }
func windowShouldClose(_: Any) -> Bool { func windowShouldClose(_: Any) -> Bool {

View File

@ -110,7 +110,7 @@ class MainWindowComponent: WindowComponent,
fileprivate let scrollFlow: EmbeddableComponent fileprivate let scrollFlow: EmbeddableComponent
fileprivate let httpServer: HttpServer; fileprivate let httpServer: Swifter.HttpServer;
// MARK: - API // MARK: - API
var uuid: String { var uuid: String {
@ -144,7 +144,7 @@ class MainWindowComponent: WindowComponent,
fileItemService: FileItemService, fileItemService: FileItemService,
cwd: URL, cwd: URL,
urls: [URL] = [], urls: [URL] = [],
httpServer: HttpServer, httpServer: Swifter.HttpServer,
initialData: PrefData) initialData: PrefData)
{ {
self.neoVimView = NeoVimView(frame: CGRect.zero, self.neoVimView = NeoVimView(frame: CGRect.zero,

View File

@ -20,7 +20,7 @@ class MainWindowManager: StandardFlow {
fileprivate let fileItemService: FileItemService fileprivate let fileItemService: FileItemService
fileprivate var data: PrefData 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) { init(source: Observable<Any>, fileItemService: FileItemService, initialData: PrefData) {
self.fileItemService = fileItemService self.fileItemService = fileItemService

View File

@ -1,7 +1,7 @@
// /**
// Created by Tae Won Ha on 1/17/17. * Tae Won Ha - http://taewon.de - @hataewon
// Copyright (c) 2017 Tae Won Ha. All rights reserved. * See LICENSE
// */
import Foundation import Foundation
import RxSwift import RxSwift
@ -27,8 +27,11 @@ class MainWindowTransformer: Transformer {
.forEach { state.urlsToOpen.removeValue(forKey: $0) } .forEach { state.urlsToOpen.removeValue(forKey: $0) }
state.buffers = buffers state.buffers = buffers
case let .setCurrentBuffer(buffer):
state.currentBuffer = buffer
default: default:
break return pair
} }

View File

@ -149,7 +149,7 @@ class MarkdownRenderer: NSObject, Flow, PreviewRenderer {
fileprivate var currentPreviewPosition = Position(row: 1, column: 1) fileprivate var currentPreviewPosition = Position(row: 1, column: 1)
fileprivate let uuid = UUID().uuidString fileprivate let uuid = UUID().uuidString
fileprivate let httpServer: HttpServer fileprivate let httpServer: Swifter.HttpServer
fileprivate let port: Int fileprivate let port: Int
fileprivate let tempDir = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) fileprivate let tempDir = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
@ -171,7 +171,7 @@ class MarkdownRenderer: NSObject, Flow, PreviewRenderer {
let toolbar: NSView? = NSView(forAutoLayout: ()) let toolbar: NSView? = NSView(forAutoLayout: ())
let menuItems: [NSMenuItem]? 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) \(uuid)")
NSLog("\(#function) \(self.tempDir)") NSLog("\(#function) \(self.tempDir)")

View File

@ -113,7 +113,7 @@ class PreviewComponent: NSView, ViewComponent, ToolDataHolder, WKNavigationDeleg
} }
} }
fileprivate let httpServer: HttpServer fileprivate let httpServer: Swifter.HttpServer
required init?(coder: NSCoder) { required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
@ -140,7 +140,7 @@ class PreviewComponent: NSView, ViewComponent, ToolDataHolder, WKNavigationDeleg
return self 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.flow = EmbeddableComponent(source: source)
self.httpServer = httpServer self.httpServer = httpServer

52
VimR/PreviewTool.swift Normal file
View 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")
}
}

View 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()
}

View File

@ -1,12 +1,13 @@
// /**
// Created by Tae Won Ha on 1/13/17. * Tae Won Ha - http://taewon.de - @hataewon
// Copyright (c) 2017 Tae Won Ha. All rights reserved. * See LICENSE
// */
import Foundation import Foundation
import RxSwift import RxSwift
typealias ActionEmitter = Emitter<Any> typealias ActionEmitter = Emitter<Any>
typealias StateSource = Observable<Any>
class Emitter<T> { class Emitter<T> {
@ -30,10 +31,12 @@ class Emitter<T> {
class StateActionPair<S, A> { class StateActionPair<S, A> {
let modified: Bool
let state: S let state: S
let action: A let action: A
init(state: S, action: A) { init(state: S, action: A, modified: Bool = true) {
self.modified = modified
self.state = state self.state = state
self.action = action 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) { let baseServerUrl: URL
self.last = last
self.current = [:] 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 { 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) isToolButtonsVisible: true)
var isAllToolsVisible = true var isAllToolsVisible = true
var isToolButtonsVisible = true var isToolButtonsVisible = true
// transient ////// transient
// neovim
var uuid = UUID().uuidString var uuid = UUID().uuidString
var currentBuffer: NeoVimBuffer?
var buffers = [NeoVimBuffer]() var buffers = [NeoVimBuffer]()
var cwd = FileUtils.userHomeUrl var cwd = FileUtils.userHomeUrl
@ -338,291 +131,16 @@ extension MainWindow {
var isUseLigatures = false var isUseLigatures = false
var isUseInteractiveZsh = false var isUseInteractiveZsh = false
var serverBaseUrl: URL
// transient^2 // transient^2
var urlsToOpen = [URL: OpenMode]() var urlsToOpen = [URL: OpenMode]()
init(isAllToolsVisible: Bool, isToolButtonsVisible: Bool) { init(serverBaseUrl: URL, isAllToolsVisible: Bool, isToolButtonsVisible: Bool) {
self.serverBaseUrl = serverBaseUrl
self.isAllToolsVisible = isAllToolsVisible self.isAllToolsVisible = isAllToolsVisible
self.isToolButtonsVisible = isToolButtonsVisible 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"
// }
//}

View File

@ -1,28 +1,29 @@
// /**
// Created by Tae Won Ha on 1/16/17. * Tae Won Ha - http://taewon.de - @hataewon
// Copyright (c) 2017 Tae Won Ha. All rights reserved. * See LICENSE
// */
import Cocoa import Cocoa
import RxSwift import RxSwift
class UiRoot: UiComponent { 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.source = source
self.emitter = emitter self.emitter = emitter
source source
.mapOmittingNil { $0 as? StateType }
.observeOn(MainScheduler.instance) .observeOn(MainScheduler.instance)
.subscribe(onNext: { [unowned self] state in .subscribe(onNext: { [unowned self] state in
let keys = Set(self.mainWindows.keys) let keys = Set(self.mainWindows.keys)
let keysInState = Set(state.current.keys) let keysInState = Set(state.mainWindows.keys)
keysInState keysInState
.subtracting(self.mainWindows.keys) .subtracting(self.mainWindows.keys)
.flatMap { state.current[$0] } .flatMap { state.mainWindows[$0] }
.forEach(self.createNewMainWindow) .forEach(self.createNewMainWindow)
keys keys
@ -36,15 +37,13 @@ class UiRoot: UiComponent {
} }
fileprivate func createNewMainWindow(with state: MainWindow.State) { fileprivate func createNewMainWindow(with state: MainWindow.State) {
let mainWindow = MainWindow(source: self.source.mapOmittingNil { $0.current[state.uuid] }, let mainWindow = MainWindow(source: source, emitter: self.emitter, state: state)
emitter: self.emitter,
state: state)
self.mainWindows[state.uuid] = mainWindow self.mainWindows[state.uuid] = mainWindow
mainWindow.show() mainWindow.show()
} }
fileprivate let source: Observable<StateType> fileprivate let source: StateSource
fileprivate let emitter: ActionEmitter fileprivate let emitter: ActionEmitter
fileprivate let disposeBag = DisposeBag() fileprivate let disposeBag = DisposeBag()

View File

@ -1,34 +1,34 @@
// /**
// Created by Tae Won Ha on 1/17/17. * Tae Won Ha - http://taewon.de - @hataewon
// Copyright (c) 2017 Tae Won Ha. All rights reserved. * See LICENSE
// */
import Foundation import Foundation
import RxSwift import RxSwift
class UiRootTransformer: Transformer { class UiRootTransformer: Transformer {
typealias Pair = StateActionPair<MainWindowStates, UuidAction<MainWindow.Action>> typealias Pair = StateActionPair<AppState, UuidAction<MainWindow.Action>>
func transform(_ source: Observable<Pair>) -> Observable<Pair> { func transform(_ source: Observable<Pair>) -> Observable<Pair> {
return source.map { pair in return source.map { pair in
var state = pair.state var appState = pair.state
let uuid = pair.action.uuid let uuid = pair.action.uuid
switch pair.action.payload { switch pair.action.payload {
case .becomeKey: case .becomeKey:
state.last = state.current[uuid] ?? state.last appState.currentMainWindow = appState.mainWindows[uuid] ?? appState.currentMainWindow
case .close: case .close:
state.current.removeValue(forKey: uuid) appState.mainWindows.removeValue(forKey: uuid)
default: default:
break return pair
} }
return StateActionPair(state: state, action: pair.action) return StateActionPair(state: appState, action: pair.action)
} }
} }
} }