mirror of
https://github.com/qvacua/vimr.git
synced 2024-12-28 08:13:17 +03:00
GH-339 Use a http server to serve the markdown preview html file (and the content of the containing folder)
- WKWebView does not let you load files in arbitrary folders when you use loadHTMLString() - loadFileURL can load files from one directories, but you'd have to create a temporary file in the folder
This commit is contained in:
parent
d0fbc4297e
commit
1a7f633cea
1
Cartfile
1
Cartfile
@ -5,3 +5,4 @@ github "sparkle-project/Sparkle" == 1.15.1
|
||||
github "qvacua/CocoaFontAwesome" "master"
|
||||
github "qvacua/CocoaMarkdown" "master"
|
||||
github "sindresorhus/github-markdown-css" == 2.4.1
|
||||
github "httpswift/swifter" == 1.3.2
|
||||
|
@ -6,3 +6,4 @@ github "PureLayout/PureLayout" "v3.0.2"
|
||||
github "ReactiveX/RxSwift" "3.1.0"
|
||||
github "sparkle-project/Sparkle" "1.15.1"
|
||||
github "sindresorhus/github-markdown-css" "v2.4.1"
|
||||
github "httpswift/swifter" "1.3.2"
|
||||
|
@ -25,6 +25,7 @@
|
||||
1929BA120290D6A2A61A4468 /* ArrayCommonsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B477E1E62433BC48E10B /* ArrayCommonsTest.swift */; };
|
||||
1929BA3BB94B77E9AE051FE5 /* PreviewComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B8DA5AA33536F0082200 /* PreviewComponent.swift */; };
|
||||
1929BCF444CE7F1D14D421DE /* FileItemTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B4778E20696E3AAFB69B /* FileItemTest.swift */; };
|
||||
1929BD2F41D93ADFF43C1C98 /* NetUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 1929B02440BC99C42F9EBD45 /* NetUtils.m */; };
|
||||
1929BD3F9E609BFADB27584B /* Scorer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B9D510177918080BE39B /* Scorer.swift */; };
|
||||
1929BD4CA2204E061A86A140 /* MatcherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BC19C1BC19246AFF1621 /* MatcherTests.swift */; };
|
||||
1929BD52275A6570C666A7BA /* PreviewRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B1EC32D8A26958FB39B1 /* PreviewRenderer.swift */; };
|
||||
@ -113,6 +114,8 @@
|
||||
4BB409EF1DDA77E9005F39A2 /* ProxyWorkspaceBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB409ED1DDA77E9005F39A2 /* ProxyWorkspaceBar.swift */; };
|
||||
4BB489431D952CF6005BB0E8 /* WorkspaceToolButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB489411D952CF6005BB0E8 /* WorkspaceToolButton.swift */; };
|
||||
4BBDD3ED1D967DA2008FCC92 /* AdvancedPrefPane.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BBDD3EC1D967DA2008FCC92 /* AdvancedPrefPane.swift */; };
|
||||
4BBFF8621E280E31008A3C1A /* Swifter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BBFF8611E280E31008A3C1A /* Swifter.framework */; };
|
||||
4BBFF8631E2810FE008A3C1A /* Swifter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 4BBFF8611E280E31008A3C1A /* Swifter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
4BCADE081D11ED12004DAD0F /* CocoaExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BCADE071D11ED12004DAD0F /* CocoaExtensions.swift */; };
|
||||
4BD3BF931D32A95800082605 /* MainWindowComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BD3BF921D32A95800082605 /* MainWindowComponent.swift */; };
|
||||
4BD3BF971D32B0DB00082605 /* MainWindowManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BD3BF961D32B0DB00082605 /* MainWindowManager.swift */; };
|
||||
@ -200,6 +203,7 @@
|
||||
files = (
|
||||
4BDD056B1DB0CACB00D1B405 /* Sparkle.framework in Embed Frameworks */,
|
||||
4BDF50121D760B7200D8FBC3 /* EonilFileSystemEvents.framework in Embed Frameworks */,
|
||||
4BBFF8631E2810FE008A3C1A /* Swifter.framework in Embed Frameworks */,
|
||||
4B91FFF61DEB772B00447068 /* CocoaFontAwesome.framework in Embed Frameworks */,
|
||||
4B183E101E06E29C0079E8A8 /* CocoaMarkdown.framework in Embed Frameworks */,
|
||||
4B2A2BFF1D0351810074CE9A /* SwiftNeoVim.framework in Embed Frameworks */,
|
||||
@ -276,6 +280,7 @@
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
1929B02440BC99C42F9EBD45 /* NetUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NetUtils.m; 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>"; };
|
||||
@ -295,6 +300,7 @@
|
||||
1929B9D510177918080BE39B /* Scorer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Scorer.swift; sourceTree = "<group>"; };
|
||||
1929BA6128BFDD54CA92F46E /* ColorUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColorUtils.swift; sourceTree = "<group>"; };
|
||||
1929BA8AC40B901B20F20B71 /* FileUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileUtils.swift; sourceTree = "<group>"; };
|
||||
1929BADEB143008EFA6F3318 /* NetUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NetUtils.h; sourceTree = "<group>"; };
|
||||
1929BB251F74BEFC82CEEF84 /* PrefPane.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrefPane.swift; sourceTree = "<group>"; };
|
||||
1929BB6CFF4CC0B5E8B00C62 /* DataWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DataWrapper.m; sourceTree = "<group>"; };
|
||||
1929BB8BCA48637156F92945 /* PreviewService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreviewService.swift; sourceTree = "<group>"; };
|
||||
@ -371,6 +377,7 @@
|
||||
4BB409ED1DDA77E9005F39A2 /* ProxyWorkspaceBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ProxyWorkspaceBar.swift; path = Workspace/ProxyWorkspaceBar.swift; sourceTree = "<group>"; };
|
||||
4BB489411D952CF6005BB0E8 /* WorkspaceToolButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WorkspaceToolButton.swift; path = Workspace/WorkspaceToolButton.swift; sourceTree = "<group>"; };
|
||||
4BBDD3EC1D967DA2008FCC92 /* AdvancedPrefPane.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdvancedPrefPane.swift; sourceTree = "<group>"; };
|
||||
4BBFF8611E280E31008A3C1A /* Swifter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Swifter.framework; path = Carthage/Build/Mac/Swifter.framework; sourceTree = SOURCE_ROOT; };
|
||||
4BCADE071D11ED12004DAD0F /* CocoaExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaExtensions.swift; sourceTree = "<group>"; };
|
||||
4BCF638F1D323CFD00F15CE4 /* nvim */ = {isa = PBXFileReference; lastKnownFileType = folder; name = nvim; path = neovim/src/nvim; sourceTree = SOURCE_ROOT; };
|
||||
4BD3BF921D32A95800082605 /* MainWindowComponent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainWindowComponent.swift; sourceTree = "<group>"; };
|
||||
@ -477,6 +484,7 @@
|
||||
4B183E0E1E06E2940079E8A8 /* CocoaMarkdown.framework in Frameworks */,
|
||||
4B2A2BEE1D02261F0074CE9A /* RxSwift.framework in Frameworks */,
|
||||
4B401B141D0454DC00D99EDC /* PureLayout.framework in Frameworks */,
|
||||
4BBFF8621E280E31008A3C1A /* Swifter.framework in Frameworks */,
|
||||
4BDF50081D7607BF00D8FBC3 /* EonilFileSystemEvents.framework in Frameworks */,
|
||||
4B2A2BEC1D02261F0074CE9A /* RxCocoa.framework in Frameworks */,
|
||||
);
|
||||
@ -572,6 +580,7 @@
|
||||
4B2A2BE61D0225840074CE9A /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4BBFF8611E280E31008A3C1A /* Swifter.framework */,
|
||||
4B183E0D1E06E2940079E8A8 /* CocoaMarkdown.framework */,
|
||||
4B337FBA1DEB76F20020ADD2 /* CocoaFontAwesome.framework */,
|
||||
4BDD05691DB0CAB700D1B405 /* Sparkle.framework */,
|
||||
@ -844,6 +853,8 @@
|
||||
1929BA8AC40B901B20F20B71 /* FileUtils.swift */,
|
||||
1929BEEB33113B0E33C3830F /* Matcher.swift */,
|
||||
1929B9D510177918080BE39B /* Scorer.swift */,
|
||||
1929B02440BC99C42F9EBD45 /* NetUtils.m */,
|
||||
1929BADEB143008EFA6F3318 /* NetUtils.h */,
|
||||
);
|
||||
name = Utils;
|
||||
sourceTree = "<group>";
|
||||
@ -1294,6 +1305,7 @@
|
||||
1929BA3BB94B77E9AE051FE5 /* PreviewComponent.swift in Sources */,
|
||||
1929B4145AA81F006BAF3B5C /* PreviewService.swift in Sources */,
|
||||
1929BD52275A6570C666A7BA /* PreviewRenderer.swift in Sources */,
|
||||
1929BD2F41D93ADFF43C1C98 /* NetUtils.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -3,4 +3,5 @@
|
||||
* See LICENSE
|
||||
*/
|
||||
|
||||
#import <SwiftNeoVim/SwiftNeoVim.h>
|
||||
#import <SwiftNeoVim/SwiftNeoVim.h>
|
||||
#import "NetUtils.h"
|
||||
|
@ -62,5 +62,30 @@
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>VimR.Application</string>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSExceptionDomains</key>
|
||||
<dict>
|
||||
<key>localhost</key>
|
||||
<dict>
|
||||
<key>NSIncludesSubdomains</key>
|
||||
<true/>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
<key>NSExceptionRequiresForwardSecrecy</key>
|
||||
<true/>
|
||||
<key>NSExceptionMinimumTLSVersion</key>
|
||||
<string>TLSv1.2</string>
|
||||
<key>NSThirdPartyExceptionAllowsInsecureHTTPLoads</key>
|
||||
<false/>
|
||||
<key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
|
||||
<true/>
|
||||
<key>NSThirdPartyExceptionMinimumTLSVersion</key>
|
||||
<string>TLSv1.2</string>
|
||||
<key>NSRequiresCertificateTransparency</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
@ -8,6 +8,7 @@ import RxSwift
|
||||
import PureLayout
|
||||
import CocoaMarkdown
|
||||
import WebKit
|
||||
import Swifter
|
||||
|
||||
fileprivate class WebviewMessageHandler: NSObject, WKScriptMessageHandler {
|
||||
|
||||
@ -105,7 +106,8 @@ class MarkdownRenderer: NSObject, Flow, PreviewRenderer {
|
||||
fileprivate let scrollFlow: EmbeddableComponent
|
||||
|
||||
fileprivate let scheduler = ConcurrentDispatchQueueScheduler(qos: .userInitiated)
|
||||
fileprivate let baseUrl = Bundle.main.resourceURL!.appendingPathComponent("markdown")
|
||||
fileprivate var baseUrl = FileUtils.userHomeUrl
|
||||
fileprivate let resourceBaesUrl = Bundle.main.resourceURL!.appendingPathComponent("markdown")
|
||||
fileprivate let extensions = Set(["md", "markdown", ])
|
||||
fileprivate let template: String
|
||||
|
||||
@ -150,6 +152,9 @@ class MarkdownRenderer: NSObject, Flow, PreviewRenderer {
|
||||
let toolbar: NSView? = NSView(forAutoLayout: ())
|
||||
let menuItems: [NSMenuItem]?
|
||||
|
||||
fileprivate var server = HttpServer()
|
||||
fileprivate let port: in_port_t
|
||||
|
||||
init(source: Observable<Any>, scrollSource: Observable<Any>, initialData: PrefData) {
|
||||
guard let templateUrl = Bundle.main.url(forResource: "template",
|
||||
withExtension: "html",
|
||||
@ -162,6 +167,13 @@ class MarkdownRenderer: NSObject, Flow, PreviewRenderer {
|
||||
preconditionFailure("ERROR Cannot load markdown template")
|
||||
}
|
||||
|
||||
self.server["/preview/markdown/:path"] = shareFilesFromDirectory("/Users/hat/Downloads")
|
||||
let css = (try? String(contentsOf: self.resourceBaesUrl.appendingPathComponent("github-markdown.css"))) ?? ""
|
||||
self.server.GET["/preview/markdown/github-markdown.css"] = { arg in .ok(.html(css)) }
|
||||
self.port = NetUtils.openPort()
|
||||
NSLog("opening a server on port \(port)")
|
||||
do { try self.server.start(port) } catch { NSLog("!!!!!!!!!!!!!!!!!!!!!!!") }
|
||||
|
||||
self.template = template
|
||||
|
||||
self.flow = EmbeddableComponent(source: source)
|
||||
@ -223,6 +235,10 @@ class MarkdownRenderer: NSObject, Flow, PreviewRenderer {
|
||||
self.userContentController.add(webviewMessageHandler, name: "com_vimr_preview_markdown")
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.server.stop()
|
||||
}
|
||||
|
||||
func canRender(fileExtension: String) -> Bool {
|
||||
return extensions.contains(fileExtension)
|
||||
}
|
||||
@ -337,6 +353,7 @@ class MarkdownRenderer: NSObject, Flow, PreviewRenderer {
|
||||
|
||||
fileprivate func filledTemplate(body: String, title: String) -> String {
|
||||
return self.template
|
||||
.replacingOccurrences(of: "{{ resource-base-path }}", with: self.resourceBaesUrl.path)
|
||||
.replacingOccurrences(of: "{{ title }}", with: title)
|
||||
.replacingOccurrences(of: "{{ body }}", with: body)
|
||||
}
|
||||
@ -351,9 +368,15 @@ class MarkdownRenderer: NSObject, Flow, PreviewRenderer {
|
||||
}
|
||||
|
||||
let html = filledTemplate(body: body, title: url.lastPathComponent)
|
||||
self.webview.loadHTMLString(html, baseURL: self.baseUrl)
|
||||
|
||||
try? html.write(toFile: "/tmp/markdown-preview.html", atomically: false, encoding: .utf8)
|
||||
|
||||
let baseUrl = url.deletingLastPathComponent()
|
||||
NSLog("baseUrl: \(baseUrl)")
|
||||
|
||||
self.server.GET["/preview/markdown/index.html"] = { arg in .ok(.html(html)) }
|
||||
|
||||
let url = URL(string: "http://localhost:\(self.port)/preview/markdown/index.html")!
|
||||
self.webview.load(URLRequest(url: url))
|
||||
self.flow.publish(event: PreviewRendererAction.view(renderer: self, view: self.webview))
|
||||
}
|
||||
}
|
||||
|
13
VimR/NetUtils.h
Normal file
13
VimR/NetUtils.h
Normal file
@ -0,0 +1,13 @@
|
||||
//
|
||||
// Created by Tae Won Ha on 1/13/17.
|
||||
// Copyright (c) 2017 Tae Won Ha. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
|
||||
@interface NetUtils : NSObject
|
||||
|
||||
+ (in_port_t)openPort;
|
||||
|
||||
@end
|
56
VimR/NetUtils.m
Normal file
56
VimR/NetUtils.m
Normal file
@ -0,0 +1,56 @@
|
||||
//
|
||||
// Created by Tae Won Ha on 1/13/17.
|
||||
// Copyright (c) 2017 Tae Won Ha. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NetUtils.h"
|
||||
#import <sys/socket.h>
|
||||
#import <netinet/in.h>
|
||||
|
||||
@implementation NetUtils
|
||||
|
||||
// from https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/UsingSocketsandSocketStreams.html#//apple_ref/doc/uid/CH73-SW9
|
||||
// and http://stackoverflow.com/a/20850182/6939513
|
||||
// slightly modified
|
||||
+ (in_port_t)openPort {
|
||||
int sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if(sock < 0) {
|
||||
NSLog(@"ERROR Could not open a socket");
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct sockaddr_in sin;
|
||||
memset(&sin, 0, sizeof(sin));
|
||||
|
||||
sin.sin_len = sizeof(sin);
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_port = htons(0);
|
||||
sin.sin_addr.s_addr= INADDR_ANY;
|
||||
|
||||
if (bind(sock, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
|
||||
if(errno == EADDRINUSE) {
|
||||
NSLog(@"ERROR the port is not available. already to other process");
|
||||
return 0;
|
||||
} else {
|
||||
NSLog(@"ERROR could not bind to process (%d) %s", errno, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
socklen_t len = sizeof(sin);
|
||||
if (getsockname(sock, (struct sockaddr *)&sin, &len) == -1) {
|
||||
NSLog(@"ERROR getsockname");
|
||||
return 0;
|
||||
}
|
||||
|
||||
in_port_t result = ntohs(sin.sin_port);
|
||||
|
||||
if (close (sock) < 0 ) {
|
||||
NSLog(@"ERROR did not close: %s", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
@ -8,7 +8,7 @@ import RxSwift
|
||||
import PureLayout
|
||||
import WebKit
|
||||
|
||||
class PreviewComponent: NSView, ViewComponent, ToolDataHolder {
|
||||
class PreviewComponent: NSView, ViewComponent, ToolDataHolder, WKNavigationDelegate {
|
||||
|
||||
enum Action {
|
||||
|
||||
@ -157,6 +157,8 @@ class PreviewComponent: NSView, ViewComponent, ToolDataHolder {
|
||||
super.init(frame: .zero)
|
||||
self.configureForAutoLayout()
|
||||
|
||||
self.webview.navigationDelegate = self
|
||||
|
||||
self.flow.set(subscription: self.subscription)
|
||||
|
||||
self.webview.loadHTMLString(self.previewService.emptyHtml(), baseURL: self.baseUrl)
|
||||
@ -276,4 +278,8 @@ class PreviewComponent: NSView, ViewComponent, ToolDataHolder {
|
||||
})
|
||||
.addDisposableTo(self.flow.disposeBag)
|
||||
}
|
||||
|
||||
func webView(_: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
|
||||
NSLog("ERROR preview component's webview: \(error)")
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user