diff --git a/.swiftformat b/.swiftformat
index 6a52dfc9c..eef2a89ec 100644
--- a/.swiftformat
+++ b/.swiftformat
@@ -5,13 +5,19 @@
--asynccapturing
--beforemarks
--binarygrouping 4,8
+--callsiteparen default
--categorymark "MARK: %c"
--classthreshold 0
--closingparen balanced
--closurevoid remove
--commas always
+--complexattrs preserve
+--computedvarattrs preserve
+--condassignment after-property
--conflictmarkers reject
+--dateformat system
--decimalgrouping 3,6
+--doccomments before-declarations
--elseposition same-line
--emptybraces no-space
--enumnamespaces always
@@ -30,11 +36,12 @@
--header ignore
--hexgrouping 4,8
--hexliteralcase uppercase
---ifdef no-indent
+--ifdef indent
--importgrouping alpha
--indent 4
--indentcase false
--indentstrings false
+--initcodernil false
--lifecycle
--lineaftermarks true
--linebreaks lf
@@ -42,34 +49,38 @@
--markextensions always
--marktypes always
--maxwidth none
---modifierorder
+--modifierorder nonisolated,public
--nevertrailing
+--nilinit remove
+--noncomplexattrs
--nospaceoperators
--nowrapoperators
--octalgrouping 4,8
--onelineforeach ignore
--operatorfunc spaced
+--organizationmode visibility
--organizetypes actor,class,enum,struct
--patternlet hoist
--ranges spaced
---redundanttype inferred
+--redundanttype infer-locals-only
--self remove
--selfrequired
--semicolons inline
---shortoptionals always
+--shortoptionals except-properties
--smarttabs enabled
---someany false
+--someany true
+--storedvarattrs preserve
--stripunusedargs always
--structthreshold 0
---swiftversion 5.10
--tabwidth unspecified
--throwcapturing
+--timezone system
--trailingclosures
---trimwhitespace nonblank-lines
+--trimwhitespace always
--typeattributes preserve
--typeblanklines remove
+--typedelimiter space-after
--typemark "MARK: - %t"
---varattributes preserve
--voidtype void
--wraparguments preserve
--wrapcollections preserve
@@ -82,5 +93,4 @@
--wraptypealiases preserve
--xcodeindentation disabled
--yodaswap always
---disable blankLineAfterImports,wrapMultilineStatementBraces
---enable acronyms,blankLinesBetweenImports
+--disable unusedArguments
diff --git a/Sources/OpenSwiftUICore/Data/Transaction/TransitionTraitKey.swift b/Sources/OpenSwiftUICore/Data/Transaction/TransitionTraitKey.swift
new file mode 100644
index 000000000..4118102c5
--- /dev/null
+++ b/Sources/OpenSwiftUICore/Data/Transaction/TransitionTraitKey.swift
@@ -0,0 +1,22 @@
+//
+// TransitionTraitKey.swift
+// OpenSwiftUICore
+//
+// Audited for iOS 18.0
+// Status: WIP
+
+@usableFromInline
+struct CanTransitionTraitKey: _ViewTraitKey {
+ @inlinable
+ static var defaultValue: Bool { false }
+}
+
+@available(*, unavailable)
+extension CanTransitionTraitKey: Sendable {}
+
+extension ViewTraitCollection {
+ package var canTransition: Bool {
+ get { self[CanTransitionTraitKey.self] }
+ set { self[CanTransitionTraitKey.self] = newValue }
+ }
+}
diff --git a/Sources/OpenSwiftUICore/Graph/GraphInputs.swift b/Sources/OpenSwiftUICore/Graph/GraphInputs.swift
index 25f11c9e1..a9926734f 100644
--- a/Sources/OpenSwiftUICore/Graph/GraphInputs.swift
+++ b/Sources/OpenSwiftUICore/Graph/GraphInputs.swift
@@ -199,6 +199,13 @@ public struct _GraphInputs {
package struct Phase: Equatable {
var value: UInt32
+ @inline(__always)
+ static var isBeingRemovedBitCount: Int { 1 }
+ @inline(__always)
+ static var isBeingRemovedMask: UInt32 { (1 << isBeingRemovedBitCount) - 1}
+ @inline(__always)
+ static var resetSeedMask: UInt32 { ~isBeingRemovedMask }
+
@inlinable
package init(value: UInt32) {
self.value = value
@@ -211,19 +218,17 @@ public struct _GraphInputs {
@inlinable
package var resetSeed: UInt32 {
- get { value >> 1 }
- set { value = (newValue << 1) | (value & 1) }
+ get { value >> Self.isBeingRemovedBitCount }
+ set { value = (newValue << Self.isBeingRemovedBitCount) | (value & Self.isBeingRemovedMask) }
}
package var isBeingRemoved: Bool {
- get { value & 1 != 0 }
- set { value = (newValue ? 1 : 0) | (value & 0xFFFF_FFFE) }
+ get { (value & Self.isBeingRemovedMask) != 0 }
+ set { value = (newValue ? 1 : 0) | (value & Self.resetSeedMask) }
}
@inlinable
- package var isInserted: Bool {
- value & 1 == 0
- }
+ package var isInserted: Bool { !isBeingRemoved }
@inlinable
package mutating func merge(_ other: _GraphInputs.Phase) {
diff --git a/Sources/OpenSwiftUICore/Graph/GraphReuse.swift b/Sources/OpenSwiftUICore/Graph/GraphReuse.swift
index d9c9ec0e1..0c989fcd2 100644
--- a/Sources/OpenSwiftUICore/Graph/GraphReuse.swift
+++ b/Sources/OpenSwiftUICore/Graph/GraphReuse.swift
@@ -12,7 +12,7 @@ package import OpenGraphShims
package typealias Subgraph = OGSubgraph
package typealias Graph = OGGraph
-package final class IndirectAttributeMap {
+public final class IndirectAttributeMap {
#if canImport(Darwin)
package final let subgraph: Subgraph
package final var map: [AnyAttribute: AnyAttribute]
diff --git a/Sources/OpenSwiftUICore/Graph/ReuseTrace.swift b/Sources/OpenSwiftUICore/Graph/ReuseTrace.swift
index 72f9cf7d0..c63eb0025 100644
--- a/Sources/OpenSwiftUICore/Graph/ReuseTrace.swift
+++ b/Sources/OpenSwiftUICore/Graph/ReuseTrace.swift
@@ -34,7 +34,23 @@ package struct ReuseTrace {
package static func traceReuseViewInputsDifferentFailure() {
traceReuseFailure("reuse_inputsDifferent")
}
-
+
+ @inline(__always)
+ package static func traceReuseUnaryElementExpectedFailure(_ elementType: any Any.Type) {
+ traceReuseFailure("reuse_unaryElement")
+ }
+
+ @inline(__always)
+ package static func traceReuseInvalidSubgraphFailure(_ typeFoundInvalid: any Any.Type) {
+ // FIXME: ReuseTraceInternal.InvalidSubgraphFailure
+ traceReuseFailure("reuse_invalidSubgraph")
+ }
+
+ @inline(__always)
+ package static func traceReuseBodyMismatchedFailure() {
+ traceReuseFailure("reuse_bodyMismatched")
+ }
+
// TODO
final package class Recorder {
diff --git a/Sources/OpenSwiftUICore/Modifier/ViewModifier/CustomViewModifier.swift b/Sources/OpenSwiftUICore/Modifier/ViewModifier/CustomViewModifier.swift
index 45d470148..01d38e856 100644
--- a/Sources/OpenSwiftUICore/Modifier/ViewModifier/CustomViewModifier.swift
+++ b/Sources/OpenSwiftUICore/Modifier/ViewModifier/CustomViewModifier.swift
@@ -139,3 +139,56 @@ extension _ViewModifier_Content {
}
}
}
+
+// MARK: - BodyInput
+
+// FIXME
+private struct BodyInput {}
+
+private enum BodyInputElement: GraphReusable, Equatable {
+ typealias MakeViewBody = (_Graph, _ViewInputs) -> _ViewOutputs
+ typealias MakeViewListBody = (_Graph, _ViewListInputs) -> _ViewListOutputs
+
+ case view(MakeViewBody)
+ case list(MakeViewListBody)
+
+ static func == (lhs: BodyInputElement, rhs: BodyInputElement) -> Bool {
+ if case let .view(lhsBody) = lhs, case let .view(rhsBody) = rhs {
+ compareValues(lhsBody, rhsBody, options: .init(rawValue: 0x103))
+ } else if case let .list(lhsBody) = lhs, case let .list(rhsBody) = rhs{
+ compareValues(lhsBody, rhsBody, options: .init(rawValue: 0x103))
+ } else {
+ false
+ }
+ }
+
+ static var isTriviallyReusable: Bool {
+ _SemanticFeature_v5.isEnabled
+ }
+
+ func makeReusable(indirectMap: IndirectAttributeMap) {
+ return
+ }
+
+ func tryToReuse(by other: BodyInputElement, indirectMap: IndirectAttributeMap, testOnly: Bool) -> Bool {
+ switch self {
+ case let .view(makeViewBody):
+ guard case let .view(otherMakeViewBody) = other else {
+ ReuseTrace.traceReuseInternalFailure()
+ return false
+ }
+ return Self.isTriviallyReusable || compareValues(makeViewBody, otherMakeViewBody, options: .init(rawValue: 0x103))
+ case let .list(makeViewListBody):
+ guard case let .list(otherMakeViewListBody) = other else {
+ ReuseTrace.traceReuseInternalFailure()
+ return false
+ }
+ return Self.isTriviallyReusable || compareValues(makeViewListBody, otherMakeViewListBody, options: .init(rawValue: 0x103))
+ }
+ }
+}
+
+
+private struct BodyCountInput: ViewInput {
+ static var defaultValue: Stack<_ViewListCountInputs> { .init() }
+}
diff --git a/Sources/OpenSwiftUICore/Modifier/ViewModifier/ViewModifier.swift b/Sources/OpenSwiftUICore/Modifier/ViewModifier/ViewModifier.swift
index ca04f9f05..7732a0151 100644
--- a/Sources/OpenSwiftUICore/Modifier/ViewModifier/ViewModifier.swift
+++ b/Sources/OpenSwiftUICore/Modifier/ViewModifier/ViewModifier.swift
@@ -120,9 +120,7 @@ extension ViewModifier where Self: _GraphInputsModifier, Body == Never {
body: @escaping (_Graph, _ViewListInputs) -> _ViewListOutputs
) -> _ViewListOutputs {
var inputs = inputs
- inputs.withMutateGraphInputs { inputs in
- _makeInputs(modifier: modifier, inputs: &inputs)
- }
+ _makeInputs(modifier: modifier, inputs: &inputs.base)
let outputs = body(_Graph(), inputs)
return outputs
}
diff --git a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayList_StableIdentity.swift b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayList_StableIdentity.swift
index bab243615..e9fef0fa1 100644
--- a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayList_StableIdentity.swift
+++ b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayList_StableIdentity.swift
@@ -134,7 +134,7 @@ extension _GraphInputs {
pushScope(id: makeStableTypeData(type))
}
- package var stableIDScope: WeakAttribute<_DisplayList_StableIdentityScope>? {
+ package var stableIDScope: WeakAttribute? {
guard !options.contains(.needsStableDisplayListIDs) else {
return nil
}
diff --git a/Sources/OpenSwiftUICore/View/AnyView.swift b/Sources/OpenSwiftUICore/View/AnyView.swift
index f240353e5..3d6666e03 100644
--- a/Sources/OpenSwiftUICore/View/AnyView.swift
+++ b/Sources/OpenSwiftUICore/View/AnyView.swift
@@ -323,6 +323,10 @@ private struct AnyViewList: StatefulRule, AsyncAttribute {
}
struct Transform: _ViewList_SublistTransform_Item {
+ func bindID(_ id: inout _ViewList_ID) {
+ // TODO
+ }
+
func apply(sublist: inout _ViewList_Sublist) {
item.bindID(&sublist.id)
sublist.elements = item.wrapping(sublist.elements)
diff --git a/Sources/OpenSwiftUICore/View/CustomView.swift b/Sources/OpenSwiftUICore/View/CustomView.swift
index 6fc660ef6..1d49ab8be 100644
--- a/Sources/OpenSwiftUICore/View/CustomView.swift
+++ b/Sources/OpenSwiftUICore/View/CustomView.swift
@@ -44,9 +44,7 @@ extension View {
nonisolated package static func makeViewList(view: _GraphValue, inputs: _ViewListInputs) -> _ViewListOutputs {
let fields = DynamicPropertyCache.fields(of: Self.self)
var inputs = inputs
- let (body, buffer) = inputs.withMutateGraphInputs { inputs in
- makeBody(view: view, inputs: &inputs, fields: fields)
- }
+ let (body, buffer) = makeBody(view: view, inputs: &inputs.base, fields: fields)
let outputs = Body.makeDebuggableViewList(view: body, inputs: inputs)
if let buffer {
buffer.traceMountedProperties(to: body, fields: fields)
diff --git a/Sources/OpenSwiftUICore/View/EmptyView.swift b/Sources/OpenSwiftUICore/View/EmptyView.swift
index c154c13e5..111059a43 100644
--- a/Sources/OpenSwiftUICore/View/EmptyView.swift
+++ b/Sources/OpenSwiftUICore/View/EmptyView.swift
@@ -36,10 +36,7 @@ public struct EmptyView: PrimitiveView {
}
public static func _makeViewList(view: _GraphValue, inputs: _ViewListInputs) -> _ViewListOutputs {
- guard inputs.options.contains(.isNonEmptyParent) else {
- return _ViewListOutputs.emptyParentViewList(inputs: inputs)
- }
- return _ViewListOutputs.nonEmptyParentViewList(inputs: inputs)
+ .emptyViewList(inputs: inputs)
}
public static func _viewListCount(inputs: _ViewListCountInputs) -> Int? {
diff --git a/Sources/OpenSwiftUICore/View/Input/ViewList.swift b/Sources/OpenSwiftUICore/View/Input/ViewList.swift
new file mode 100644
index 000000000..67ef122ac
--- /dev/null
+++ b/Sources/OpenSwiftUICore/View/Input/ViewList.swift
@@ -0,0 +1,1961 @@
+//
+// ViewList.swift
+// OpenSwiftUICore
+//
+// Audited for iOS 18.0
+// Status: WIP
+// ID: 70E71091E926A1B09B75AAEB38F5AA3F (SwiftUI)
+// ID: E479C0E92CDD045BAF2EF653123E2E0B (SwiftUICore)
+
+import Foundation
+package import OpenGraphShims
+
+// MARK: - _ViewListInputs
+
+/// Input values to `View._makeViewList()`.
+public struct _ViewListInputs {
+ package var base: _GraphInputs
+ package var implicitID: Int
+
+ package struct Options: OptionSet {
+ package let rawValue: Int
+
+ package init(rawValue: Int) {
+ self.rawValue = rawValue
+ }
+
+ package static let canTransition: Options = .init(rawValue: 1 << 0)
+ package static let disableTransitions: Options = .init(rawValue: 1 << 1)
+ package static let requiresDepthAndSections: Options = .init(rawValue: 1 << 2)
+ package static let requiresNonEmptyGroupParent: Options = .init(rawValue: 1 << 3)
+ package static let isNonEmptyParent: Options = .init(rawValue: 1 << 4)
+ package static let resetHeaderStyleContext: Options = .init(rawValue: 1 << 5)
+ package static let resetFooterStyleContext: Options = .init(rawValue: 1 << 6)
+ package static let layoutPriorityIsTrait: Options = .init(rawValue: 1 << 7)
+ package static let requiresSections: Options = .init(rawValue: 1 << 8)
+ package static let tupleViewCreatesUnaryElements: Options = .init(rawValue: 1 << 9)
+ package static let previewContext: Options = .init(rawValue: 1 << 10)
+ package static let needsDynamicTraits: Options = .init(rawValue: 1 << 11)
+ package static let allowsNestedSections: Options = .init(rawValue: 1 << 12)
+ package static let sectionsConcatenateFooter: Options = .init(rawValue: 1 << 13)
+ package static let needsArchivedAnimationTraits: Options = .init(rawValue: 1 << 14)
+ package static let sectionsAreHierarchical: Options = .init(rawValue: 1 << 15)
+ }
+
+ package var options: _ViewListInputs.Options
+
+ private var _traits: OptionalAttribute
+
+ package var traits: Attribute? {
+ get { _traits.attribute }
+ set { _traits.attribute = newValue }
+ }
+
+ package var traitKeys: ViewTraitKeys?
+
+ package init(_ base: _GraphInputs, implicitID: Int = 0, options: _ViewListInputs.Options = .init()) {
+ self.base = base
+ self.implicitID = implicitID
+ self.options = options
+ self._traits = .init()
+ self.traitKeys = .init()
+ }
+
+ package init(_ base: _GraphInputs, implicitID: Int) {
+ self.base = base
+ self.implicitID = implicitID
+ self.options = []
+ self._traits = .init()
+ self.traitKeys = .init()
+ }
+
+ package init(_ base: _GraphInputs, options: _ViewListInputs.Options) {
+ self.base = base
+ self.implicitID = 0
+ self.options = options
+ self._traits = .init()
+ self.traitKeys = .init()
+ }
+
+ package init(_ base: _GraphInputs) {
+ self.base = base
+ self.implicitID = 0
+ self.options = []
+ self._traits = .init()
+ self.traitKeys = .init()
+ }
+
+ package subscript(input: T.Type) -> T.Value where T: ViewInput {
+ get { base[input] }
+ set { base[input] = newValue }
+ }
+
+ package subscript(input: T.Type) -> T.Value where T: ViewInput, T.Value: GraphReusable {
+ get { base[input] }
+ set { base[input] = newValue }
+ }
+
+ package var canTransition: Bool {
+ options.contains(.canTransition) && !options.contains(.disableTransitions)
+ }
+
+ package mutating func addTraitKey(_ key: K.Type) where K: _ViewTraitKey {
+ traitKeys?.insert(key)
+ }
+}
+
+// MARK: - _ViewListCountInputs
+
+/// Input values to `View._viewListCount()`.
+public struct _ViewListCountInputs {
+ package var customInputs: PropertyList
+ package var options: _ViewListInputs.Options
+ package var baseOptions: _GraphInputs.Options
+ package var customModifierTypes: [ObjectIdentifier]
+
+ package init(_ inputs: _ViewListInputs) {
+ customInputs = inputs.base.customInputs
+ options = inputs.options
+ baseOptions = inputs.base.options
+ customModifierTypes = []
+ }
+
+ package subscript(input: T.Type) -> T.Value where T: GraphInput {
+ get { customInputs[input] }
+ set { customInputs[input] = newValue }
+ }
+
+ package mutating func append(_ newValue: U, to key: T.Type) where T: GraphInput, T.Value == Stack {
+ var stack = self[key]
+ defer { self[key] = stack }
+ stack.push(newValue)
+ }
+
+ package mutating func popLast(_ key: T.Type) -> U? where T: GraphInput, T.Value == Stack {
+ var stack = self[key]
+ defer { self[key] = stack }
+ return stack.pop()
+ }
+
+ package var base: _GraphInputs {
+ var inputs = _GraphInputs.invalid
+ inputs.customInputs = customInputs
+ inputs.options = baseOptions
+ return inputs
+ }
+}
+
+@available(*, unavailable)
+extension _ViewListCountInputs: Sendable {}
+
+// MARK: - ViewListOutputs
+
+/// Output values from `View._makeViewList()`.
+public struct _ViewListOutputs {
+ package enum Views {
+ case staticList(any ViewList.Elements)
+ case dynamicList(Attribute, ListModifier?)
+ }
+
+ package var views: Views
+ package var nextImplicitID: Int
+ package var staticCount: Int?
+
+ package init(_ views: _ViewListOutputs.Views, nextImplicitID: Int, staticCount: Int?) {
+ self.views = views
+ self.nextImplicitID = nextImplicitID
+ self.staticCount = staticCount
+ }
+
+ package init(_ views: _ViewListOutputs.Views, nextImplicitID: Int) {
+ self.views = views
+ self.nextImplicitID = nextImplicitID
+ self.staticCount = nil
+ }
+
+ package class ListModifier {
+ init() {}
+
+ package func apply(to list: inout ViewList) {}
+ }
+}
+
+@available(*, unavailable)
+extension _ViewListOutputs: Sendable {}
+
+// MARK: - ViewList
+
+package protocol ViewList {
+ typealias ID = _ViewList_ID
+ typealias Elements = _ViewList_Elements
+ typealias Traits = ViewTraitCollection
+ typealias Node = _ViewList_Node
+ typealias Group = _ViewList_Group
+ typealias IteratorStyle = _ViewList_IteratorStyle
+ typealias Section = _ViewList_Section
+ typealias Sublist = _ViewList_Sublist
+ typealias SublistTransform = _ViewList_SublistTransform
+ typealias Subgraph = _ViewList_Subgraph
+ typealias Edit = _ViewList_Edit
+
+ func count(style: IteratorStyle) -> Int
+ func estimatedCount(style: IteratorStyle) -> Int
+ var traitKeys: ViewTraitKeys? { get }
+ var viewIDs: ID.Views? { get }
+ var traits: ViewTraitCollection { get }
+
+ typealias ApplyBody = (inout Int, IteratorStyle, Node, inout SublistTransform) -> Bool
+
+ @discardableResult
+ func applyNodes(
+ from start: inout Int,
+ style: IteratorStyle,
+ list: Attribute?,
+ transform: inout SublistTransform,
+ to body: ApplyBody
+ ) -> Bool
+ func edit(forID id: ID, since transaction: TransactionID) -> Edit?
+ func firstOffset(forID id: OtherID, style: IteratorStyle) -> Int? where OtherID: Hashable
+}
+
+// MARK: - ViewList.IteratorStyle
+
+package struct _ViewList_IteratorStyle: Equatable {
+ var value: UInt
+
+ @inline(__always)
+ static var applyGranularityBitCount: Int { 1 }
+ @inline(__always)
+ static var applyGranularityMask: UInt { (1 << applyGranularityBitCount) - 1 }
+ @inline(__always)
+ static var granularityMask: UInt { ~applyGranularityMask }
+
+ package var applyGranularity: Bool {
+ get { (value & Self.applyGranularityMask) != 0 }
+ set { value = (newValue ? 1 : 0) | (value & Self.granularityMask) }
+ }
+
+ package var granularity: Int {
+ get { Int(bitPattern: value >> Self.applyGranularityBitCount) }
+ set { value = (UInt(bitPattern: newValue) << Self.applyGranularityBitCount) | (value & Self.applyGranularityMask) }
+ }
+
+ package init(granularity: Int) {
+ value = UInt(bitPattern: granularity) << Self.applyGranularityBitCount
+ }
+
+ package init() {
+ self.init(granularity: 1)
+ }
+
+ package func applyGranularity(to count: Int) -> Int {
+ guard value != .zero else { return count }
+ return granularity * count
+ }
+
+ package func alignToPreviousGranularityMultiple(_ value: inout Int) {
+ guard value != .zero else { return }
+ let granularity = granularity
+ guard granularity != 1 else { return }
+ let diff = value - value / granularity * granularity
+ value -= diff
+ }
+
+ package func alignToNextGranularityMultiple(_ value: inout Int) {
+ let granularity = granularity
+ guard granularity != 1 else { return }
+ let diff = value - value / granularity * granularity
+ guard diff != .zero else { return }
+ value += (granularity - diff)
+ }
+}
+
+// MARK: - ViewList.Edit
+
+package enum _ViewList_Edit {
+ case inserted
+ case removed
+}
+
+// MARK: - ViewList.Sublist
+
+package struct _ViewList_Sublist {
+ package var start: Int
+ package var count: Int
+ package var id: ViewList.ID
+ package var elements: any ViewList.Elements
+ package var traits: ViewTraitCollection
+ package var list: Attribute?
+
+ package init(start: Int, count: Int, id: _ViewList_ID, elements: any ViewList.Elements, traits: ViewList.Traits, list: Attribute?) {
+ self.start = start
+ self.count = count
+ self.id = id
+ self.elements = elements
+ self.traits = traits
+ self.list = list
+ }
+}
+
+// MARK: - ViewList.SublistTransform
+
+package struct _ViewList_SublistTransform {
+ package typealias Item = _ViewList_SublistTransform_Item
+
+ package var items: [any Item]
+
+ package init() { items = [] }
+
+ package var isEmpty: Bool { items.isEmpty }
+
+ package mutating func push(_ item: T) where T: Item {
+ items.append(item)
+ }
+
+ package mutating func pop() {
+ items.removeLast()
+ }
+
+ package func apply(sublist: inout ViewList.Sublist) {
+ for item in items.reversed() {
+ item.apply(sublist: &sublist)
+ }
+ }
+
+ package func bindID(_ id: inout ViewList.ID) {
+ for item in items.reversed() {
+ item.bindID(&id)
+ }
+ }
+}
+
+// MARK: - ViewList.SublistTransform.Item
+
+package protocol _ViewList_SublistTransform_Item {
+ func apply(sublist: inout ViewList.Sublist)
+ func bindID(_ id: inout ViewList.ID)
+}
+
+// MARK: - ViewList.Node
+
+package enum _ViewList_Node {
+ case list(any ViewList, Attribute?)
+ case sublist(ViewList.Sublist)
+ case group(ViewList.Group)
+ case section(ViewList.Section)
+
+ package func count(style: ViewList.IteratorStyle) -> Int {
+ switch self {
+ case let .list(list, _):
+ list.count(style: style)
+ case let .sublist(sublist):
+ style.applyGranularity(to: sublist.count)
+ case let .group(group):
+ group.count(style: style)
+ case let .section(section):
+ section.count(style: style)
+ }
+ }
+
+ package func estimatedCount(style: ViewList.IteratorStyle) -> Int {
+ switch self {
+ case let .list(list, _):
+ list.estimatedCount(style: style)
+ case let .sublist(sublist):
+ style.applyGranularity(to: sublist.count)
+ case let .group(group):
+ group.estimatedCount(style: style)
+ case let .section(section):
+ section.estimatedCount(style: style)
+ }
+ }
+
+ @discardableResult
+ package func applyNodes(
+ from start: inout Int,
+ style: ViewList.IteratorStyle,
+ transform: inout ViewList.SublistTransform,
+ to body: ViewList.ApplyBody
+ ) -> Bool {
+ switch self {
+ case let .list(list, attribute):
+ return list.applyNodes(
+ from: &start,
+ style: style,
+ list: attribute,
+ transform: &transform,
+ to: body
+ )
+ case let .sublist(sublist):
+ let count = style.applyGranularity(to: sublist.count)
+ if start >= count {
+ start &-= count
+ return true
+ } else {
+ defer { start = 0 }
+ return body(&start, style, self, &transform)
+ }
+ case let .group(group):
+ return group.applyNodes(
+ from: &start,
+ style: style,
+ transform: &transform,
+ to: body
+ )
+ case let .section(section):
+ if section.isHierarchical {
+ let list = section.base.lists[0]
+ return list.list.applyNodes(
+ from: &start,
+ style: style,
+ list: list.attribute,
+ transform: &transform,
+ to: body
+ )
+ } else {
+ return section.base.applyNodes(
+ from: &start,
+ style: style,
+ transform: &transform,
+ to: body
+ )
+ }
+ }
+ }
+
+ @discardableResult
+ package func applyNodes(
+ from start: inout Int,
+ transform: inout _ViewList_SublistTransform,
+ to body: ViewList.ApplyBody
+ ) -> Bool {
+ applyNodes(
+ from: &start,
+ style: .init(),
+ transform: &transform,
+ to: body
+ )
+ }
+
+ @discardableResult
+ package func applySublists(
+ from start: inout Int,
+ style: ViewList.IteratorStyle,
+ transform: inout ViewList.SublistTransform,
+ to body: (ViewList.Sublist) -> Bool
+ ) -> Bool {
+ switch self {
+ case let .list(list, attribute):
+ return list.applySublists(
+ from: &start,
+ style: style,
+ list: attribute,
+ transform: &transform,
+ to: body
+ )
+ case let .sublist(sublist):
+ var sublist = sublist
+ let count = style.applyGranularity(to: sublist.count)
+ if start >= count {
+ start &-= count
+ return true
+ } else {
+ transform.apply(sublist: &sublist)
+ defer { start = 0 }
+ return body(sublist)
+ }
+ case let .group(group):
+ return group.applyNodes(
+ from: &start,
+ style: style,
+ transform: &transform
+ ) { start, style, node, transform in
+ node.applySublists(
+ from: &start,
+ style: style,
+ transform: &transform,
+ to: body
+ )
+ }
+ case let .section(section):
+ return section.applyNodes(
+ from: &start,
+ style: style,
+ transform: &transform
+ ) { start, style, node, info, transform in
+ node.applySublists(
+ from: &start,
+ style: style,
+ transform: &transform,
+ to: body
+ )
+ }
+ }
+ }
+
+ @discardableResult
+ package func applySublists(
+ from start: inout Int,
+ transform: inout ViewList.SublistTransform,
+ to body: (ViewList.Sublist) -> Bool
+ ) -> Bool {
+ applySublists(
+ from: &start,
+ style: .init(),
+ transform: &transform,
+ to: body
+ )
+ }
+
+ package func firstOffset(forID id: OtherID, style: ViewList.IteratorStyle) -> Int? where OtherID: Hashable {
+ switch self {
+ case let .list(list, _):
+ list.firstOffset(forID: id, style: style)
+ case .sublist:
+ nil
+ case let .group(group):
+ group.firstOffset(forID: id, style: style)
+ case let .section(section):
+ section.firstOffset(forID: id, style: style)
+ }
+ }
+}
+
+// MARK: - ViewList + Extension [Blocked by ID.Views]
+
+extension ViewList {
+ package var isEmpty: Bool { count == 0 }
+
+ package var count: Int {
+ count(style: .init())
+ }
+
+ package var estimatedCount: Int {
+ estimatedCount(style: .init())
+ }
+
+ @discardableResult
+ package func applySublists(
+ from start: inout Int,
+ style: IteratorStyle,
+ list: Attribute?,
+ transform: inout SublistTransform,
+ to body: (Sublist) -> Bool
+ ) -> Bool {
+ applyNodes(from: &start, style: style, list: list, transform: &transform) { start, style, node, transform in
+ node.applySublists(from: &start, style: style, transform: &transform, to: body)
+ }
+ }
+
+ @discardableResult
+ package func applySublists(
+ from start: inout Int,
+ list: Attribute?,
+ transform: inout SublistTransform,
+ to body: (Sublist) -> Bool
+ ) -> Bool {
+ applySublists(from: &start, style: .init(), list: list, transform: &transform, to: body)
+ }
+
+ @discardableResult
+ package func applySublists(
+ from start: inout Int,
+ style: IteratorStyle,
+ list: Attribute?,
+ to body: (Sublist) -> Bool
+ ) -> Bool {
+ var transform = SublistTransform()
+ return applySublists(from: &start, style: style, list: list, transform: &transform, to: body)
+ }
+
+ @discardableResult
+ package func applySublists(
+ from start: inout Int,
+ list: Attribute?,
+ to body: (Sublist) -> Bool
+ ) -> Bool {
+ applySublists(from: &start, style: .init(), list: list, to: body)
+ }
+
+ @discardableResult
+ package func applySublists(
+ from start: inout Int,
+ style: IteratorStyle,
+ to body: (Sublist) -> Bool
+ ) -> Bool {
+ applySublists(from: &start, style: style, list: nil, to: body)
+ }
+
+ @discardableResult
+ package func applySublists(
+ from start: inout Int,
+ to body: (Sublist) -> Bool
+ ) -> Bool {
+ applySublists(from: &start, style: .init(), list: nil, to: body)
+ }
+
+ package var allViewIDs: ID.Views {
+ if let viewIDs {
+ return viewIDs
+ } else {
+ var start = 0
+ let result = applySublists(from: &start, style: .init(), list: nil) { sublist in
+ // sublist.elements append
+ true
+ }
+ preconditionFailure("TODO")
+ }
+ }
+
+ package func applyIDs(
+ from start: inout Int,
+ style: IteratorStyle,
+ listAttribute: Attribute?,
+ transform: inout ViewList.SublistTransform,
+ to body: (ViewList.ID) -> Bool
+ ) -> Bool {
+ preconditionFailure("TODO")
+ }
+
+ package func applyIDs(
+ from start: inout Int,
+ listAttribute: Attribute?,
+ transform t: inout ViewList.SublistTransform,
+ to body: (ViewList.ID) -> Bool
+ ) -> Bool {
+ preconditionFailure("TODO")
+ }
+
+ package func applyIDs(
+ from start: inout Int,
+ listAttribute: Attribute?,
+ to body: (ViewList.ID) -> Bool
+ ) -> Bool {
+ preconditionFailure("TODO")
+ }
+
+ package func applyIDs(
+ from start: inout Int,
+ transform t: inout ViewList.SublistTransform,
+ to body: (ViewList.ID) -> Bool
+ ) -> Bool {
+ preconditionFailure("TODO")
+ }
+
+ package func firstOffset(of id: ViewList.ID.Canonical, style: IteratorStyle) -> Int? {
+ preconditionFailure("TODO")
+ }
+
+ package func firstOffset(of id: ViewList.ID.Canonical) -> Int? {
+ firstOffset(of: id, style: .init())
+ }
+}
+
+// MARK: - ViewList.Elements
+
+package protocol _ViewList_Elements {
+ typealias Body = (_ViewInputs, @escaping MakeElement) -> (_ViewOutputs?, Bool)
+ typealias MakeElement = (_ViewInputs) -> _ViewOutputs
+ typealias Release = _ViewList_ReleaseElements
+
+ var count: Int { get }
+
+ func makeElements(
+ from start: inout Int,
+ inputs: _ViewInputs,
+ indirectMap: IndirectAttributeMap?,
+ body: Body
+ ) -> (_ViewOutputs?, Bool)
+
+ func tryToReuseElement(
+ at index: Int,
+ by other: any ViewList.Elements,
+ at otherIndex: Int,
+ indirectMap: IndirectAttributeMap,
+ testOnly: Bool
+ ) -> Bool
+
+ func retain() -> Release?
+}
+
+extension ViewList.Elements {
+ @inline(__always)
+ package func makeAllElements(
+ inputs: _ViewInputs,
+ indirectMap: IndirectAttributeMap?,
+ body: (_ViewInputs, @escaping MakeElement) -> _ViewOutputs?
+ ) -> _ViewOutputs? {
+ withoutActuallyEscaping(body) { escapingBody in
+ let wrapper: Body = { inputs, makeElement in
+ (escapingBody(inputs, makeElement), true)
+ }
+ var start = 0
+ return makeElements(from: &start, inputs: inputs, indirectMap: indirectMap, body: wrapper).0
+ }
+ }
+
+ @inline(__always)
+ package func makeAllElements(
+ inputs: _ViewInputs,
+ body: (_ViewInputs, @escaping MakeElement) -> _ViewOutputs?
+ ) -> _ViewOutputs? {
+ makeAllElements(inputs: inputs, indirectMap: nil, body: body)
+ }
+
+ @inline(__always)
+ package func makeOneElement(
+ at index: Int,
+ inputs: _ViewInputs,
+ indirectMap: IndirectAttributeMap?,
+ body: (_ViewInputs, @escaping MakeElement) -> _ViewOutputs?
+ ) -> _ViewOutputs? {
+ withoutActuallyEscaping(body) { escapingBody in
+ let wrapper: Body = { inputs, makeElement in
+ (escapingBody(inputs, makeElement), false)
+ }
+ var start = index
+ return makeElements(from: &start, inputs: inputs, indirectMap: indirectMap, body: wrapper).0
+ }
+ }
+
+ @inline(__always)
+ package func makeOneElement(
+ at index: Int,
+ inputs: _ViewInputs,
+ body: (_ViewInputs, @escaping MakeElement) -> _ViewOutputs?
+ ) -> _ViewOutputs? {
+ makeOneElement(at: index, inputs: inputs, indirectMap: nil, body: body)
+ }
+
+ package func retain() -> Release? {
+ nil
+ }
+}
+
+// MARK: - View.List.ID
+
+@_spi(ForOpenSwiftUIOnly)
+public struct _ViewList_ID: Hashable {
+ package typealias Views = _ViewList_ID_Views
+
+ private var _index: Int32
+
+ package var index: Int {
+ get { Int(_index) }
+ set { _index = Int32(newValue) }
+ }
+
+ private var implicitID: Int32
+
+ private var explicitIDs: [Explicit]
+
+ package init(implicitID: Int) {
+ self._index = 0
+ self.implicitID = Int32(implicitID)
+ self.explicitIDs = []
+ }
+
+ package init() {
+ self._index = 0
+ self.implicitID = 0
+ self.explicitIDs = []
+ }
+
+ private struct Explicit: Equatable {
+ let id: AnyHashable2
+ let reuseID: Int
+ #if canImport(Darwin)
+ let owner: AnyAttribute
+ #endif
+ let isUnary: Bool
+ }
+
+ #if canImport(Darwin)
+ package static func explicit(_ id: ID, owner: AnyAttribute) -> ViewList.ID where ID: Hashable {
+ var viewListID = ViewList.ID()
+ viewListID.bind(explicitID: id, owner: owner, isUnary: true, reuseID: .zero)
+ return viewListID
+ }
+
+ package static func explicit(_ id: ID) -> ViewList.ID where ID: Hashable {
+ explicit(id, owner: .nil)
+ }
+ #endif
+
+ package func elementID(at index: Int) -> ViewList.ID {
+ var id = self
+ id.index = index
+ return id
+ }
+
+ package struct Canonical: Hashable, CustomStringConvertible {
+ private var _index: Int32
+
+ package var index: Int {
+ get { Int(_index) }
+ set { _index = Int32(newValue) }
+ }
+
+ private var implicitID: Int32
+
+ package var explicitID: AnyHashable2?
+
+ init(_index: Int32, implicitID: Int32, explicitID: AnyHashable2?) {
+ self._index = _index
+ self.implicitID = implicitID
+ self.explicitID = explicitID
+ }
+
+ package var requiresImplicitID: Bool { implicitID >= 0 }
+
+ package var description: String {
+ if let explicitID {
+ explicitID.description
+ } else {
+ "@\(_index)"
+ }
+ }
+ }
+
+ package var canonicalID: Canonical {
+ guard let explicitID = explicitIDs.first else {
+ return Canonical(_index: _index, implicitID: implicitID, explicitID: nil)
+ }
+ return Canonical(_index: _index, implicitID: explicitID.isUnary ? -1 : implicitID, explicitID: explicitID.id)
+ }
+
+ package struct ElementCollection: RandomAccessCollection, Equatable {
+ package var id: ViewList.ID
+ package var count: Int
+
+ package init(id: ViewList.ID, count: Int) {
+ self.id = id
+ self.count = count
+ }
+
+ package var startIndex: Int { .zero }
+ package var endIndex: Int { count }
+
+ package subscript(index: Int) -> ViewList.ID {
+ id.elementID(at: index)
+ }
+ }
+
+ package func elementIDs(count: Int) -> ElementCollection {
+ ElementCollection(id: self, count: count)
+ }
+
+ #if canImport(Darwin)
+ package mutating func bind(explicitID: ID, owner: AnyAttribute, isUnary: Bool, reuseID: Int) where ID: Hashable {
+ explicitIDs.append(Explicit(id: AnyHashable2(explicitID), reuseID: reuseID, owner: owner, isUnary: isUnary))
+ }
+
+ package mutating func bind(explicitID: ID, owner: AnyAttribute, reuseID: Int) where ID: Hashable {
+ bind(explicitID: explicitID, owner: owner, isUnary: false, reuseID: reuseID)
+ }
+
+ package mutating func bind(explicitID: ID, owner: AnyAttribute, isUnary: Bool) where ID: Hashable {
+ bind(explicitID: explicitID, owner: owner, isUnary: isUnary, reuseID: .zero)
+ }
+
+ package mutating func bind(explicitID: ID, owner: AnyAttribute) where ID: Hashable {
+ bind(explicitID: explicitID, owner: owner, isUnary: false, reuseID: .zero)
+ }
+ #endif
+
+ package var primaryExplicitID: AnyHashable2? { explicitIDs.first?.id }
+
+ package var allExplicitIDs: [AnyHashable2] { explicitIDs.map(\.id) }
+
+ #if canImport(Darwin)
+ package func explicitID(owner: AnyAttribute) -> ID? where ID: Hashable {
+ for explicitID in explicitIDs {
+ guard explicitID.owner == owner,
+ let id = explicitID.id.as(type: ID.self)
+ else { continue }
+ return id
+ }
+ return nil
+ }
+ #endif
+
+ package func explicitID(for idType: ID.Type) -> ID? where ID: Hashable {
+ for explicitID in explicitIDs {
+ guard let id = explicitID.id.as(type: ID.self)
+ else { continue }
+ return id
+ }
+ return nil
+ }
+
+ package func containsID(_ id: ID) -> Bool where ID: Hashable {
+ for explicitID in explicitIDs {
+ guard explicitID.id.as(type: ID.self) == id
+ else { continue }
+ return true
+ }
+ return false
+ }
+
+ public func hash(into hasher: inout Hasher) {
+ hasher.combine(_index)
+ hasher.combine(implicitID)
+ for explicitID in explicitIDs {
+ hasher.combine(explicitID.id)
+ #if canImport(Darwin)
+ hasher.combine(explicitID.owner)
+ #endif
+ }
+ }
+
+ package var reuseIdentifier: Int {
+ var hasher = Hasher()
+ hasher.combine(_index)
+ hasher.combine(implicitID)
+ for explicitID in explicitIDs {
+ hasher.combine(explicitID.reuseID)
+ }
+ return hasher.finalize()
+ }
+
+ final package class _Views: Views where Base: Equatable, Base: RandomAccessCollection, Base.Element == ViewList.ID, Base.Index == Int {
+ package let base: Base
+
+ package init(_ base: Base, isDataDependent: Bool) {
+ self.base = base
+ super.init(isDataDependent: isDataDependent)
+ }
+
+ override package var endIndex: Int {
+ base.endIndex
+ }
+
+ override package subscript(index: Int) -> ViewList.ID {
+ base[index]
+ }
+
+ override package func isEqual(to other: _ViewList_ID.Views) -> Bool {
+ guard let other = other as? Self else { return false }
+ return base == other.base
+ }
+ }
+
+ final package class JoinedViews: Views {
+ package let views: [(views: Views, endOffset: Int)]
+ package let count: Int
+
+ package init(_ views: [Views], isDataDependent: Bool) {
+ var offset = 0
+ var result: [(views: Views, endOffset: Int)] = []
+ for view in views {
+ offset += views.distance(from: 0, to: view.endIndex)
+ result.append((view, offset))
+ }
+ self.views = result
+ self.count = offset
+ super.init(isDataDependent: isDataDependent)
+ }
+
+ override package var endIndex: Int { count }
+
+ override package subscript(index: Int) -> ViewList.ID {
+ var index = index
+ // Copied from Swift Standard Library's _partitioningIndex(where:) implementation
+ var n = views.count
+ var l = 0
+ while n > 0 {
+ let half = n / 2
+ let mid = l + half
+ if views[mid].endOffset > index {
+ n = half
+ } else {
+ l = mid + 1
+ n -= half + 1
+ }
+ }
+
+ let targetIndex = l
+ if targetIndex != 0 {
+ index &-= views[targetIndex - 1].endOffset
+ }
+
+ let view = views[targetIndex]
+ // Copied from Swift Standard Library's _checkIndex(_:) implementation
+ Swift.precondition(index >= startIndex, "Negative Array index is out of range")
+ Swift.precondition(index <= endIndex, "Array index is out of range")
+ return view.views[index]
+ }
+
+ override package func isEqual(to other: ViewList.ID.Views) -> Bool {
+ guard let other = other as? JoinedViews,
+ count == other.count
+ else {
+ return false
+ }
+ guard !views.isEmpty else {
+ return true
+ }
+ for index in views.indices {
+ guard views[index].views.isEqual(to: other.views[index].views) else {
+ return false
+ }
+ }
+ return true
+ }
+ }
+}
+
+@_spi(ForOpenSwiftUIOnly)
+@available(*, unavailable)
+extension _ViewList_ID: Sendable {}
+
+// MARK: - ViewList.ID.Views
+
+@_spi(ForOpenSwiftUIOnly)
+open class _ViewList_ID_Views: RandomAccessCollection, Equatable {
+ final public let isDataDependent: Bool
+
+ final public var startIndex: Int { 0 }
+
+ open var endIndex: Int { preconditionFailure("") }
+
+ open subscript(index: Int) -> _ViewList_ID { preconditionFailure("") }
+
+ open func isEqual(to other: _ViewList_ID_Views) -> Bool { preconditionFailure("") }
+
+ package init(isDataDependent: Bool) {
+ self.isDataDependent = isDataDependent
+ }
+
+ package func withDataDependency() -> ViewList.ID.Views {
+ if isDataDependent {
+ self
+ } else {
+ ViewList.ID._Views(self, isDataDependent: true)
+ }
+ }
+
+ public static func == (lhs: _ViewList_ID_Views, rhs: _ViewList_ID_Views) -> Bool {
+ lhs.isEqual(to: rhs)
+ }
+}
+
+@_spi(ForOpenSwiftUIOnly)
+@available(*, unavailable)
+extension ViewList.ID.Views: Sendable {}
+
+// MARK: - UnaryViewGenerator
+
+public protocol UnaryViewGenerator {
+ func makeView(inputs: _ViewInputs, indirectMap: IndirectAttributeMap?) -> _ViewOutputs
+ func tryToReuse(by other: Self, indirectMap: IndirectAttributeMap, testOnly: Bool) -> Bool
+}
+
+public struct BodyUnaryViewGenerator: UnaryViewGenerator {
+ let body: ViewList.Elements.MakeElement
+
+ public func makeView(inputs: _ViewInputs, indirectMap: IndirectAttributeMap?) -> _ViewOutputs {
+ body(inputs)
+ }
+
+ public func tryToReuse(by other: BodyUnaryViewGenerator, indirectMap: IndirectAttributeMap, testOnly: Bool) -> Bool {
+ // FIXME
+ // 1. pass body to OGCompareValues directly instaed of withUnsafePointer
+ // 2. Add 0x103 case instead of rawValue
+ guard compareValues(body, other.body, mode: .init(rawValue: 0x103)) else {
+ ReuseTrace.traceReuseBodyMismatchedFailure()
+ Log.graphReuse("Reuse failed: \(Self.self) failed comparison")
+ return false
+ }
+ return true
+ }
+}
+
+public struct TypedUnaryViewGenerator: UnaryViewGenerator where V: View {
+ let view: WeakAttribute
+
+ public func makeView(inputs: _ViewInputs, indirectMap: IndirectAttributeMap?) -> _ViewOutputs {
+ guard var view = view.attribute else {
+ return .init()
+ }
+ if let indirectMap {
+ view.makeReusable(indirectMap: indirectMap)
+ }
+ return V.makeDebuggableView(view: _GraphValue(view), inputs: inputs)
+ }
+
+ public func tryToReuse(by other: TypedUnaryViewGenerator, indirectMap: IndirectAttributeMap, testOnly: Bool) -> Bool {
+ guard let view = view.attribute, let otherView = other.view.attribute else {
+ Log.graphReuse("Reuse failed: missing attribute for \(V.self)")
+ return false
+ }
+ return view.tryToReuse(by: otherView, indirectMap: indirectMap, testOnly: testOnly)
+ }
+}
+
+// MARK: - UnaryElements [WIP]
+
+private struct UnaryElements: ViewList.Elements where Generator: UnaryViewGenerator {
+ var body: Generator
+ var baseInputs: _GraphInputs
+
+ init(body: Generator, baseInputs: _GraphInputs) {
+ self.body = body
+ self.baseInputs = baseInputs
+ }
+
+ var count: Int { 1 }
+
+ func makeElements(
+ from start: inout Int,
+ inputs: _ViewInputs,
+ indirectMap: IndirectAttributeMap?,
+ body: Body
+ ) -> (_ViewOutputs?, Bool) {
+ preconditionFailure("TODO")
+ }
+
+ func tryToReuseElement(
+ at index: Int,
+ by other: any ViewList.Elements,
+ at otherIndex: Int,
+ indirectMap: IndirectAttributeMap,
+ testOnly: Bool
+ ) -> Bool {
+ guard let other = other as? UnaryElements else {
+ Log.graphReuse("Reuse failed: other is not Unary")
+ ReuseTrace.traceReuseUnaryElementExpectedFailure(type(of: other))
+ return false
+ }
+ // BodyInput
+ preconditionFailure("TODO")
+ }
+}
+
+// MARK: - ViewListOutputs + Extension [WIP]
+
+extension _ViewListOutputs {
+ private struct ApplyModifiers: Rule, AsyncAttribute {
+ @Attribute var base: any ViewList
+ let modifier: ListModifier
+
+ var value: any ViewList {
+ var value = base
+ modifier.apply(to: &value)
+ return value
+ }
+ }
+
+ private static func staticList(
+ _ elements: any ViewList.Elements,
+ inputs: _ViewListInputs,
+ staticCount: Int
+ ) -> _ViewListOutputs {
+ let implicitID = inputs.implicitID
+ let scope = inputs.base.stableIDScope
+ let traits = inputs.traits
+ let canTransition = inputs.canTransition
+ let views: Views
+ if scope != nil || traits != nil || canTransition {
+ views = .dynamicList(
+ Attribute(BaseViewList.Init(
+ elements: elements,
+ implicitID: implicitID,
+ canTransition: canTransition,
+ stableIDScope: scope,
+ traitKeys: inputs.traitKeys,
+ traits: .init(traits)
+ )),
+ nil
+ )
+ } else {
+ views = .staticList(elements)
+ }
+ return _ViewListOutputs(
+ views,
+ nextImplicitID: implicitID &+ staticCount,
+ staticCount: staticCount
+ )
+ }
+
+ // FIXME: Group
+ package static func nonEmptyParentViewList(inputs: _ViewListInputs) -> _ViewListOutputs {
+ preconditionFailure("TODO")
+ }
+
+ package static func unaryViewList(view: _GraphValue, inputs: _ViewListInputs) -> _ViewListOutputs where V: View {
+ let generator = TypedUnaryViewGenerator(view: .init(view.value))
+ let elements = UnaryElements(body: generator, baseInputs: inputs.base)
+ return staticList(elements, inputs: inputs, staticCount: 1)
+ }
+
+ package static func unaryViewList(viewType: T.Type = T.self, inputs: _ViewListInputs, body: @escaping ViewList.Elements.MakeElement) -> _ViewListOutputs {
+ let generator = BodyUnaryViewGenerator(body: body)
+ let elements = UnaryElements(body: generator, baseInputs: inputs.base)
+ return staticList(elements, inputs: inputs, staticCount: 1)
+ }
+
+ package static func emptyViewList(inputs: _ViewListInputs) -> _ViewListOutputs {
+ if inputs.options.contains(.isNonEmptyParent) {
+ nonEmptyParentViewList(inputs: inputs)
+ } else {
+ staticList(EmptyViewListElements(), inputs: inputs, staticCount: 0)
+ }
+ }
+
+ package func makeAttribute(inputs: _ViewListInputs) -> Attribute {
+ switch views {
+ case let .staticList(elements):
+ Attribute(value: BaseViewList(
+ elements: elements,
+ implicitID: nextImplicitID,
+ canTransition: inputs.canTransition,
+ stableIDScope: inputs.base.stableIDScope,
+ traitKeys: .init(),
+ traits: .init()
+ ))
+ case let .dynamicList(attribute, modifier):
+ if let modifier {
+ Attribute(ApplyModifiers(base: attribute, modifier: modifier))
+ } else {
+ attribute
+ }
+ }
+ }
+
+ package func makeAttribute(viewInputs: _ViewInputs) -> Attribute {
+ switch views {
+ case let .staticList(elements):
+ Attribute(value: BaseViewList(
+ elements: elements,
+ implicitID: nextImplicitID,
+ canTransition: false,
+ stableIDScope: viewInputs.base.stableIDScope,
+ traitKeys: .init(),
+ traits: .init()
+ ))
+ case let .dynamicList(attribute, modifier):
+ if let modifier {
+ Attribute(ApplyModifiers(base: attribute, modifier: modifier))
+ } else {
+ attribute
+ }
+ }
+ }
+
+ package static func makeModifiedList(list: Attribute, modifier: ListModifier?) -> Attribute {
+ if let modifier {
+ Attribute(ApplyModifiers(base: list, modifier: modifier))
+ } else {
+ list
+ }
+ }
+
+ package mutating func multiModifier(_ modifier: _GraphValue, inputs: _ViewListInputs) where T: ViewModifier {
+ preconditionFailure("TODO")
+ }
+
+ package static func concat(_ outputs: [_ViewListOutputs], inputs: _ViewListInputs) -> _ViewListOutputs {
+ preconditionFailure("TODO")
+ }
+}
+
+// MARK: - BaseViewList
+
+private struct BaseViewList: ViewList {
+ var elements: any Elements
+ var implicitID: Int
+ var traitKeys: ViewTraitKeys?
+ var traits: ViewTraitCollection
+
+ init(
+ elements: any Elements,
+ implicitID: Int,
+ canTransition: Bool,
+ stableIDScope: WeakAttribute?,
+ traitKeys: ViewTraitKeys?,
+ traits: ViewTraitCollection
+ ) {
+ self.elements = elements
+ self.implicitID = implicitID
+ self.traitKeys = traitKeys
+ self.traits = traits
+ if canTransition {
+ self.traits.canTransition = true
+ }
+ if let stableIDScope {
+ self.traits[DisplayList.StableIdentityScope.self] = stableIDScope
+ }
+ }
+
+ func count(style: IteratorStyle) -> Int {
+ style.applyGranularity(to: elements.count)
+ }
+
+ func estimatedCount(style: IteratorStyle) -> Int {
+ style.applyGranularity(to: elements.count)
+ }
+
+ var viewIDs: ID.Views? {
+ ID._Views(
+ ID.ElementCollection(
+ id: ID(implicitID: implicitID),
+ count: elements.count),
+ isDataDependent: false
+ )
+ }
+
+ func applyNodes(
+ from start: inout Int,
+ style: IteratorStyle,
+ list: Attribute?,
+ transform: inout SublistTransform,
+ to body: ApplyBody
+ ) -> Bool {
+ let count = count(style: style)
+ guard start < count else {
+ start -= count
+ return true
+ }
+ let sublist = Sublist(
+ start: start,
+ count: count,
+ id: ViewList.ID(implicitID: implicitID),
+ elements: elements,
+ traits: traits,
+ list: list
+ )
+ defer { start = 0 }
+ return body(&start, style, .sublist(sublist), &transform)
+ }
+
+ func edit(forID id: ID, since transaction: TransactionID) -> Edit? {
+ nil
+ }
+
+ func firstOffset(forID id: OtherID, style: IteratorStyle) -> Int? where OtherID : Hashable {
+ nil
+ }
+
+ struct Init: Rule, AsyncAttribute, CustomStringConvertible {
+ let elements: any Elements
+ let implicitID: Int
+ let canTransition: Bool
+ let stableIDScope: WeakAttribute?
+ let traitKeys: ViewTraitKeys?
+ @OptionalAttribute var traits: ViewTraitCollection?
+
+ var value: any ViewList {
+ BaseViewList(
+ elements: elements,
+ implicitID: implicitID,
+ canTransition: canTransition,
+ stableIDScope: stableIDScope,
+ traitKeys: traitKeys,
+ traits: traits ?? .init()
+ )
+ }
+
+ var description: String {
+ "Elements [\(elements.count)]"
+ }
+ }
+}
+
+// MARK: - EmptyViewList
+
+package struct EmptyViewList: ViewList {
+ package init() {}
+
+ package func count(style: IteratorStyle) -> Int { .zero }
+
+ package func estimatedCount(style: IteratorStyle) -> Int { .zero }
+
+ package var traitKeys: ViewTraitKeys? { .init() }
+
+ package var viewIDs: ID.Views? { .init(isDataDependent: false) }
+
+ package var traits: Traits { .init() }
+
+ package func applyNodes(
+ from start: inout Int,
+ style: IteratorStyle,
+ list: Attribute?,
+ transform: inout SublistTransform,
+ to body: ApplyBody
+ ) -> Bool {
+ true
+ }
+
+ package func edit(forID id: ID, since transaction: TransactionID) -> Edit? {
+ nil
+ }
+
+ package func firstOffset(forID id: OtherID, style: IteratorStyle) -> Int? where OtherID: Hashable {
+ nil
+ }
+}
+
+// MARK: - EmptyViewListElements
+
+package struct EmptyViewListElements: ViewList.Elements {
+ package init() {}
+
+ package var count: Int { 0 }
+
+ package func makeElements(
+ from start: inout Int,
+ inputs: _ViewInputs,
+ indirectMap: IndirectAttributeMap?,
+ body: Body
+ ) -> (_ViewOutputs?, Bool) {
+ return (nil, true)
+ }
+
+ package func tryToReuseElement(
+ at index: Int,
+ by other: any ViewList.Elements,
+ at otherIndex: Int,
+ indirectMap: IndirectAttributeMap,
+ testOnly: Bool
+ ) -> Bool {
+ guard other is EmptyViewListElements else {
+ Log.graphReuse("Reuse failed: other is not Empty")
+ return false
+ }
+ return true
+ }
+}
+
+// MARK: - ViewListSlice
+
+package struct ViewListSlice: ViewList {
+ let base: any ViewList
+ let bounds: Range
+
+ package var traitKeys: ViewTraitKeys? { nil }
+
+ package var traits: Traits { .init() }
+
+ final class ViewIDsSlice: ID.Views {
+ let base: ID.Views
+ let bounds: Range
+
+ init?(base: ID.Views?, bounds: Range) {
+ guard let base else {
+ return nil
+ }
+ self.base = base
+ self.bounds = bounds
+ super.init(isDataDependent: base.isDataDependent)
+ }
+
+ override var endIndex: Int { bounds.count }
+
+ override subscript(index: Int) -> ID {
+ base[index + bounds.lowerBound]
+ }
+ }
+
+ package var viewIDs: ID.Views? {
+ ViewIDsSlice(base: base.viewIDs, bounds: bounds)
+ }
+
+ package init(base: any ViewList, bounds: Range) {
+ self.base = base
+ self.bounds = bounds
+ }
+
+ package func count(style: IteratorStyle) -> Int {
+ bounds.count
+ }
+
+ package func estimatedCount(style: IteratorStyle) -> Int {
+ bounds.count
+ }
+
+ package func applyNodes(
+ from start: inout Int,
+ style: IteratorStyle,
+ list: Attribute?,
+ transform: inout SublistTransform,
+ to body: ApplyBody
+ ) -> Bool {
+ var start = bounds.lowerBound + start
+ return base.applyNodes(
+ from: &start,
+ style: style,
+ list: list,
+ transform: &transform
+ ) { start, style, node, transform in
+ guard start < bounds.upperBound else {
+ return false
+ }
+ return body(&start, style, node, &transform)
+ }
+ }
+
+ package func edit(forID id: ID, since transaction: TransactionID) -> Edit? {
+ base.edit(forID: id, since: transaction)
+ }
+
+ package func firstOffset(forID id: OtherID, style: IteratorStyle) -> Int? where OtherID: Hashable {
+ guard let offset = base.firstOffset(forID: id, style: style) else {
+ return nil
+ }
+ return offset - bounds.lowerBound
+ }
+}
+
+// MARK: - ViewList.Group
+
+package struct _ViewList_Group: ViewList {
+ package typealias AttributedList = (list: any ViewList, attribute: Attribute)
+
+ package var lists: [AttributedList]
+
+ package func count(style: IteratorStyle) -> Int {
+ lists.reduce(0) { $0 + $1.list.count(style: style) }
+ }
+
+ package func estimatedCount(style: IteratorStyle) -> Int {
+ lists.reduce(0) { $0 + $1.list.estimatedCount(style: style) }
+ }
+
+ package var traits: Traits { .init() }
+
+ package var traitKeys: ViewTraitKeys? {
+ var traitKeys = ViewTraitKeys()
+ for (list, _) in lists {
+ guard let keys = list.traitKeys else {
+ return nil
+ }
+ traitKeys.formUnion(keys)
+ }
+ return traitKeys
+ }
+
+ package var viewIDs: ID.Views? {
+ var views: [ID.Views] = []
+ var isDataDependent = false
+ for (list, _) in lists {
+ guard let ids = list.viewIDs else {
+ return nil
+ }
+ views.append(ids)
+ isDataDependent = isDataDependent || ids.isDataDependent
+ }
+ return ViewList.ID.JoinedViews(views, isDataDependent: isDataDependent)
+
+ }
+
+ package func applyNodes(
+ from start: inout Int,
+ style: IteratorStyle,
+ list: Attribute?,
+ transform: inout SublistTransform,
+ to body: ApplyBody
+ ) -> Bool {
+ body(&start, style, .group(self), &transform)
+ }
+
+ package func applyNodes(
+ from start: inout Int,
+ style: IteratorStyle,
+ transform: inout SublistTransform,
+ to body: ApplyBody
+ ) -> Bool {
+ for (list, attribute) in lists {
+ guard list.applyNodes(
+ from: &start,
+ style: style,
+ list: attribute,
+ transform: &transform,
+ to: body
+ ) else {
+ return false
+ }
+ }
+ return true
+ }
+
+ package func edit(forID id: ID, since transaction: TransactionID) -> Edit? {
+ for (list, _) in lists {
+ guard let edit = list.edit(forID: id, since: transaction) else {
+ continue
+ }
+ return edit
+ }
+ return nil
+ }
+
+ package func firstOffset(forID id: OtherID, style: IteratorStyle) -> Int? where OtherID: Hashable {
+ var previousCount = 0
+ for (list, _) in lists {
+ guard let offset = list.firstOffset(forID: id, style: style) else {
+ previousCount += list.count(style: style)
+ continue
+ }
+ return previousCount + offset
+ }
+ return nil
+ }
+
+ package struct Init: Rule, AsyncAttribute, CustomStringConvertible {
+ var lists: [Attribute]
+
+ package var value: any ViewList {
+ _ViewList_Group(lists: lists.map { ($0.value, $0) })
+ }
+
+ package var description: String { "∪" }
+ }
+}
+
+// MARK: - ViewList.Section
+
+package struct _ViewList_Section: ViewList {
+ package var id: UInt32
+ package var base: ViewList.Group
+ package var traits: ViewList.Traits
+ package var isHierarchical: Bool
+
+ package var header: ViewList.Group.AttributedList {
+ base.lists[0]
+ }
+
+ package var content: ViewList.Group.AttributedList {
+ base.lists[1]
+ }
+
+ package var footer: ViewList.Group.AttributedList {
+ base.lists[2]
+ }
+
+ package var traitKeys: ViewTraitKeys? {
+ base.traitKeys
+ }
+
+ package var viewIDs: ID.Views? {
+ if isHierarchical {
+ header.list.viewIDs
+ } else {
+ base.viewIDs
+ }
+ }
+
+ @inline(__always)
+ package func headerFooterStyle(for style: IteratorStyle) -> IteratorStyle {
+ var style = style
+ style.applyGranularity = (style.granularity != 1)
+ return style
+ }
+
+
+ package func count(style: IteratorStyle) -> Int {
+ if isHierarchical {
+ var style = style
+ style.applyGranularity = (style.granularity != 1)
+ return header.list.count(style: style)
+ } else {
+ var contentCount = content.list.count(style: style)
+ style.alignToNextGranularityMultiple(&contentCount)
+ let headerFooterStyle = headerFooterStyle(for: style)
+ let headerCount = header.list.count(style: headerFooterStyle)
+ let footerCount = footer.list.count(style: headerFooterStyle)
+ return contentCount + headerCount + footerCount
+ }
+ }
+
+ package func estimatedCount(style: IteratorStyle) -> Int {
+ if isHierarchical {
+ var style = style
+ style.applyGranularity = (style.granularity != 1)
+ return header.list.estimatedCount(style: style)
+ } else {
+ var contentEstimatedCount = content.list.estimatedCount(style: style)
+ style.alignToNextGranularityMultiple(&contentEstimatedCount)
+ let headerFooterStyle = headerFooterStyle(for: style)
+ let headerEstimatedCount = header.list.estimatedCount(style: headerFooterStyle)
+ let footerEstimatedCount = footer.list.estimatedCount(style: headerFooterStyle)
+ return contentEstimatedCount + headerEstimatedCount + footerEstimatedCount
+ }
+ }
+
+ package func applyNodes(
+ from start: inout Int,
+ style: IteratorStyle,
+ list: Attribute?,
+ transform: inout SublistTransform,
+ to body: ApplyBody
+ ) -> Bool {
+ body(&start, style, .section(self), &transform)
+ }
+
+ package struct Info {
+ package var id: UInt32
+ package var isHeader: Bool
+ package var isFooter: Bool
+ }
+
+ package func applyNodes(
+ from start: inout Int,
+ style: IteratorStyle,
+ transform: inout SublistTransform,
+ to body: (inout Int, IteratorStyle, Node, Info, inout SublistTransform) -> Bool
+ ) -> Bool {
+ style.alignToPreviousGranularityMultiple(&start)
+ let range = (0 ..< base.lists.count).prefix(isHierarchical ? 1 : .max)
+ let headerFooterStyle = headerFooterStyle(for: style)
+ for index in range {
+ let (list, attribute) = base.lists[index]
+ let result = list.applyNodes(
+ from: &start,
+ style: index == 1 ? style : headerFooterStyle,
+ list: attribute,
+ transform: &transform
+ ) { start, style, node, transform in
+ body(
+ &start,
+ style,
+ node,
+ Info(id: id, isHeader: index == 0, isFooter: index == 2),
+ &transform
+ )
+ }
+ guard result else {
+ return false
+ }
+ style.alignToNextGranularityMultiple(&start)
+ }
+ return true
+ }
+
+ package func edit(forID id: ID, since transaction: TransactionID) -> Edit? {
+ base.edit(forID: id, since: transaction)
+ }
+
+ package func firstOffset(forID id: OtherID, style: IteratorStyle) -> Int? where OtherID: Hashable {
+ let range = (0 ..< base.lists.count).prefix(isHierarchical ? 1 : .max)
+ let headerFooterStyle = headerFooterStyle(for: style)
+ var previousCount = 0
+ for index in range {
+ let (list, _) = base.lists[index]
+ let currentStyle = index == 1 ? style : headerFooterStyle
+ guard let offset = list.firstOffset(
+ forID: id,
+ style: currentStyle
+ ) else {
+ var count = list.count(style: currentStyle)
+ style.alignToNextGranularityMultiple(&count)
+ previousCount += count
+ continue
+ }
+ return previousCount + offset
+ }
+ return nil
+ }
+}
+
+// MARK: - ViewList.Subgraph
+
+@_spi(ForOpenSwiftUIOnly)
+open class _ViewList_Subgraph {
+ final package let subgraph: Subgraph
+ private(set) final package var refcount: UInt32
+
+ package init(subgraph: Subgraph) {
+ self.subgraph = subgraph
+ self.refcount = 1
+ }
+
+ final package func wrapping(_ base: any ViewList.Elements) -> any ViewList.Elements {
+ SubgraphElements(base: base, subgraph: self)
+ }
+
+ final package func wrapping(_ list: any ViewList) -> any ViewList {
+ SubgraphList(base: list, subgraph: self)
+ }
+
+ open func invalidate() {}
+
+ @inline(__always)
+ final var isValid: Bool {
+ guard refcount != 0 else {
+ return false
+ }
+ return subgraph.isValid
+ }
+
+ @inline(__always)
+ final func retain() {
+ refcount &+= 1
+ }
+
+ @inline(__always)
+ final func release(isInserted: Bool) {
+ refcount &-= 1
+ guard refcount == 0 else {
+ return
+ }
+ invalidate()
+ guard subgraph.isValid else {
+ return
+ }
+ subgraph.willInvalidate(isInserted: isInserted)
+ subgraph.invalidate()
+ }
+}
+
+@_spi(ForOpenSwiftUIOnly)
+@available(*, unavailable)
+extension ViewList.Subgraph: Sendable {}
+
+private struct SubgraphElements: ViewList.Elements {
+ let base: any ViewList.Elements
+ let subgraph: ViewList.Subgraph
+
+ var count: Int { base.count }
+
+ func makeElements(
+ from start: inout Int,
+ inputs: _ViewInputs,
+ indirectMap: IndirectAttributeMap?,
+ body: Body
+ ) -> (_ViewOutputs?, Bool) {
+ guard subgraph.isValid else {
+ return (nil, true)
+ }
+ return base.makeElements(from: &start, inputs: inputs, indirectMap: indirectMap, body: body)
+ }
+
+ func tryToReuseElement(
+ at index: Int,
+ by other: any ViewList.Elements,
+ at otherIndex: Int,
+ indirectMap: IndirectAttributeMap,
+ testOnly: Bool
+ ) -> Bool {
+ guard let otherSubgraphElement = other as? SubgraphElements,
+ otherSubgraphElement.subgraph.isValid
+ else {
+ ReuseTrace.traceReuseInvalidSubgraphFailure(type(of: other))
+ return false
+ }
+ return base.tryToReuseElement(
+ at: index,
+ by: otherSubgraphElement.base,
+ at: otherIndex,
+ indirectMap: indirectMap,
+ testOnly: testOnly
+ )
+ }
+
+ func retain() -> Release? {
+ guard subgraph.isValid else {
+ return nil
+ }
+ return Release(base: base.retain(), subgraph: subgraph)
+ }
+}
+
+private struct SubgraphList: ViewList {
+ var base: any ViewList
+ var subgraph: ViewList.Subgraph
+
+ func count(style: IteratorStyle) -> Int {
+ base.count(style: style)
+ }
+
+ func estimatedCount(style: IteratorStyle) -> Int {
+ base.estimatedCount(style: style)
+ }
+
+ var traitKeys: ViewTraitKeys? {
+ base.traitKeys
+ }
+
+ var viewIDs: ID.Views? {
+ base.viewIDs
+ }
+
+ var traits: ViewTraitCollection {
+ base.traits
+ }
+
+ func applyNodes(
+ from start: inout Int,
+ style: IteratorStyle,
+ list: Attribute?,
+ transform: inout SublistTransform,
+ to body: ApplyBody
+ ) -> Bool {
+ transform.push(Transform(subgraph: subgraph))
+ defer { transform.pop() }
+ return base.applyNodes(from: &start, style: style, list: list, transform: &transform, to: body)
+ }
+
+ func edit(forID id: ID, since transaction: TransactionID) -> Edit? {
+ base.edit(forID: id, since: transaction)
+ }
+
+ func firstOffset(forID id: OtherID, style: IteratorStyle) -> Int? where OtherID: Hashable {
+ base.firstOffset(forID: id, style: style)
+ }
+
+ struct Transform: ViewList.SublistTransform.Item {
+ var subgraph: ViewList.Subgraph
+
+ func apply(sublist: inout _ViewList_Sublist) {
+ sublist.elements = SubgraphElements(base: sublist.elements, subgraph: subgraph)
+ }
+
+ func bindID(_ id: inout _ViewList_ID) {}
+ }
+}
+
+// MARK: - ViewList.Elements.Release
+
+final package class _ViewList_ReleaseElements: Equatable {
+ var base: ViewList.Elements.Release?
+ var subgraph: ViewList.Subgraph
+
+ init(base: ViewList.Elements.Release?, subgraph: ViewList.Subgraph) {
+ self.base = base
+ self.subgraph = subgraph
+ }
+
+ deinit {
+ Update.ensure {
+ subgraph.release(isInserted: true)
+ }
+ }
+
+ package static func == (lhs: ViewList.Elements.Release, rhs: ViewList.Elements.Release) -> Bool {
+ guard lhs.subgraph === rhs.subgraph else {
+ return false
+ }
+ guard let lhsBase = lhs.base, let rhsBase = rhs.base else {
+ return lhs.base == nil && rhs.base == nil
+ }
+ return lhsBase == rhsBase
+ }
+}
+
+// FIXME: VariadicView Part
+
+// MARK: - _ViewList_View
+
+package struct _ViewList_View {
+ var elements: any ViewList.Elements
+ var id: _ViewList_ID
+ var index: Int
+ var count: Int
+ var contentSubgraph: Subgraph
+}
+
+// MARK: - ViewListVisitor
+
+protocol ViewListVisitor {
+ mutating func visit(view: _ViewList_View, traits: ViewTraitCollection) -> Bool
+}
diff --git a/Sources/OpenSwiftUICore/View/ViewList.swift b/Sources/OpenSwiftUICore/View/ViewList.swift
deleted file mode 100644
index c3e9ccaec..000000000
--- a/Sources/OpenSwiftUICore/View/ViewList.swift
+++ /dev/null
@@ -1,488 +0,0 @@
-//
-// ViewList.swift
-// OpenSwiftUI
-//
-// Audited for iOS 15.5
-// Status: WIP
-// ID: 70E71091E926A1B09B75AAEB38F5AA3F
-
-import Foundation
-package import OpenGraphShims
-
-// MARK: - _ViewListInputs
-
-/// Input values to `View._makeViewList()`.
-public struct _ViewListInputs {
- package var base: _GraphInputs
- package var implicitID: Int
-
- package struct Options: OptionSet {
- package let rawValue: Int
-
- package init(rawValue: Int) {
- self.rawValue = rawValue
- }
-
- package static let canTransition: Options = Options(rawValue: 1 << 0)
- package static let disableTransitions: Options = Options(rawValue: 1 << 1)
- package static let requiresDepthAndSections: Options = Options(rawValue: 1 << 2)
- package static let requiresNonEmptyGroupParent: Options = Options(rawValue: 1 << 3)
- package static let isNonEmptyParent: Options = Options(rawValue: 1 << 4)
- package static let resetHeaderStyleContext: Options = Options(rawValue: 1 << 5)
- package static let resetFooterStyleContext: Options = Options(rawValue: 1 << 6)
- package static let layoutPriorityIsTrait: Options = Options(rawValue: 1 << 7)
- package static let requiresSections: Options = Options(rawValue: 1 << 8)
- package static let tupleViewCreatesUnaryElements: Options = Options(rawValue: 1 << 9)
- package static let previewContext: Options = Options(rawValue: 1 << 10)
- package static let needsDynamicTraits: Options = Options(rawValue: 1 << 11)
- package static let allowsNestedSections: Options = Options(rawValue: 1 << 12)
- package static let sectionsConcatenateFooter: Options = Options(rawValue: 1 << 13)
- package static let needsArchivedAnimationTraits: Options = Options(rawValue: 1 << 14)
- package static let sectionsAreHierarchical: Options = Options(rawValue: 1 << 15)
- }
-
- package var options: _ViewListInputs.Options
- @OptionalAttribute var traits: ViewTraitCollection?
- package var traitKeys: ViewTraitKeys?
-
-
- // MARK: - base
-
- @inline(__always)
- mutating func withMutateGraphInputs(_ body: (inout _GraphInputs) -> R) -> R {
- body(&base)
- }
-}
-
-// MARK: - ViewListOutputs
-
-/// Output values from `View._makeViewList()`.
-public struct _ViewListOutputs {
- var views: Views
- var nextImplicitID: Int
- var staticCount: Int?
-
- enum Views {
- case staticList(_ViewList_Elements)
- case dynamicList(Attribute, ListModifier?)
- }
-
- class ListModifier {
- init() {}
-
- func apply(to: inout ViewList) {
- // TODO
- }
- }
-
- private static func staticList(_ elements: _ViewList_Elements, inputs: _ViewListInputs, staticCount: Int) -> _ViewListOutputs {
- preconditionFailure("TODO")
- }
-}
-
-extension _ViewListOutputs {
- package static func unaryViewList(view: _GraphValue, inputs: _ViewListInputs) -> _ViewListOutputs where V: View {
- preconditionFailure("TODO")
- }
-
-
- @inline(__always)
- static func emptyParentViewList(inputs: _ViewListInputs) -> _ViewListOutputs {
- staticList(EmptyElements(), inputs: inputs, staticCount: 0)
- }
-
- package static func nonEmptyParentViewList(inputs: _ViewListInputs) -> _ViewListOutputs {
- preconditionFailure("TODO")
- }
-}
-
-// MARK: - _ViewListCountInputs
-
-/// Input values to `View._viewListCount()`.
-public struct _ViewListCountInputs {
- var customInputs: PropertyList
- var options: _ViewListInputs.Options
- var baseOptions: _GraphInputs.Options
-
- subscript(_ type: Input.Type) -> Input.Value {
- get { customInputs[type] }
- set { customInputs[type] = newValue }
- }
-
- mutating func append(_ value: Value, to type: Input.Type) where Input.Value == [Value] {
- var values = self[type]
- values.append(value)
- self[type] = values
- }
-
- mutating func popLast(_ type: Input.Type) -> Value? where Input.Value == [Value] {
- var values = self[type]
- guard let value = values.popLast() else {
- return nil
- }
- self[type] = values
- return value
- }
-}
-
-// MARK: - _ViewList_ID
-
-package struct _ViewList_ID {
- var _index: Int32
- var implicitID: Int32
- private var explicitIDs: [Explicit]
-
- package class Views {
- let isDataDependent: Bool
- var endIndex: Int { preconditionFailure("") }
- subscript(index: Int) -> _ViewList_ID { preconditionFailure("") }
- func isEqual(to other: Views) -> Bool { preconditionFailure("") }
- init(isDataDependent: Bool) {
- self.isDataDependent = isDataDependent
- }
- // func withDataDependency() -> Views {}
- }
-
- final class _Views: Views where Base: Equatable, Base: RandomAccessCollection, Base.Element == _ViewList_ID, Base.Index == Int {
- let base: Base
-
- init(_ base: Base, isDataDependent: Bool) {
- self.base = base
- super.init(isDataDependent: isDataDependent)
- }
- override var endIndex: Int {
- base.endIndex
- }
-
- override subscript(index: Int) -> _ViewList_ID {
- base[index]
- }
-
- override func isEqual(to other: _ViewList_ID.Views) -> Bool {
- guard let other = other as? Self else { return false }
- return base == other.base
- }
- }
-
- final class JoinedViews: Views {
- let views: [(views: Views, endOffset: Int)]
- let count: Int
-
- init(_ views: [Views], isDataDependent: Bool) {
- var offset = 0
- var result: [(views: Views, endOffset: Int)] = []
- for view in views {
- offset += views.distance(from: 0, to: view.endIndex)
- result.append((view, offset))
- }
- self.views = result
- count = offset
- super.init(isDataDependent: isDataDependent)
- }
-
- override var endIndex: Int {
- views.endIndex
- }
-
- override subscript(index: Int) -> _ViewList_ID {
- preconditionFailure("TODO")
- }
-
- override func isEqual(to other: _ViewList_ID.Views) -> Bool {
- preconditionFailure("TODO")
- }
- }
-
- private struct Explicit: Equatable {
- let id: AnyHashable
- #if canImport(Darwin)
- let owner: AnyAttribute
- #endif
- let isUnary: Bool
- }
-
- struct Canonical {
- var _index: Int32
- var implicitID: Int32
- var explicitID: AnyHashable?
- }
-
- #if canImport(Darwin)
- mutating func bind(explicitID: AnyHashable, owner: AnyAttribute, isUnary: Bool) {
- explicitIDs.append(.init(id: explicitID, owner: owner, isUnary: isUnary))
- }
- #endif
-}
-
-// MARK: - IndirectMap
-
-//#if OPENSWIFTUI_RELEASE_2024
-//final package class IndirectAttributeMap {
-// final package let subgraph: OGSubgraph
-// // final package var map: [AnyAttribute: AnyAttribute]
-//
-// package init(subgraph: OGSubgraph) {
-// self.subgraph = subgraph
-// // self.map = [:]
-// }
-//}
-//#elseif OPENSWIFTUI_RELEASE_2021
-final package class _ViewList_IndirectMap {
- final package let subgraph: OGSubgraph
-
- #if canImport(Darwin)
- final package var map: [AnyAttribute: AnyAttribute]
- #endif
-
- init(subgraph: OGSubgraph) {
- self.subgraph = subgraph
- #if canImport(Darwin)
- self.map = [:]
- #endif
- }
-}
-//#endif
-
-// MARK: - _ViewList_Elements
-
-package protocol _ViewList_Elements {
- typealias Body = (_ViewInputs, @escaping Self.MakeElement) -> (_ViewOutputs?, Swift.Bool)
- typealias MakeElement = (_ViewInputs) -> _ViewOutputs
-// #if OPENSWIFTUI_RELEASE_2024
-// typealias Release = _ViewList_ReleaseElements
-// #elseif OPENSWIFTUI_RELEASE_2021
- typealias Release = () -> Void
-// #endif
-
- var count: Int { get }
-
-// #if OPENSWIFTUI_RELEASE_2024
-// func makeElements(
-// from start: inout Int,
-// inputs: _ViewInputs,
-// indirectMap: IndirectAttributeMap?,
-// body: Body
-// ) -> (_ViewOutputs?, Bool)
-//
-// func tryToReuseElement(
-// at index: Int,
-// by other: any _ViewList_Elements,
-// at otherIndex: Int,
-// indirectMap: IndirectAttributeMap,
-// testOnly: Bool
-// ) -> Bool
-// #elseif OPENSWIFTUI_RELEASE_2021
- func makeElements(
- from start: inout Int,
- inputs: _ViewInputs,
- indirectMap: _ViewList_IndirectMap?,
- body: Body
- ) -> (_ViewOutputs?, Bool)
-
- func tryToReuseElement(
- at index: Int,
- by other: any _ViewList_Elements,
- at otherIndex: Int,
- indirectMap: _ViewList_IndirectMap,
- testOnly: Bool
- ) -> Bool
-// #endif
-
- func retain() -> Release
-}
-
-extension _ViewList_Elements {
- func retain() -> Release {
- {}
- }
-}
-
-private struct EmptyElements: _ViewList_Elements {
- var count: Int { 0 }
-
- func makeElements(
- from start: inout Int,
- inputs: _ViewInputs,
- indirectMap: _ViewList_IndirectMap?,
- body: Body
- ) -> (_ViewOutputs?, Bool) {
- return (nil, true)
- }
-
- func tryToReuseElement(
- at index: Int,
- by other: any _ViewList_Elements,
- at otherIndex: Int,
- indirectMap: _ViewList_IndirectMap,
- testOnly: Bool
- ) -> Bool {
- other is EmptyElements
- }
-}
-
-// TODO
-private struct UnaryElements: _ViewList_Elements {
- var body: Value
- var baseInputs: _GraphInputs
-
- init(body: Value, baseInputs: _GraphInputs) {
- self.body = body
- self.baseInputs = baseInputs
- }
-
- var count: Int { 1 }
-
- func makeElements(
- from start: inout Int,
- inputs: _ViewInputs,
- indirectMap: _ViewList_IndirectMap?,
- body: Body
- ) -> (_ViewOutputs?, Bool) {
- preconditionFailure("TODO")
- }
-
- func tryToReuseElement(
- at index: Int,
- by other: any _ViewList_Elements,
- at otherIndex: Int,
- indirectMap: _ViewList_IndirectMap,
- testOnly: Bool
- ) -> Bool {
- preconditionFailure("TODO")
- }
-}
-
-// MARK: - _ViewList_Subgraph
-
-class _ViewList_Subgraph {
- let subgraph: OGSubgraph
- private var refcount : UInt32
-
- init(subgraph: OGSubgraph) {
- self.subgraph = subgraph
- self.refcount = 1
- }
-
- func invalidate() {}
-}
-
-extension _ViewList_Subgraph {
- var isValid: Bool {
- guard refcount > 0 else {
- return false
- }
- return subgraph.isValid
- }
-
- func retain() {
- refcount &+= 1
- }
-
- func release(isInserted: Bool) {
- refcount &-= 1
- guard refcount == 0 else {
- return
- }
- invalidate()
- guard subgraph.isValid else {
- return
- }
- subgraph.willInvalidate(isInserted: isInserted)
- subgraph.invalidate()
- }
-
- @inlinable
- func wrapping(_ elements: _ViewList_Elements) -> _ViewList_Elements {
- SubgraphElements(base: elements, subgraph: self)
- }
-}
-
-// TODO
-private struct SubgraphElements: _ViewList_Elements {
- let base: _ViewList_Elements
- let subgraph: _ViewList_Subgraph
-
- var count: Int {
- preconditionFailure("TODO")
- }
-
- func makeElements(from start: inout Int, inputs: _ViewInputs, indirectMap: _ViewList_IndirectMap?, body: (_ViewInputs, @escaping MakeElement) -> (_ViewOutputs?, Bool)) -> (_ViewOutputs?, Bool) {
- preconditionFailure("TODO")
- }
-
- func tryToReuseElement(at index: Int, by other: any _ViewList_Elements, at otherIndex: Int, indirectMap: _ViewList_IndirectMap, testOnly: Bool) -> Bool {
- preconditionFailure("TODO")
- }
-}
-
-// MARK: - _ViewList_View
-
-package struct _ViewList_View {
- var elements: _ViewList_Elements
- var id: _ViewList_ID
- var index: Int
- var count: Int
- var contentSubgraph: OGSubgraph
-}
-
-// MARK: - _ViewList_Sublist
-
-struct _ViewList_Sublist {
- var start: Int
- var count: Int
- var id: _ViewList_ID
- var elements: _ViewList_Elements
- var traits: ViewTraitCollection
- var list: Attribute?
-}
-
-struct _ViewList_SublistTransform {
- var items: [any _ViewList_SublistTransform_Item]
-}
-
-
-protocol _ViewList_SublistTransform_Item {
- func apply(sublist: inout _ViewList_Sublist)
-}
-
-// MARK: - ViewList
-
-protocol ViewList {
- func count(style: _ViewList_IteratorStyle) -> Int
- func estimatedCount(style: _ViewList_IteratorStyle) -> Int
- var traitKeys: ViewTraitKeys? { get }
- var viewIDs: _ViewList_ID.Views? { get }
- var traits: ViewTraitCollection { get }
- func applyNodes(from index: inout Int, style: _ViewList_IteratorStyle, list: _GraphValue?, transform: inout _ViewList_SublistTransform, to body: (inout Int, _ViewList_IteratorStyle, _ViewList_Node, inout _ViewList_SublistTransform) -> Bool) -> Bool
- func edit(forID id: _ViewList_ID, since transaction: TransactionID) -> _ViewList_Edit?
- func firstOffset(forID id: OtherID, style: _ViewList_IteratorStyle) -> Int? where OtherID: Hashable
-}
-
-// MARK: - ViewListVisitor
-
-protocol ViewListVisitor {
- mutating func visit(view: _ViewList_View, traits: ViewTraitCollection) -> Bool
-}
-
-// MARK: - _ViewList_IteratorStyle
-
-// TODO
-struct _ViewList_IteratorStyle: Equatable {
- var value: UInt
-
- func alignToPreviousGranularityMultiple(_ value: inout Int) {
- preconditionFailure("TODO")
- }
-}
-
-enum _ViewList_Edit: Equatable {
- case inserted
- case removed
-}
-
-enum _ViewList_Node {
- case list(any ViewList, Attribute?)
- case sublist(_ViewList_Sublist)
- // case group(_ViewList_Group)
- // case section(_ViewList_Section)
-}
diff --git a/Tests/OpenSwiftUICoreTests/View/Input/ViewListTests.swift b/Tests/OpenSwiftUICoreTests/View/Input/ViewListTests.swift
new file mode 100644
index 000000000..6c459b05c
--- /dev/null
+++ b/Tests/OpenSwiftUICoreTests/View/Input/ViewListTests.swift
@@ -0,0 +1,149 @@
+//
+// ViewListTests.swift
+// OpenSwiftUICoreTests
+
+@_spi(ForOpenSwiftUIOnly)
+import OpenSwiftUICore
+import Testing
+
+struct ViewListIteratorStyleTests {
+ @Test
+ func granularityAndApplyGranularity() {
+ var iteratorStyle = ViewList.IteratorStyle()
+ #expect(iteratorStyle.granularity == 1)
+ #expect(iteratorStyle.applyGranularity == false)
+
+ iteratorStyle.granularity = 2
+ iteratorStyle.applyGranularity = true
+
+ #expect(iteratorStyle.granularity == 2)
+ #expect(iteratorStyle.applyGranularity == true)
+ }
+
+ @Test(arguments: [
+ (ViewList.IteratorStyle(granularity: 1), 0, 0, 0),
+ (ViewList.IteratorStyle(granularity: 1), 1, 1, 1),
+ (ViewList.IteratorStyle(granularity: 1), 2, 2, 2),
+ (ViewList.IteratorStyle(granularity: 2), 0, 0, 0),
+ (ViewList.IteratorStyle(granularity: 2), 1, 0, 2),
+ (ViewList.IteratorStyle(granularity: 2), 2, 2, 2),
+ (ViewList.IteratorStyle(granularity: 2), 3, 2, 4),
+ (ViewList.IteratorStyle(granularity: 3), 0, 0, 0),
+ (ViewList.IteratorStyle(granularity: 3), 1, 0, 3),
+ (ViewList.IteratorStyle(granularity: 3), 2, 0, 3),
+ (ViewList.IteratorStyle(granularity: 3), 3, 3, 3),
+ (ViewList.IteratorStyle(granularity: 3), 4, 3, 6),
+ ])
+ func alignment(
+ _ iteratorStyle: ViewList.IteratorStyle,
+ _ initialValue: Int,
+ _ expectedPrevious: Int,
+ _ expectedNext: Int
+ ) {
+ var previous = initialValue
+ iteratorStyle.alignToPreviousGranularityMultiple(&previous)
+ #expect(previous == expectedPrevious)
+
+ var next = initialValue
+ iteratorStyle.alignToNextGranularityMultiple(&next)
+ #expect(next == expectedNext)
+ }
+}
+
+struct ViewListIDTests {
+ @Test
+ func initialization() {
+ let id1 = ViewList.ID()
+ #expect(id1.index == 0)
+ #expect(id1.primaryExplicitID == nil)
+
+ let id2 = ViewList.ID(implicitID: 42)
+ #expect(id2.index == 0)
+ #expect(id2.primaryExplicitID == nil)
+ }
+
+ @Test
+ func elementIDGeneration() {
+ let baseID = ViewList.ID(implicitID: 1)
+ let element1 = baseID.elementID(at: 0)
+ let element2 = baseID.elementID(at: 5)
+
+ #expect(element1.index == 0)
+ #expect(element2.index == 5)
+ }
+
+ @Test
+ func elementCollection() {
+ let baseID = ViewList.ID(implicitID: 1)
+ let collection = baseID.elementIDs(count: 3)
+
+ #expect(collection.count == 3)
+ #expect(collection.startIndex == 0)
+ #expect(collection.endIndex == 3)
+
+ let elements = Array(collection)
+ #expect(elements.count == 3)
+ #expect(elements[0].index == 0)
+ #expect(elements[1].index == 1)
+ #expect(elements[2].index == 2)
+ }
+
+ @Test
+ func canonicalID() {
+ var id = ViewList.ID(implicitID: 42)
+ let canonical1 = id.canonicalID
+ #expect(canonical1.index == 0)
+ #expect(canonical1.requiresImplicitID == true)
+ #expect(canonical1.explicitID == nil)
+ #expect(canonical1.description == "@0")
+
+ #if canImport(Darwin)
+ id.bind(explicitID: "test", owner: .nil)
+ let canonical2 = id.canonicalID
+ #expect(canonical2.index == 0)
+ #expect(canonical2.requiresImplicitID == true)
+ #expect(canonical2.explicitID?.description == "test")
+ #expect(canonical2.description == "test")
+ #endif
+ }
+
+ @Test
+ func explicitIDOperations() {
+ #if canImport(Darwin)
+ var id = ViewList.ID()
+
+ // Test binding single explicit ID
+ id.bind(explicitID: "test1", owner: .nil)
+ #expect(id.primaryExplicitID?.description == "test1")
+ #expect(id.allExplicitIDs.count == 1)
+
+ // Test binding multiple explicit IDs
+ id.bind(explicitID: "test2", owner: .nil)
+ #expect(id.allExplicitIDs.count == 2)
+ #expect(id.primaryExplicitID?.description == "test1")
+
+ // Test explicit ID lookup
+ let stringID: String? = id.explicitID(for: String.self)
+ #expect(stringID == "test1")
+
+ // Test static explicit ID creation
+ let explicitID = ViewList.ID.explicit("test3")
+ #expect(explicitID.primaryExplicitID?.description == "test3")
+ #endif
+ }
+
+ @Test
+ func hashableConformance() {
+ let id1 = ViewList.ID(implicitID: 1)
+ let id2 = ViewList.ID(implicitID: 1)
+ let id3 = ViewList.ID(implicitID: 2)
+
+ #expect(id1 == id2)
+ #expect(id1 != id3)
+
+ var hasher = Hasher()
+ id1.hash(into: &hasher)
+ id2.hash(into: &hasher)
+ // Just verifying that hashing doesn't crash
+ }
+}