wip
This commit is contained in:
parent
ce73da1bcb
commit
5d5e67f020
|
@ -178,13 +178,6 @@ let package = Package(
|
|||
condition: .when(platforms: [.wasi])
|
||||
),
|
||||
]
|
||||
// resources: [.copy("logo-header.png")],
|
||||
// linkerSettings: [
|
||||
// .unsafeFlags(
|
||||
// ["-Xlinker", "--stack-first", "-Xlinker", "-z", "-Xlinker", "stack-size=16777216"],
|
||||
// .when(platforms: [.wasi])
|
||||
// ),
|
||||
// ]
|
||||
),
|
||||
.executableTarget(
|
||||
name: "TokamakDemo",
|
||||
|
|
|
@ -3,64 +3,62 @@ import Foundation
|
|||
|
||||
@main
|
||||
struct TokamakApp: App {
|
||||
static let _configuration: _AppConfiguration = .init(reconciler: .fiber(useDynamicLayout: false))
|
||||
var body: some Scene { WindowGroup("Tokamak App") { ContentView() } }
|
||||
static let _configuration: _AppConfiguration = .init(reconciler: .fiber(useDynamicLayout: false))
|
||||
var body: some Scene { WindowGroup("Tokamak App") { ContentView() } }
|
||||
}
|
||||
|
||||
enum State {
|
||||
case a
|
||||
case b([String])
|
||||
case c(String, [Int])
|
||||
case d(String, [Int], String)
|
||||
case a
|
||||
case b([String])
|
||||
case c(String, [Int])
|
||||
case d(String, [Int], String)
|
||||
}
|
||||
|
||||
final class StateManager: ObservableObject {
|
||||
private init() { }
|
||||
static let shared = StateManager()
|
||||
private init() { }
|
||||
static let shared = StateManager()
|
||||
|
||||
@Published var state = State.a //b(["eins", "2", "III"])
|
||||
@Published var state = State.a //b(["eins", "2", "III"])
|
||||
}
|
||||
|
||||
struct ContentView: View {
|
||||
@ObservedObject var sm = StateManager.shared
|
||||
@ObservedObject var sm = StateManager.shared
|
||||
|
||||
var body: some View {
|
||||
switch sm.state {
|
||||
case .a:
|
||||
// VStack {
|
||||
Button("go to b") {
|
||||
sm.state = .b(["eins", "zwei", "drei"])
|
||||
}
|
||||
// }
|
||||
case .b(let arr):
|
||||
VStack {
|
||||
Text("b:")
|
||||
ForEach(arr, id: \.self) { s in
|
||||
Button(s) {
|
||||
sm.state = .c(s, s == "zwei" ? [1, 2] : [1])
|
||||
}
|
||||
}
|
||||
}
|
||||
case .c(let str, let ints):
|
||||
VStack {
|
||||
Text("c \(str)")
|
||||
.font(.headline)
|
||||
Text("hello there")
|
||||
ForEach(ints, id: \.self) { i in
|
||||
let d = "i = \(i)"
|
||||
Button(d) {
|
||||
sm.state = .d(str, ints, d)
|
||||
}
|
||||
}
|
||||
}
|
||||
case .d(_, let ints, let other):
|
||||
VStack {
|
||||
Text("d \(other)")
|
||||
Text("count \(ints.count)")
|
||||
Button("back") {
|
||||
sm.state = .a
|
||||
}
|
||||
}
|
||||
var body: some View {
|
||||
switch sm.state {
|
||||
case .a:
|
||||
Button("go to b") {
|
||||
sm.state = .b(["eins", "zwei", "drei"])
|
||||
}
|
||||
case .b(let arr):
|
||||
VStack {
|
||||
Text("b:")
|
||||
ForEach(arr, id: \.self) { s in
|
||||
Button(s) {
|
||||
sm.state = .c(s, s == "zwei" ? [1, 2] : [1])
|
||||
}
|
||||
}
|
||||
}
|
||||
case .c(let str, let ints):
|
||||
VStack {
|
||||
Text("c \(str)")
|
||||
.font(.headline)
|
||||
Text("hello there")
|
||||
ForEach(ints, id: \.self) { i in
|
||||
let d = "i = \(i)"
|
||||
Button(d) {
|
||||
sm.state = .d(str, ints, d)
|
||||
}
|
||||
}
|
||||
}
|
||||
case .d(_, let ints, let other):
|
||||
VStack {
|
||||
Text("d \(other)")
|
||||
Text("count \(ints.count)")
|
||||
Button("back") {
|
||||
sm.state = .a
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,13 +34,14 @@ extension FiberReconciler.Fiber: CustomDebugStringConvertible {
|
|||
proposal: .unspecified
|
||||
)
|
||||
return """
|
||||
\(spaces)\(String(describing: typeInfo?.type ?? Any.self)
|
||||
.split(separator: "<")[0])\(element != nil ? "(\(element!))" : "") {\(element != nil ?
|
||||
"\n\(spaces)geometry: \(geometry)" :
|
||||
"")
|
||||
\(child?.flush(level: level + 2) ?? "")
|
||||
\(spaces)}
|
||||
\(spaces)\(debugDescription)\(element != nil ? "(\(element!))" : "")
|
||||
\(child?.flush(level: level + 2) ?? "")\
|
||||
\(sibling?.flush(level: level) ?? "")
|
||||
"""
|
||||
}
|
||||
|
||||
public var recursiveDescription: String {
|
||||
flush()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -270,6 +270,14 @@ public final class FiberReconciler<Renderer: FiberRenderer> {
|
|||
self.alternate = current
|
||||
current = alternate
|
||||
|
||||
print("""
|
||||
reconcile done
|
||||
mutations were:
|
||||
\(visitor.mutations.map { " \($0)" }.joined(separator: "\n"))
|
||||
alternate is \(self.alternate.recursiveDescription)
|
||||
current is \(current.recursiveDescription)
|
||||
""")
|
||||
|
||||
isReconciling = false
|
||||
|
||||
for action in afterReconcileActions {
|
||||
|
|
|
@ -209,6 +209,7 @@ struct ReconcilePass: FiberReconcilerPass {
|
|||
}
|
||||
alternateSibling = currentAltSibling.sibling
|
||||
}
|
||||
node.fiber?.alternate?.sibling = nil
|
||||
guard let parent = node.parent else { return }
|
||||
// When we walk back to the root, exit
|
||||
guard parent !== root.fiber?.alternate else { return }
|
||||
|
@ -241,6 +242,7 @@ struct ReconcilePass: FiberReconcilerPass {
|
|||
}
|
||||
let el = R.ElementType(from: content)
|
||||
node.fiber?.element = el
|
||||
node.fiber?.alternate?.element = el
|
||||
return .insert(element: el, parent: parent, index: index)
|
||||
}
|
||||
|
||||
|
@ -256,6 +258,7 @@ struct ReconcilePass: FiberReconcilerPass {
|
|||
if node.fiber?.typeInfo?.type != node.fiber?.alternate?.typeInfo?.type {
|
||||
let el = R.ElementType(from: content)
|
||||
node.fiber?.element = el
|
||||
node.fiber?.alternate?.element = el
|
||||
return .replace(parent: parent, previous: altElement, replacement: el)
|
||||
} else if content != altElement.content {
|
||||
node.fiber?.element = altElement
|
||||
|
@ -272,10 +275,12 @@ struct ReconcilePass: FiberReconcilerPass {
|
|||
}
|
||||
|
||||
if let alt = node.fiber?.alternate?.element,
|
||||
let parent = node.fiber?.elementParent?.element,
|
||||
let parent = node.fiber?.alternate?.elementParent?.element,
|
||||
node.newContent == nil
|
||||
{
|
||||
node.fiber?.element = nil
|
||||
node.fiber?.elementIndex = 0
|
||||
if let p = node.fiber?.elementParent { caches.elementIndices[ObjectIdentifier(p)]? -= 1 }
|
||||
return .remove(element: alt, parent: parent)
|
||||
}
|
||||
|
||||
|
|
|
@ -298,7 +298,6 @@ public struct DOMFiberRenderer: FiberRenderer {
|
|||
|
||||
public func commit(_ mutations: [Mutation<Self>]) {
|
||||
for mutation in mutations {
|
||||
print(mutation)
|
||||
switch mutation {
|
||||
case let .insert(newElement, parent, index):
|
||||
let element = createElement(newElement)
|
||||
|
|
|
@ -106,11 +106,23 @@ public final class TestFiberElement: FiberElement, CustomStringConvertible {
|
|||
}
|
||||
|
||||
public var description: String {
|
||||
"""
|
||||
\(content.renderedValue)
|
||||
\(children.map { " \($0.description)" }.joined(separator: "\n"))
|
||||
\(content.closingTag)
|
||||
"""
|
||||
let memoryAddress = String(format: "%010p", unsafeBitCast(self, to: Int.self))
|
||||
return content.renderedValue + " (\(memoryAddress)) [\(children.count)]"
|
||||
}
|
||||
|
||||
public var recursiveDescription: String {
|
||||
var d = description
|
||||
if !children.isEmpty {
|
||||
d.append("\n")
|
||||
d.append(
|
||||
children
|
||||
.flatMap { $0.recursiveDescription.components(separatedBy:"\n").map { " \($0)"} }
|
||||
.joined(separator: "\n")
|
||||
)
|
||||
d.append("\n")
|
||||
}
|
||||
d.append(content.closingTag)
|
||||
return d
|
||||
}
|
||||
|
||||
public init(renderedValue: String, closingTag: String) {
|
||||
|
@ -169,6 +181,8 @@ public struct TestFiberRenderer: FiberRenderer {
|
|||
case let .replace(parent, previous, replacement):
|
||||
guard let index = parent.children.firstIndex(where: { $0 === previous })
|
||||
else { continue }
|
||||
let grandchildren = parent.children[index].children
|
||||
replacement.children = grandchildren
|
||||
parent.children[index] = replacement
|
||||
case let .layout(element, geometry):
|
||||
element.geometry = geometry
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
// Copyright 2022 Tokamak contributors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// Created by Lukas Stabe on 10/30/22.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
@_spi(TokamakCore) @testable import TokamakCore
|
||||
import TokamakTestRenderer
|
||||
|
||||
final class VaryingPrimitivenessTests: XCTestCase {
|
||||
func testVaryingPrimitiveness() {
|
||||
enum State {
|
||||
case a
|
||||
case b([String])
|
||||
case c(String, [Int])
|
||||
case d(String, [Int], String)
|
||||
}
|
||||
|
||||
final class StateManager: ObservableObject {
|
||||
private init() { }
|
||||
static let shared = StateManager()
|
||||
|
||||
@Published var state = State.a //b(["eins", "2", "III"])
|
||||
}
|
||||
|
||||
struct ContentView: View {
|
||||
@ObservedObject var sm = StateManager.shared
|
||||
|
||||
var body: some View {
|
||||
switch sm.state {
|
||||
case .a:
|
||||
Button("go to b") {
|
||||
sm.state = .b(["eins", "zwei", "drei"])
|
||||
}.identified(by: "a.1")
|
||||
case .b(let arr):
|
||||
VStack {
|
||||
Text("b:")
|
||||
ForEach(arr, id: \.self) { s in
|
||||
Button(s) {
|
||||
sm.state = .c(s, s == "zwei" ? [1, 2] : [1])
|
||||
}.identified(by: "b.\(s)")
|
||||
}
|
||||
}
|
||||
case .c(let str, let ints):
|
||||
VStack {
|
||||
Text("c \(str)")
|
||||
.font(.headline)
|
||||
Text("hello there")
|
||||
ForEach(ints, id: \.self) { i in
|
||||
let d = "i = \(i)"
|
||||
Button(d) {
|
||||
sm.state = .d(str, ints, d)
|
||||
}.identified(by: "c." + d)
|
||||
}
|
||||
}
|
||||
case .d(_, let ints, let other):
|
||||
VStack {
|
||||
Text("d \(other)")
|
||||
Text("count \(ints.count)")
|
||||
Button("back") {
|
||||
sm.state = .a
|
||||
}.identified(by: "d.back")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let reconciler = TestFiberRenderer(.root, size: .zero).render(ContentView())
|
||||
let root = reconciler.renderer.rootElement
|
||||
|
||||
XCTAssertEqual(root.children.count, 1) // button style
|
||||
XCTAssertEqual(root.children[0].children.count, 1) // text
|
||||
|
||||
reconciler.findView(id: "a.1", as: Button<Text>.self).action?()
|
||||
reconciler.findView(id: "a.1", as: Button<Text>.self).action?()
|
||||
|
||||
XCTAssertEqual(root.children.count, 1)
|
||||
XCTAssert(root.children[0].description.contains("VStack"))
|
||||
XCTAssertEqual(root.children[0].children.count, 4) // stack content
|
||||
XCTAssert(root.children[0].children[0].description.contains("Text"))
|
||||
XCTAssert(root.children[0].children[1].description.contains("ButtonStyle"))
|
||||
|
||||
reconciler.findView(id: "b.zwei", as: Button<Text>.self).action?()
|
||||
reconciler.findView(id: "b.zwei", as: Button<Text>.self).action?()
|
||||
|
||||
XCTAssertEqual(root.children.count, 1)
|
||||
XCTAssert(root.children[0].description.contains("VStack"))
|
||||
XCTAssertEqual(root.children[0].children.count, 4) // stack content
|
||||
XCTAssert(root.children[0].children[0].description.contains("Text"))
|
||||
XCTAssert(root.children[0].children[1].description.contains("Text"))
|
||||
XCTAssert(root.children[0].children[2].description.contains("ButtonStyle"))
|
||||
|
||||
reconciler.findView(id: "c.i = 2", as: Button<Text>.self).action?()
|
||||
reconciler.findView(id: "c.i = 2", as: Button<Text>.self).action?()
|
||||
|
||||
XCTAssertEqual(root.children[0].children.count, 3) // stack content
|
||||
|
||||
reconciler.findView(id: "d.back", as: Button<Text>.self).action?()
|
||||
reconciler.findView(id: "d.back", as: Button<Text>.self).action?()
|
||||
|
||||
XCTAssertEqual(root.children.count, 1)
|
||||
XCTAssert(root.children[0].description.contains("ButtonStyle"))
|
||||
XCTAssertEqual(root.children[0].children.count, 1)
|
||||
XCTAssert(root.children[0].children[0].description.contains("Text"))
|
||||
|
||||
reconciler.findView(id: "a.1", as: Button<Text>.self).action?()
|
||||
reconciler.findView(id: "a.1", as: Button<Text>.self).action?()
|
||||
|
||||
XCTAssertEqual(root.children.count, 1)
|
||||
XCTAssert(root.children[0].description.contains("VStack"))
|
||||
XCTAssertEqual(root.children[0].children.count, 4) // stack content
|
||||
XCTAssert(root.children[0].children[0].description.contains("Text"))
|
||||
XCTAssert(root.children[0].children[1].description.contains("ButtonStyle"))
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue