diff --git a/Sources/XiEditor/Client.swift b/Sources/XiEditor/Client.swift
index 6c164225..973b9cec 100644
--- a/Sources/XiEditor/Client.swift
+++ b/Sources/XiEditor/Client.swift
@@ -86,4 +86,7 @@ protocol XiClient: AnyObject {
/// A notification containing the current replace status.
func replaceStatus(viewIdentifier: String, status: ReplaceStatus)
+
+ /// A notification telling toggle tail config was successfully changed.
+ func toggleTailConfigChanged(viewIdentifier: String, isTailEnabled: Bool)
}
diff --git a/Sources/XiEditor/ClientImplementation.swift b/Sources/XiEditor/ClientImplementation.swift
index a7e54296..8d32b50f 100644
--- a/Sources/XiEditor/ClientImplementation.swift
+++ b/Sources/XiEditor/ClientImplementation.swift
@@ -176,6 +176,13 @@ class ClientImplementation: XiClient, DocumentsProviding, ConfigCacheProviding,
}
}
}
+
+ func toggleTailConfigChanged(viewIdentifier: String, isTailEnabled: Bool) {
+ let document = documentForViewIdentifier(viewIdentifier: viewIdentifier)
+ DispatchQueue.main.async {
+ document?.editViewController?.toggleTailConfigChanged(isTailEnabled)
+ }
+ }
// Stores the config dict so new windows don't have to wait for core to send it.
// The main purpose of this is ensuring that `unified_titlebar` applies immediately.
diff --git a/Sources/XiEditor/Core/CoreNotification.swift b/Sources/XiEditor/Core/CoreNotification.swift
index d7422565..f8be8d5c 100644
--- a/Sources/XiEditor/Core/CoreNotification.swift
+++ b/Sources/XiEditor/Core/CoreNotification.swift
@@ -71,6 +71,7 @@ enum CoreNotification {
case findStatus(viewIdentifier: ViewIdentifier, status: [FindStatus])
case replaceStatus(viewIdentifier: ViewIdentifier, status: ReplaceStatus)
+ case toggleTailChanged(viewIdentifier: ViewIdentifier, isTailEnabled: Bool)
static func fromJson(_ json: [String: Any]) -> CoreNotification? {
guard
@@ -228,6 +229,11 @@ enum CoreNotification {
{
return .replaceStatus(viewIdentifier: viewIdentifier!, status: replaceStatus)
}
+ case "toggle_tail_config_changed":
+ if let isTailEnabled = jsonParams["is_tail_enabled"] as? Bool
+ {
+ return .toggleTailChanged(viewIdentifier: viewIdentifier!, isTailEnabled: isTailEnabled)
+ }
default:
assertionFailure("Unsupported notification method from core: \(jsonMethod)")
diff --git a/Sources/XiEditor/EditViewController.swift b/Sources/XiEditor/EditViewController.swift
index 63218ab9..95eed132 100644
--- a/Sources/XiEditor/EditViewController.swift
+++ b/Sources/XiEditor/EditViewController.swift
@@ -150,6 +150,12 @@ class EditViewController: NSViewController, EditViewDataSource, FindDelegate, Sc
updateLanguageMenu()
}
}
+
+ var isTailEnabled: Bool = false {
+ didSet {
+ updateTailMenu()
+ }
+ }
// used to calculate the gutter width. Initial -1 so that a new document
// still triggers update of gutter width.
@@ -719,6 +725,10 @@ class EditViewController: NSViewController, EditViewDataSource, FindDelegate, Sc
document.xiCore.setTheme(themeName: sender.title)
}
+ @IBAction func debugToggleTail(_ sender: NSMenuItem) {
+ document.xiCore.toggleTailConfig(identifier: document.coreViewIdentifier!, enabled: !self.isTailEnabled)
+ }
+
@IBAction func debugSetLanguage(_ sender: NSMenuItem) {
guard sender.state != NSControl.StateValue.on else { print("language already active"); return }
document.xiCore.setLanguage(identifier: document.coreViewIdentifier!, languageName: sender.title)
@@ -848,11 +858,22 @@ class EditViewController: NSViewController, EditViewDataSource, FindDelegate, Sc
item.state = findViewController.showMultipleSearchQueries ? .on : .off
}
+ func updateTailMenu() {
+ let toggleTailSubMenu = NSApplication.shared.mainMenu!.item(withTitle: "Debug")!.submenu!.item(withTitle: "Tail File")
+
+ if self.isTailEnabled {
+ toggleTailSubMenu!.state = NSControl.StateValue.on
+ } else {
+ toggleTailSubMenu!.state = NSControl.StateValue.off
+ }
+ }
+
// Gets called when active window changes
func updateMenuState() {
updatePluginMenu()
updateLanguageMenu()
updateFindMenu()
+ updateTailMenu()
}
@objc func handleCommand(_ sender: NSMenuItem) {
@@ -927,6 +948,10 @@ class EditViewController: NSViewController, EditViewDataSource, FindDelegate, Sc
languagesMenu.addItem(item)
}
}
+
+ public func toggleTailConfigChanged(_ isTailEnabled: Bool) {
+ self.isTailEnabled = isTailEnabled
+ }
@IBAction func gotoLine(_ sender: AnyObject) {
guard let window = self.view.window else { return }
diff --git a/Sources/XiEditor/Main.storyboard b/Sources/XiEditor/Main.storyboard
index c810bd06..7bd20c4a 100644
--- a/Sources/XiEditor/Main.storyboard
+++ b/Sources/XiEditor/Main.storyboard
@@ -1198,6 +1198,12 @@ Gw
+
@@ -1227,6 +1233,7 @@ Gw
+
diff --git a/Sources/XiEditor/RPCSending.swift b/Sources/XiEditor/RPCSending.swift
index 7dcc44bb..1044a2d1 100644
--- a/Sources/XiEditor/RPCSending.swift
+++ b/Sources/XiEditor/RPCSending.swift
@@ -30,7 +30,7 @@ struct RemoteError {
let code: Int
let message: String
let data: AnyObject?
-
+
init?(json: [String: AnyObject]) {
guard let code = json["code"] as? Int,
let message = json["message"] as? String else { return nil }
@@ -63,12 +63,12 @@ class StdoutRPCSender: RPCSending {
weak var client: XiClient?
private let rpcLogWriter: FileWriter?
private var lastLogs = CircleBuffer(capacity: 100)
-
+
// RPC state
private var queue = DispatchQueue(label: "io.xi-editor.XiEditor.CoreConnection", attributes: [])
private var rpcIndex = 0
private var pending = Dictionary()
-
+
init(path: String, errorLogDirectory: URL?) {
if let rpcLogPath = ProcessInfo.processInfo.environment[XI_RPC_LOG] {
self.rpcLogWriter = FileWriter(path: rpcLogPath)
@@ -88,18 +88,18 @@ class StdoutRPCSender: RPCSending {
task.environment = ProcessInfo.processInfo.environment
}
task.environment?["RUST_BACKTRACE"] = "1"
-
+
let outPipe = Pipe()
task.standardOutput = outPipe
let inPipe = Pipe()
task.standardInput = inPipe
inHandle = inPipe.fileHandleForWriting
-
+
outPipe.fileHandleForReading.readabilityHandler = { handle in
let data = handle.availableData
self.recvHandler(data)
}
-
+
let errPipe = Pipe()
task.standardError = errPipe
errPipe.fileHandleForReading.readabilityHandler = { [weak self] handle in
@@ -112,22 +112,22 @@ class StdoutRPCSender: RPCSending {
self?.lastLogs.push(errString)
}
}
-
+
// save backtrace on core crash
task.terminationHandler = { [weak self] process in
guard process.terminationStatus != 0, let strongSelf = self else {
print("xi-core exited with code 0")
return
}
-
+
print("xi-core exited with code \(process.terminationStatus), attempting to save log")
-
+
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd-HHMMSS"
let timeStamp = dateFormatter.string(from: Date())
let crashLogFilename = "XiEditor-Crash-\(timeStamp).log"
let crashLogPath = errorLogDirectory?.appendingPathComponent(crashLogFilename)
-
+
let logText = strongSelf.lastLogs.allItems().joined()
if let path = crashLogPath {
do {
@@ -140,12 +140,12 @@ class StdoutRPCSender: RPCSending {
}
task.launch()
}
-
+
private func recvHandler(_ data: Data) {
if data.count == 0 {
return
}
-
+
// Split incoming bytes into "packets" (separated by newlines)
// and dispatch to the app to handle
data.withUnsafeBytes { buffer in
@@ -161,44 +161,44 @@ class StdoutRPCSender: RPCSending {
} else {
handleRaw(bytes)
}
-
+
i = j + 1
}
}
-
+
if i < buffer.endIndex {
recvBuf.append(Data(buffer[i.. RpcResult {
let semaphore = DispatchSemaphore(value: 0)
var result: RpcResult? = nil
-
+
sendRpcAsync(method, params: params) { (r) in
result = r
semaphore.signal()
@@ -344,3 +346,4 @@ class StdoutRPCSender: RPCSending {
return result!
}
}
+
diff --git a/Sources/XiEditor/XiCore.swift b/Sources/XiEditor/XiCore.swift
index dd120d58..02dbbb4f 100644
--- a/Sources/XiEditor/XiCore.swift
+++ b/Sources/XiEditor/XiCore.swift
@@ -48,6 +48,9 @@ protocol XiCore: class {
/// `Document` calls are migrated to it's own protocol.
func sendRpcAsync(_ method: String, params: Any, callback: RpcCallback?)
func sendRpc(_ method: String, params: Any) -> RpcResult
+ /// Will tail opened file if enabled.
+ /// If toggle succeeds the client will receive a `toggle_tail_config_changed` notification.
+ func toggleTailConfig(identifier: ViewIdentifier, enabled: Bool)
}
final class CoreConnection: XiCore {
@@ -109,6 +112,10 @@ final class CoreConnection: XiCore {
let params = ["destination": destination, "frontend_samples": frontendSamples] as AnyObject
sendRpcAsync("save_trace", params: params)
}
+
+ func toggleTailConfig(identifier: ViewIdentifier, enabled: Bool) {
+ sendRpcAsync("toggle_tail", params: ["view_id": identifier, "enabled": enabled])
+ }
func sendRpcAsync(_ method: String, params: Any, callback: RpcCallback? = nil) {
rpcSender.sendRpcAsync(method, params: params, callback: callback)
diff --git a/Tests/IntegrationTests/TestClientImplementation.swift b/Tests/IntegrationTests/TestClientImplementation.swift
index 4cde8f95..00e6a33b 100644
--- a/Tests/IntegrationTests/TestClientImplementation.swift
+++ b/Tests/IntegrationTests/TestClientImplementation.swift
@@ -86,4 +86,7 @@ class TestClientImplementation: XiClient {
func replaceStatus(viewIdentifier: String, status: ReplaceStatus) {
}
+
+ func toggleTailConfigChanged(viewIdentifier: String, isTailEnabled: Bool) {
+ }
}
diff --git a/xi-editor b/xi-editor
index a5850993..fd14ce69 160000
--- a/xi-editor
+++ b/xi-editor
@@ -1 +1 @@
-Subproject commit a58509934eeab8dc10a0ccf8b68b6d10c78a856c
+Subproject commit fd14ce69bbbe80c011088ae47e50a9131217ea50