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

GH-383 Add stub HTML preview tool

This commit is contained in:
Tae Won Ha 2017-03-24 17:47:08 +01:00
parent ad15f17298
commit c26ea812d5
No known key found for this signature in database
GPG Key ID: E40743465B5B8B44
7 changed files with 239 additions and 14 deletions

View File

@ -26,6 +26,7 @@
1929B3CEE0C1A1850E9CCE2F /* BasicTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B2FBE11048569391E092 /* BasicTypes.swift */; };
1929B3F5743967125F357C9F /* Matcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BEEB33113B0E33C3830F /* Matcher.swift */; };
1929B462CD4935AFF6D69457 /* FileItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B7CB4863F80230C32D3C /* FileItem.swift */; };
1929B4B00D7BB191A9A6532D /* HtmlPreviewToolReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BE5AEA3D0980860EED50 /* HtmlPreviewToolReducer.swift */; };
1929B4B70926DE113E6BF990 /* PreviewReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BE37AA2843779CAFA76F /* PreviewReducer.swift */; };
1929B4E54E2F13A7F5F2B682 /* OpenedFileListReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B67A10E6BB2986B2416E /* OpenedFileListReducer.swift */; };
1929B4F0612224E594E89B92 /* AppearancePref.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B0FBFB766042CF06E463 /* AppearancePref.swift */; };
@ -67,6 +68,7 @@
1929BEDE1BE950EDA9497363 /* GeneralPref.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BB55946DAEBF55D24048 /* GeneralPref.swift */; };
1929BEFEABA0448306CDB6D4 /* FileItemIgnorePatternTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BBC84557C8351EC6183E /* FileItemIgnorePatternTest.swift */; };
1929BF81A40B4154D3EA33CE /* server_ui.m in Sources */ = {isa = PBXBuildFile; fileRef = 1929B93013228985F509C8F6 /* server_ui.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
1929BFDE22D155F7C4B19E96 /* HtmlPreviewTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B85023B042C485409CE1 /* HtmlPreviewTool.swift */; };
4B029F1A1D45E349004EE0D3 /* PrefWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B029F1C1D45E349004EE0D3 /* PrefWindow.xib */; };
4B0BCC941D70320C00D3CE65 /* Logger.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B0BCC931D70320C00D3CE65 /* Logger.h */; settings = {ATTRIBUTES = (Private, ); }; };
4B183E0E1E06E2940079E8A8 /* CocoaMarkdown.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B183E0D1E06E2940079E8A8 /* CocoaMarkdown.framework */; };
@ -328,6 +330,7 @@
1929B71A92C24FEFE79A851E /* OpenQuicklyWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenQuicklyWindow.swift; sourceTree = "<group>"; };
1929B7A68B7109CEFAF105E8 /* AppDelegateReducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegateReducer.swift; sourceTree = "<group>"; };
1929B7CB4863F80230C32D3C /* FileItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileItem.swift; sourceTree = "<group>"; };
1929B85023B042C485409CE1 /* HtmlPreviewTool.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HtmlPreviewTool.swift; sourceTree = "<group>"; };
1929B8EF9A9F5ACC175452BD /* PreviewUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreviewUtils.swift; sourceTree = "<group>"; };
1929B93013228985F509C8F6 /* server_ui.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = server_ui.m; sourceTree = "<group>"; };
1929B9AF20D7BD6E5C975128 /* FoundationCommons.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FoundationCommons.swift; sourceTree = "<group>"; };
@ -352,6 +355,7 @@
1929BDF9EBAF1D9D44399045 /* ScoredFileItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScoredFileItem.swift; sourceTree = "<group>"; };
1929BE168F31344B69E61B62 /* PrefWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrefWindow.swift; sourceTree = "<group>"; };
1929BE37AA2843779CAFA76F /* PreviewReducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreviewReducer.swift; sourceTree = "<group>"; };
1929BE5AEA3D0980860EED50 /* HtmlPreviewToolReducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HtmlPreviewToolReducer.swift; sourceTree = "<group>"; };
1929BE69CF9AB1A10D0DD4F2 /* CocoaCategories.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CocoaCategories.h; sourceTree = "<group>"; };
1929BED01F5D94BFCA4CF80F /* AppearancePrefReducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppearancePrefReducer.swift; sourceTree = "<group>"; };
1929BEDE7F92BC7B49E802AF /* OpenQuicklyFilterOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenQuicklyFilterOperation.swift; sourceTree = "<group>"; };
@ -552,6 +556,7 @@
1929BC56ADBA3275E7A0A598 /* Preferences */,
1929B1645977E0AEFE53193D /* File Browser */,
1929BD9EEC30C0A498877E5B /* Open Quickly */,
1929B85023B042C485409CE1 /* HtmlPreviewTool.swift */,
);
name = Components;
sourceTree = "<group>";
@ -577,6 +582,7 @@
1929BDC8F5D48578A90236E9 /* FileBrowserReducer.swift */,
1929B67A10E6BB2986B2416E /* OpenedFileListReducer.swift */,
1929BB4CF1C1FFEE6CCDD6FD /* Preferences */,
1929BE5AEA3D0980860EED50 /* HtmlPreviewToolReducer.swift */,
);
name = Transformers;
sourceTree = "<group>";
@ -1374,6 +1380,8 @@
1929B71610FF1DC6E459BA49 /* PreviewUtils.swift in Sources */,
1929B08C6230B9C5AB72DAF1 /* Pref128ToCurrentConverter.swift in Sources */,
1929B94083273D4B321AD848 /* FileItemUtils.swift in Sources */,
1929BFDE22D155F7C4B19E96 /* HtmlPreviewTool.swift in Sources */,
1929B4B00D7BB191A9A6532D /* HtmlPreviewToolReducer.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -61,6 +61,8 @@ class Context {
})
.addDisposableTo(self.disposeBag)
self.httpService = HttpService(port: baseServerUrl.port!)
// MainWindow.State
Observable
.of(
@ -71,7 +73,7 @@ class Context {
.transform(by: previewTransformer.forMainWindow)
.filter { $0.modified }
.apply(to: previewService.forMainWindow)
.apply(to: HttpServerService(port: baseServerUrl.port ?? 0))
.apply(to: self.httpService.forMainWindow)
.map { $0.state },
actionSource
.mapOmittingNil { $0 as? UuidAction<PreviewTool.Action> }
@ -79,6 +81,12 @@ class Context {
.transform(by: PreviewToolTransformer(baseServerUrl: baseServerUrl))
.filter { $0.modified }
.map { $0.state },
actionSource
.mapOmittingNil { $0 as? UuidAction<HtmlPreviewTool.Action> }
.mapOmittingNil { self.mainWindowStateActionPair(for: $0) }
.transform(by: self.httpService.forHtmlPreviewTool)
.filter { $0.modified }
.map { $0.state },
actionSource
.mapOmittingNil { $0 as? UuidAction<FileBrowser.Action> }
.mapOmittingNil { self.mainWindowStateActionPair(for: $0) }
@ -150,6 +158,8 @@ class Context {
self.stateSubject.onCompleted()
}
fileprivate let httpService: HttpService
fileprivate let stateSubject = PublishSubject<AppState>()
fileprivate let scheduler = SerialDispatchQueueScheduler(qos: .userInitiated)
fileprivate let disposeBag = DisposeBag()

118
VimR/HtmlPreviewTool.swift Normal file
View File

@ -0,0 +1,118 @@
/**
* Tae Won Ha - http://taewon.de - @hataewon
* See LICENSE
*/
import Cocoa
import RxSwift
import PureLayout
import WebKit
import Swifter
class HtmlPreviewTool: NSView, UiComponent {
enum Action {
case selectHtmlFile(URL)
}
typealias StateType = MainWindow.State
let innerCustomToolbar = InnerCustomToolbar()
required init(source: Observable<StateType>, emitter: ActionEmitter, state: StateType) {
self.emitter = emitter
self.uuid = state.uuid
let configuration = WKWebViewConfiguration()
self.webview = WKWebView(frame: CGRect.zero, configuration: configuration)
super.init(frame: .zero)
self.configureForAutoLayout()
self.innerCustomToolbar.htmlPreviewTool = self
self.addViews()
self.webview.load(URLRequest(url: URL(string: "http://apple.com")!))
source
.observeOn(MainScheduler.instance)
.subscribe(onNext: { [unowned self] state in
})
.addDisposableTo(self.disposeBag)
}
fileprivate func addViews() {
self.webview.configureForAutoLayout()
self.addSubview(self.webview)
self.webview.autoPinEdgesToSuperviewEdges()
}
fileprivate let emitter: ActionEmitter
fileprivate let uuid: String
fileprivate let webview: WKWebView
fileprivate let disposeBag = DisposeBag()
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func selectHtmlFile(sender: Any?) {
let panel = NSOpenPanel()
panel.canChooseDirectories = false
panel.allowsMultipleSelection = false
panel.beginSheetModal(for: self.window!) { result in
guard result == NSFileHandlingPanelOKButton else {
return
}
let urls = panel.urls
guard urls.count == 1 else {
return
}
self.emitter.emit(UuidAction(uuid: self.uuid, action: Action.selectHtmlFile(urls[0])))
}
}
}
extension HtmlPreviewTool {
class InnerCustomToolbar: NSView {
fileprivate weak var htmlPreviewTool: HtmlPreviewTool? {
didSet {
self.selectHtmlFile.target = self.htmlPreviewTool
}
}
let selectHtmlFile = NSButton(forAutoLayout:())
init() {
super.init(frame: .zero)
self.configureForAutoLayout()
self.addViews()
}
fileprivate func addViews() {
let selectHtmlFile = self.selectHtmlFile
InnerToolBar.configureToStandardIconButton(button: selectHtmlFile, iconName: .fileCodeO)
selectHtmlFile.toolTip = "Select the HTML file"
selectHtmlFile.action = #selector(HtmlPreviewTool.selectHtmlFile)
self.addSubview(selectHtmlFile)
selectHtmlFile.autoPinEdge(toSuperviewEdge: .top)
selectHtmlFile.autoPinEdge(toSuperviewEdge: .right)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
}

View File

@ -0,0 +1,33 @@
/**
* Tae Won Ha - http://taewon.de - @hataewon
* See LICENSE
*/
import Foundation
import RxSwift
class HtmlPreviewToolReducer: Reducer {
typealias Pair = StateActionPair<UuidState<MainWindow.State>, HtmlPreviewTool.Action>
init(baseServerUrl: URL) {
self.baseServerUrl = baseServerUrl
}
func transform(_ source: Observable<Pair>) -> Observable<Pair> {
return source.map { pair in
var state = pair.state.payload
switch pair.action {
case let .selectHtmlFile(url):
state.preview = PreviewUtils.state(for: pair.state.uuid,
baseUrl: self.baseServerUrl,
buffer: state.currentBuffer)
return StateActionPair(state: UuidState(uuid: state.uuid, state: state), action: pair.action)
}
}
fileprivate let baseServerUrl: URL
}

View File

@ -20,27 +20,64 @@ fileprivate func shareFile(_ path: String) -> ((HttpRequest) -> HttpResponse) {
}
}
class HttpServerService: Service {
class HttpService {
typealias Element = StateActionPair<UuidState<MainWindow.State>, MainWindow.Action>
let forMainWindow: MainWindowHttpServerService
let forHtmlPreviewTool: HtmlPreviewToolHttpServerService
init(port: Int) {
self.port = port
self.forMainWindow = MainWindowHttpServerService(server: self.server)
self.forHtmlPreviewTool = HtmlPreviewToolHttpServerService(server: self.server)
do {
try self.server.start(in_port_t(port))
NSLog("server started on http://localhost:\(port)")
} catch {
NSLog("ERROR server could not be started on port \(port)")
}
}
fileprivate let server = HttpServer()
fileprivate let port: Int
}
class HtmlPreviewToolHttpServerService: Service {
typealias Element = StateActionPair<UuidState<MainWindow.State>, HtmlPreviewTool.Action>
init(server: HttpServer) {
self.server = server
let resourceUrl = Bundle.main.resourceURL!
let previewResourceUrl = resourceUrl.appendingPathComponent("preview")
self.githubCssUrl = resourceUrl.appendingPathComponent("markdown/github-markdown.css")
do {
try self.server.start(in_port_t(port))
NSLog("server started on http://localhost:\(port)")
self.server["\(PreviewTransformer.basePath)/:path"] = shareFilesFromDirectory(previewResourceUrl.path)
self.server.GET["\(PreviewTransformer.basePath)/github-markdown.css"] = shareFile(self.githubCssUrl.path)
}
func apply(_ pair: Element) {
}
fileprivate let server: HttpServer
fileprivate let githubCssUrl: URL
}
class MainWindowHttpServerService: Service {
typealias Element = StateActionPair<UuidState<MainWindow.State>, MainWindow.Action>
init(server: HttpServer) {
self.server = server
let resourceUrl = Bundle.main.resourceURL!
let previewResourceUrl = resourceUrl.appendingPathComponent("preview")
self.githubCssUrl = resourceUrl.appendingPathComponent("markdown/github-markdown.css")
self.server["\(PreviewTransformer.basePath)/:path"] = shareFilesFromDirectory(previewResourceUrl.path)
self.server.GET["\(PreviewTransformer.basePath)/github-markdown.css"] = shareFile(self.githubCssUrl.path)
} catch {
NSLog("ERROR server could not be started on port \(port)")
}
}
func apply(_ pair: Element) {
@ -66,7 +103,6 @@ class HttpServerService: Service {
self.server.GET["\(htmlBasePath)/github-markdown.css"] = shareFile(self.githubCssUrl.path)
}
fileprivate let server = HttpServer()
fileprivate let server: HttpServer
fileprivate let githubCssUrl: URL
fileprivate let port: Int
}

View File

@ -52,11 +52,12 @@ class MainWindow: NSObject,
enum Tools: String {
static let all = Set([Tools.fileBrowser, Tools.openedFilesList, Tools.preview])
static let all = Set([Tools.fileBrowser, Tools.openedFilesList, Tools.preview, Tools.htmlPreview])
case fileBrowser = "com.qvacua.vimr.tools.file-browser"
case openedFilesList = "com.qvacua.vimr.tools.opened-files-list"
case preview = "com.qvacua.vimr.tools.preview"
case htmlPreview = "com.qvacua.vimr.tools.html-preview"
}
enum OpenMode {
@ -84,6 +85,7 @@ class MainWindow: NSObject,
self.neoVimView.configureForAutoLayout()
self.workspace = Workspace(mainView: self.neoVimView)
self.htmlPreview = HtmlPreviewTool(source: source, emitter: emitter, state: state)
self.preview = PreviewTool(source: source, emitter: emitter, state: state)
self.fileBrowser = FileBrowser(source: source, emitter: emitter, state: state)
self.openedFileList = OpenedFileList(source: source, emitter: emitter, state: state)
@ -96,6 +98,12 @@ class MainWindow: NSObject,
self.previewContainer = WorkspaceTool(previewConfig)
previewContainer.dimension = state.tools[.preview]?.dimension ?? 250
let htmlPreviewConfig = WorkspaceTool.Config(title: "HTML Preview",
view: self.htmlPreview,
customToolbar: self.htmlPreview.innerCustomToolbar)
let htmlPreviewContainer = WorkspaceTool(htmlPreviewConfig)
htmlPreviewContainer.dimension = state.tools[.htmlPreview]?.dimension ?? 250
let fileBrowserConfig = WorkspaceTool.Config(title: "Files",
view: self.fileBrowser,
customToolbar: self.fileBrowser.innerCustomToolbar,
@ -108,6 +116,7 @@ class MainWindow: NSObject,
self.openedFileListContainer.dimension = state.tools[.openedFilesList]?.dimension ?? 200
self.workspace.append(tool: previewContainer, location: state.tools[.preview]?.location ?? .right)
self.workspace.append(tool: htmlPreviewContainer, location: state.tools[.htmlPreview]?.location ?? .right)
self.workspace.append(tool: fileBrowserContainer, location: state.tools[.fileBrowser]?.location ?? .left)
self.workspace.append(tool: openedFileListContainer, location: state.tools[.openedFilesList]?.location ?? .left)
@ -115,6 +124,7 @@ class MainWindow: NSObject,
.fileBrowser: self.fileBrowserContainer,
.openedFilesList: self.openedFileListContainer,
.preview: self.previewContainer,
.htmlPreview: htmlPreviewContainer,
]
super.init()
@ -236,6 +246,7 @@ class MainWindow: NSObject,
fileprivate var previewPosition: Marked<Position>
fileprivate let preview: PreviewTool
fileprivate let htmlPreview: HtmlPreviewTool
fileprivate let fileBrowser: FileBrowser
fileprivate let openedFileList: OpenedFileList

View File

@ -134,6 +134,13 @@ struct PreviewState {
}
}
struct HtmlPreviewState {
static let `default` = HtmlPreviewState()
var url: URL?
}
struct AppearanceState: SerializableState {
static let `default` = AppearanceState()
@ -191,6 +198,7 @@ extension MainWindow {
var preview = PreviewState.default
var previewTool = PreviewTool.State.default
var htmlPreviewTool = HtmlPreviewState.default
var fileBrowserShowHidden = false
@ -264,6 +272,7 @@ struct WorkspaceToolState: SerializableState {
MainWindow.Tools.fileBrowser: WorkspaceToolState(location: .left, dimension: 200, open: true),
MainWindow.Tools.openedFilesList: WorkspaceToolState(location: .left, dimension: 200, open: false),
MainWindow.Tools.preview: WorkspaceToolState(location: .right, dimension: 250, open: false),
MainWindow.Tools.htmlPreview: WorkspaceToolState(location: .right, dimension: 500, open: true),
]
var location = WorkspaceBarLocation.left