Skip to content

Commit 5550944

Browse files
committed
Update AGDebugKit to add dot format support
1 parent b9bc390 commit 5550944

File tree

5 files changed

+84
-33
lines changed

5 files changed

+84
-33
lines changed

Package.resolved

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ let package = Package(
1717
.library(name: "AGDebugKit", targets: ["AGDebugKit"]),
1818
],
1919
dependencies: [
20-
.package(url: "https://github.com/OpenSwiftUIProject/OpenGraph.git", exact: "0.0.5"),
20+
.package(url: "https://github.com/OpenSwiftUIProject/OpenGraph.git", exact: "0.0.7"),
2121
.package(url: "https://github.com/OpenSwiftUIProject/Socket.git", from: "0.3.3"),
2222
],
2323
targets: [

Sources/AGDebugKit/Graph.swift

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,18 @@ import Foundation
1010

1111
/// A wrapper class for AGGraph
1212
public final class Graph {
13-
private let graph: AGGraph?
13+
private unowned let graph: AGGraph?
1414

1515
public init() {
1616
graph = nil
1717
}
1818

1919
public init(_ pointer: UnsafeRawPointer?) {
20-
if let pointer {
21-
let sharedGraph = pointer.assumingMemoryBound(to: AGGraph.self).pointee
22-
graph = AGGraph(shared: sharedGraph)
23-
} else {
24-
graph = AGGraph()
25-
}
20+
graph = pointer?.assumingMemoryBound(to: AGGraph.self).pointee
21+
}
22+
23+
public init(bitPattern: Int) {
24+
graph = Unmanaged<AGGraph>.fromOpaque(.init(bitPattern: bitPattern)!).takeUnretainedValue()
2625
}
2726

2827
public var dict: NSDictionary? {
@@ -54,4 +53,21 @@ public final class Graph {
5453
public static func archiveGraph(name: String) {
5554
name.withCString { AGGraph.archiveJSON(name: $0) }
5655
}
56+
57+
/// Command to transform dot file to svg
58+
/// 1. Install graphviz
59+
/// 2. Execuate `dot -Tsvg xx.dot > xx.svg`
60+
public static func _graphExport(_ bitPattern: Int, name: String = "aggraph_export.dot") {
61+
// TODO: How to get the current in memory's AGGraphStorage objects?
62+
// Currently we just use Xcode Memory Graph and use any of the AGGraphStorage's address
63+
let graph = Graph(bitPattern: bitPattern)
64+
let dot = graph.dot ?? ""
65+
let path = URL(fileURLWithPath: "\(NSTemporaryDirectory())/\(name)")
66+
do {
67+
try dot.write(to: path, atomically: true, encoding: .utf8)
68+
print(#"Wrote graph data to "\#(path.absoluteString)""#)
69+
} catch {
70+
print("Error writing to file: \(error)")
71+
}
72+
}
5773
}

Sources/DemoApp/AGDebugModifier.swift

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,25 @@
66
//
77

88
import AGDebugKit
9+
import AttributeGraph
910
import SwiftUI
1011

1112
@available(macOS 13, *)
1213
struct AGDebugItem: Equatable, Identifiable {
1314
var url: URL
14-
15-
init(name: String) {
16-
url = URL(filePath: NSTemporaryDirectory().appending("\(name).json"))
15+
var format: Format
16+
17+
enum Format: String, CaseIterable, Identifiable {
18+
var id: String { rawValue }
19+
20+
case json, dot
1721
}
18-
22+
23+
init(name: String, format: Format) {
24+
url = URL(filePath: NSTemporaryDirectory().appending("\(name).\(format.rawValue)"))
25+
self.format = format
26+
}
27+
1928
var id: String { url.absoluteString }
2029
}
2130

@@ -24,12 +33,26 @@ struct AGDebugModifier: ViewModifier {
2433
@State private var showInspector = false
2534
@State private var items: [AGDebugItem] = []
2635

36+
fileprivate static var sharedGraphbitPattern: Int = 0
37+
@State private var format: AGDebugItem.Format = .dot
38+
2739
func body(content: Content) -> some View {
2840
content
2941
.toolbar {
42+
Picker("Format", selection: $format) {
43+
ForEach(AGDebugItem.Format.allCases) {
44+
Text($0.rawValue).tag($0)
45+
}
46+
}.pickerStyle(.segmented)
3047
Button {
31-
let item = AGDebugItem(name: Date.now.ISO8601Format())
32-
Graph.archiveGraph(name: item.url.lastPathComponent)
48+
let item = AGDebugItem(name: Date.now.ISO8601Format(), format: format)
49+
let name = item.url.lastPathComponent
50+
switch format {
51+
case .json:
52+
Graph.archiveGraph(name: name)
53+
case .dot:
54+
Graph._graphExport(AGDebugModifier.sharedGraphbitPattern, name: name)
55+
}
3356
items.append(item)
3457
} label: {
3558
Image(systemName: "doc.badge.plus")
@@ -43,6 +66,7 @@ struct AGDebugModifier: ViewModifier {
4366
.inspector(isPresented: $showInspector) {
4467
inspectorView
4568
}
69+
.overlay { _GraphFetcher() }
4670
}
4771

4872
private var inspectorView: some View {
@@ -89,16 +113,16 @@ struct AGDebugModifier: ViewModifier {
89113
private func openAction(_ url: URL) {
90114
_ = NSWorkspace.shared.open(url)
91115
}
92-
116+
93117
// MARK: - Move
94-
118+
95119
@State private var moving = false
96120
@State private var moveURL: URL?
97121
private func moveAction(_ url: URL) {
98122
moveURL = url
99123
moving = true
100124
}
101-
125+
102126
// MARK: - Delete
103127

104128
private func deleteAction(_ url: URL) throws {
@@ -113,3 +137,28 @@ extension View {
113137
modifier(AGDebugModifier())
114138
}
115139
}
140+
141+
struct _GraphFetcher: View {
142+
var body: Never { fatalError("Unimplemented") }
143+
144+
static func _makeView(view: _GraphValue<Self>, inputs: _ViewInputs) -> _ViewOutputs {
145+
if let current = AGSubgraph.current {
146+
let graph = current.graph
147+
if #available(macOS 14, *) {
148+
AGDebugModifier.sharedGraphbitPattern = unsafeBitCast(graph, to: Int.self)
149+
}
150+
}
151+
152+
return withUnsafePointer(to: view) { pointer in
153+
let view = UnsafeRawPointer(pointer).assumingMemoryBound(to: _GraphValue<EmptyView>.self)
154+
return EmptyView._makeView(view: view.pointee, inputs: inputs)
155+
}
156+
}
157+
158+
static func _makeViewList(view: _GraphValue<Self>, inputs: _ViewListInputs) -> _ViewListOutputs {
159+
withUnsafePointer(to: view) { pointer in
160+
let view = UnsafeRawPointer(pointer).assumingMemoryBound(to: _GraphValue<EmptyView>.self)
161+
return EmptyView._makeViewList(view: view.pointee, inputs: inputs)
162+
}
163+
}
164+
}

Sources/DemoApp/DemoApp.swift

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
//
77

88
import SwiftUI
9-
import AGDebugKit
109

1110
@main
1211
@available(macOS 14.0, *)
@@ -18,19 +17,6 @@ struct DemoApp: App {
1817
NSApp.activate(ignoringOtherApps: true)
1918
NSApp.windows.first?.makeKeyAndOrderFront(nil)
2019
}
21-
22-
// Demo test code
23-
let emptyGraph = Graph()
24-
if let dict = emptyGraph.dict {
25-
print(dict)
26-
}
27-
let defaultGraph = Graph(nil)
28-
if let dict = defaultGraph.dict {
29-
print(dict)
30-
}
31-
if let dot = defaultGraph.dot {
32-
print(dot)
33-
}
3420
}
3521

3622
var body: some Scene {

0 commit comments

Comments
 (0)