mirror of
https://github.com/livebook-dev/livebook.git
synced 2024-09-20 10:05:57 +08:00
Fix displaying error dialog and logging errors (#1848)
This commit is contained in:
parent
dcf7297f38
commit
e3a72d0dbe
|
@ -12,27 +12,37 @@ public struct Demo {
|
|||
}
|
||||
|
||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
private var window : NSWindow!
|
||||
private var window: NSWindow!
|
||||
private var button: NSButton!
|
||||
|
||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||
button = NSButton(title: "Press me!", target: self, action: #selector(pressMe))
|
||||
button.isEnabled = false
|
||||
|
||||
ElixirKit.API.start(
|
||||
name: "demo",
|
||||
readyHandler: {
|
||||
// GUI updates need to happen on the main thread.
|
||||
DispatchQueue.main.sync {
|
||||
self.button.isEnabled = true
|
||||
}
|
||||
|
||||
ElixirKit.API.publish("log", "Hello from AppKit!")
|
||||
|
||||
ElixirKit.API.addObserver(queue: .main) { (name, data) in
|
||||
switch name {
|
||||
case "log":
|
||||
print("[client] " + data)
|
||||
default:
|
||||
fatalError("unknown event \(name)")
|
||||
}
|
||||
}
|
||||
},
|
||||
terminationHandler: { _ in
|
||||
NSApp.terminate(nil)
|
||||
}
|
||||
)
|
||||
|
||||
ElixirKit.API.publish("log", "Hello from AppKit!")
|
||||
|
||||
ElixirKit.API.addObserver(queue: .main) { (name, data) in
|
||||
switch name {
|
||||
case "log":
|
||||
print("[client] " + data)
|
||||
default:
|
||||
fatalError("unknown event \(name)")
|
||||
}
|
||||
}
|
||||
|
||||
let menuItemOne = NSMenuItem()
|
||||
menuItemOne.submenu = NSMenu(title: "Demo")
|
||||
menuItemOne.submenu?.items = [
|
||||
|
@ -48,8 +58,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
defer: true)
|
||||
window.orderFrontRegardless()
|
||||
window.title = "Demo"
|
||||
|
||||
let button = NSButton(title: "Press me!", target: self, action: #selector(pressMe))
|
||||
window.contentView!.subviews.append(button)
|
||||
|
||||
NSApp.setActivationPolicy(.regular)
|
||||
|
|
|
@ -2,22 +2,27 @@ class Demo
|
|||
{
|
||||
public static void Main()
|
||||
{
|
||||
ElixirKit.API.Start(name: "demo");
|
||||
ElixirKit.API.Publish("log", "Hello from C#!");
|
||||
|
||||
ElixirKit.API.Subscribe((name, data) =>
|
||||
{
|
||||
switch (name)
|
||||
ElixirKit.API.Start(
|
||||
name: "demo",
|
||||
ready: () =>
|
||||
{
|
||||
case "log":
|
||||
Console.WriteLine($"[client] {data}");
|
||||
break;
|
||||
ElixirKit.API.Publish("log", "Hello from C#!");
|
||||
|
||||
default:
|
||||
throw new Exception($"unknown event {name}");
|
||||
ElixirKit.API.Subscribe((name, data) =>
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case "log":
|
||||
Console.WriteLine($"[client] {data}");
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception($"unknown event {name}");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
ElixirKit.API.WaitForExit();
|
||||
System.Console.WriteLine($"{ElixirKit.API.WaitForExit()}");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,17 +10,21 @@ struct Demo {
|
|||
exit(signal)
|
||||
}
|
||||
|
||||
ElixirKit.API.start(name: "demo")
|
||||
ElixirKit.API.publish("log", "Hello from Swift!")
|
||||
ElixirKit.API.start(
|
||||
name: "demo",
|
||||
readyHandler: {
|
||||
ElixirKit.API.publish("log", "Hello from Swift!")
|
||||
|
||||
ElixirKit.API.addObserver(queue: .main) { (name, data) in
|
||||
switch name {
|
||||
case "log":
|
||||
print("[client] " + data)
|
||||
default:
|
||||
fatalError("unknown event \(name)")
|
||||
ElixirKit.API.addObserver(queue: .main) { (name, data) in
|
||||
switch name {
|
||||
case "log":
|
||||
print("[client] " + data)
|
||||
default:
|
||||
fatalError("unknown event \(name)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
ElixirKit.API.waitUntilExit()
|
||||
}
|
||||
|
|
|
@ -7,31 +7,36 @@ static class DemoMain
|
|||
{
|
||||
if (ElixirKit.API.IsMainInstance("com.example.Demo"))
|
||||
{
|
||||
ElixirKit.API.Start(name: "demo", exited: (exitCode) =>
|
||||
{
|
||||
Application.Exit();
|
||||
});
|
||||
ElixirKit.API.Start(
|
||||
name: "demo",
|
||||
ready: () =>
|
||||
{
|
||||
ElixirKit.API.Publish("log", "Hello from Windows Forms!");
|
||||
|
||||
ElixirKit.API.Subscribe((name, data) =>
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case "log":
|
||||
Console.WriteLine($"[client] {data}");
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception($"unknown event {name}");
|
||||
}
|
||||
});
|
||||
},
|
||||
exited: (exitCode) =>
|
||||
{
|
||||
Application.Exit();
|
||||
}
|
||||
);
|
||||
|
||||
Application.ApplicationExit += (sender, args) =>
|
||||
{
|
||||
ElixirKit.API.Stop();
|
||||
};
|
||||
|
||||
ElixirKit.API.Publish("log", "Hello from Windows Forms!");
|
||||
|
||||
ElixirKit.API.Subscribe((name, data) =>
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case "log":
|
||||
Console.WriteLine($"[client] {data}");
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception($"unknown event {name}");
|
||||
}
|
||||
});
|
||||
|
||||
ApplicationConfiguration.Initialize();
|
||||
Application.Run(new DemoForm());
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@ using System.Net.Sockets;
|
|||
|
||||
namespace ElixirKit;
|
||||
|
||||
public delegate void ReadyHandler();
|
||||
|
||||
public delegate void ExitHandler(int ExitCode);
|
||||
|
||||
public delegate void EventHandler(string Name, string Data);
|
||||
|
@ -43,11 +45,11 @@ public static class API
|
|||
}
|
||||
}
|
||||
|
||||
public static void Start(string name, ExitHandler? exited = null, string? logPath = null)
|
||||
public static void Start(string name, ReadyHandler ready, ExitHandler? exited = null, string? logPath = null)
|
||||
{
|
||||
ensureMainInstance();
|
||||
|
||||
release = new Release(name, exited, logPath);
|
||||
release = new Release(name, ready, exited, logPath);
|
||||
|
||||
if (mutex != null)
|
||||
{
|
||||
|
@ -136,7 +138,7 @@ internal class Release
|
|||
}
|
||||
}
|
||||
|
||||
public Release(string name, ExitHandler? exited = null, string? logPath = null)
|
||||
public Release(string name, ReadyHandler ready, ExitHandler? exited = null, string? logPath = null)
|
||||
{
|
||||
logger = new Logger(logPath);
|
||||
listener = new Listener();
|
||||
|
@ -187,8 +189,11 @@ internal class Release
|
|||
process.BeginOutputReadLine();
|
||||
process.BeginErrorReadLine();
|
||||
|
||||
var tcpClient = listener.TcpListener.AcceptTcpClient();
|
||||
client = new Client(tcpClient);
|
||||
Task.Run(() => {
|
||||
var tcpClient = listener.TcpListener.AcceptTcpClient();
|
||||
client = new Client(tcpClient);
|
||||
ready();
|
||||
});
|
||||
}
|
||||
|
||||
public void Send(string message)
|
||||
|
|
|
@ -13,20 +13,25 @@ public class API {
|
|||
}
|
||||
}
|
||||
|
||||
public static func start(name: String, logPath: String? = nil, terminationHandler: ((Process) -> Void)? = nil) {
|
||||
release = Release(name: name, logPath: logPath, terminationHandler: terminationHandler)
|
||||
public static func start(
|
||||
name: String,
|
||||
logPath: String? = nil,
|
||||
readyHandler: @escaping () -> Void,
|
||||
terminationHandler: ((Process) -> Void)? = nil) {
|
||||
|
||||
release = Release(name: name, logPath: logPath, readyHandler: readyHandler, terminationHandler: terminationHandler)
|
||||
}
|
||||
|
||||
public static func publish(_ name: String, _ data: String) {
|
||||
release?.publish(name, data)
|
||||
release!.publish(name, data)
|
||||
}
|
||||
|
||||
public static func stop() {
|
||||
release?.stop();
|
||||
release!.stop();
|
||||
}
|
||||
|
||||
public static func waitUntilExit() {
|
||||
release?.waitUntilExit();
|
||||
release!.waitUntilExit();
|
||||
}
|
||||
|
||||
public static func addObserver(queue: OperationQueue?, using: @escaping (((String, String)) -> Void)) {
|
||||
|
@ -42,6 +47,7 @@ private class Release {
|
|||
let logger: Logger
|
||||
let listener: NWListener
|
||||
var connection: Connection?
|
||||
let readyHandler: () -> Void
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
|
||||
var isRunning: Bool {
|
||||
|
@ -50,7 +56,13 @@ private class Release {
|
|||
}
|
||||
}
|
||||
|
||||
init(name: String, logPath: String? = nil, terminationHandler: ((Process) -> Void)? = nil) {
|
||||
init(
|
||||
name: String,
|
||||
logPath: String? = nil,
|
||||
readyHandler: @escaping () -> Void,
|
||||
terminationHandler: ((Process) -> Void)? = nil) {
|
||||
|
||||
self.readyHandler = readyHandler
|
||||
logger = Logger(logPath: logPath)
|
||||
listener = try! NWListener(using: .tcp, on: .any)
|
||||
|
||||
|
@ -77,7 +89,7 @@ private class Release {
|
|||
listener.newConnectionHandler = didAccept(conn:)
|
||||
listener.start(queue: .global())
|
||||
|
||||
let seconds = 15
|
||||
let seconds = 5
|
||||
let timeout = DispatchTime.now() + DispatchTimeInterval.seconds(seconds)
|
||||
|
||||
if semaphore.wait(timeout: timeout) == .timedOut {
|
||||
|
@ -86,7 +98,7 @@ private class Release {
|
|||
}
|
||||
|
||||
public func stop() {
|
||||
connection!.cancel()
|
||||
connection?.cancel()
|
||||
listener.cancel()
|
||||
waitUntilExit()
|
||||
}
|
||||
|
@ -117,11 +129,12 @@ private class Release {
|
|||
env["ELIXIRKIT_PORT"] = port
|
||||
process.environment = env
|
||||
try! process.run()
|
||||
semaphore.signal()
|
||||
}
|
||||
|
||||
private func didAccept(conn: NWConnection) {
|
||||
self.connection = Connection(conn: conn, logger: logger)
|
||||
semaphore.signal()
|
||||
readyHandler()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,35 +21,40 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||
logPath = "\(NSHomeDirectory())/Library/Logs/Livebook.log"
|
||||
|
||||
ElixirKit.API.start(name: "app", logPath: logPath) { process in
|
||||
if process.terminationStatus != 0 {
|
||||
DispatchQueue.main.sync {
|
||||
let alert = NSAlert()
|
||||
alert.alertStyle = .critical
|
||||
alert.messageText = "Livebook exited with error status \(process.terminationStatus)"
|
||||
alert.addButton(withTitle: "Dismiss")
|
||||
alert.addButton(withTitle: "View Logs")
|
||||
|
||||
switch alert.runModal() {
|
||||
case .alertSecondButtonReturn:
|
||||
self.viewLogs()
|
||||
default:
|
||||
()
|
||||
ElixirKit.API.start(
|
||||
name: "app",
|
||||
logPath: logPath,
|
||||
readyHandler: {
|
||||
if (self.initialURLs == []) {
|
||||
ElixirKit.API.publish("open", "")
|
||||
}
|
||||
else {
|
||||
for url in self.initialURLs {
|
||||
ElixirKit.API.publish("open", url.absoluteString)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
terminationHandler: { process in
|
||||
if process.terminationStatus != 0 {
|
||||
DispatchQueue.main.sync {
|
||||
let alert = NSAlert()
|
||||
alert.alertStyle = .critical
|
||||
alert.messageText = "Livebook exited with error status \(process.terminationStatus)"
|
||||
alert.addButton(withTitle: "Dismiss")
|
||||
alert.addButton(withTitle: "View Logs")
|
||||
|
||||
NSApp.terminate(nil)
|
||||
}
|
||||
switch alert.runModal() {
|
||||
case .alertSecondButtonReturn:
|
||||
self.viewLogs()
|
||||
default:
|
||||
()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (self.initialURLs == []) {
|
||||
ElixirKit.API.publish("open", "")
|
||||
}
|
||||
else {
|
||||
for url in self.initialURLs {
|
||||
ElixirKit.API.publish("open", url.absoluteString)
|
||||
NSApp.terminate(nil)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
|
||||
let button = statusItem.button!
|
||||
|
@ -81,7 +86,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
fatalError("unknown event \(name)")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func applicationWillTerminate(_ aNotification: Notification) {
|
||||
|
|
|
@ -88,27 +88,28 @@ class LivebookApp : ApplicationContext
|
|||
ElixirKit.API.Start(
|
||||
name: "app",
|
||||
logPath: logPath,
|
||||
ready: () => {
|
||||
ElixirKit.API.Subscribe((name, data) =>
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case "url":
|
||||
copyURLButton.Enabled = true;
|
||||
this.url = data;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception($"unknown event {name}");
|
||||
}
|
||||
});
|
||||
|
||||
ElixirKit.API.Publish("open", url);
|
||||
},
|
||||
exited: (exitCode) =>
|
||||
{
|
||||
Application.Exit();
|
||||
}
|
||||
);
|
||||
|
||||
ElixirKit.API.Subscribe((name, data) =>
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case "url":
|
||||
copyURLButton.Enabled = true;
|
||||
this.url = data;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception($"unknown event {name}");
|
||||
}
|
||||
});
|
||||
|
||||
ElixirKit.API.Publish("open", url);
|
||||
}
|
||||
|
||||
private void threadExit(object? sender, EventArgs e)
|
||||
|
|
Loading…
Reference in a new issue