david-swift 4937c36b3b Improve updating performance
And update docs reflecting the changes in the latest commits
2024-01-27 08:07:05 +01:00

229 lines
8.4 KiB
Swift

//
// Class.swift
// Adwaita
//
// Created by david-swift on 14.01.24.
//
import Foundation
/// A class.
struct Class: Decodable {
/// The name of the class.
var name: String
/// The prefix for C symbols.
var cSymbolPrefix: String
/// The type in C.
var cType: String?
/// The parent class.
var parent: String?
/// The doc string.
var doc: String
/// The available initializers.
var constructors: [Constructor]
/// The available properties.
var properties: [Property]
/// The available signals (callbacks).
var signals: [Signal]
/// The coding keys for the class
enum CodingKeys: String, CodingKey {
/// Coding key
case name, cSymbolPrefix, cType, parent, doc
/// Coding key
case constructors = "constructor"
/// Coding key
case properties = "property"
/// Coding key
case signals = "glibSignal"
}
/// Get the properties of the class and its parent classes.
/// - Parameters:
/// - classes: The classes in the namespace.
/// - configurations: The configurations for the classes in the namespace.
/// - Returns: The properties.
func properties(classes: [Self], configurations: [WidgetConfiguration]) -> [Property] {
if let parentClass = parentClass(classes: classes) {
return properties + parentClass
.properties(classes: classes, configurations: configurations)
.map { property in
var property = property
if property.prefix == nil {
property.prefix = parentClass.prefix()
property.cast = configurations.first { $0.class == parentClass.name }?.cast ?? false
}
return property
}
}
return properties
}
/// Get the signals of the class and its parent classes.
/// - Parameter classes: The classes in the namespace.
/// - Returns: The signals.
func signals(classes: [Self]) -> [Signal] {
if let parentClass = parentClass(classes: classes) {
return signals + parentClass.signals(classes: classes)
}
return signals
}
/// Get the assignments for the static widgets of the class and its parent classes.
/// - Parameters:
/// - classes: The classes in the namespace.
/// - configs: The configurations.
/// - Returns: The code.
func staticWidgets(classes: [Self], configs: [WidgetConfiguration]) -> String {
var content = ""
if let parentClass = parentClass(classes: classes) {
content += parentClass.staticWidgets(classes: classes, configs: configs)
}
guard let config = configs.first(where: { $0.class == name }) else {
return content
}
let widgetPointer = config.cast ? "storage.pointer?.cast()" : "storage.pointer"
for widget in config.staticWidgets {
content += """
var \(widget.name)Storage: [ViewStorage] = []
for view in \(widget.name)() {
\(widget.name)Storage.append(view.storage(modifiers: modifiers))
\(widget.add)(\(widgetPointer), \(widget.name)Storage.last?.pointer?.cast())
}
storage.content["\(widget.name)"] = \(widget.name)Storage
"""
}
return content
}
/// Get the code for the properties of the static widgets.
/// - Parameters:
/// - classes: The classes in the namespace.
/// - configs: The configurations.
/// - Returns: The code.
func staticWidgetProperties(classes: [Self], configs: [WidgetConfiguration]) -> String {
var content = ""
if let parentClass = parentClass(classes: classes) {
content += parentClass.staticWidgetProperties(classes: classes, configs: configs)
}
guard let config = configs.first(where: { $0.class == name }) else {
return content
}
for staticWidget in config.staticWidgets {
content += """
/// The body for the widget "\(staticWidget.name)".
var \(staticWidget.name): () -> Body = { [] }
"""
}
return content
}
/// Get the parent class.
/// - Parameter classes: The classes in the namespace.
/// - Returns: The class.
func parentClass(classes: [Self]) -> Self? {
if let parent = classes.first(where: { $0.name == parent }), parent.name != "Widget" {
return parent
}
return nil
}
/// Get the widget's prefix (e.g. "adw_preferences_row").
/// - Returns: The prefix.
func prefix() -> String {
if cType?.hasPrefix("Adw") ?? false {
return "adw_\(cSymbolPrefix)"
} else {
return "gtk_\(cSymbolPrefix)"
}
}
// swiftlint:disable function_body_length line_length
/// Generate the code for the class.
/// - Parameters:
/// - config: The widget configuration.
/// - genConfig: The generation configuration.
/// - classes: The available classes.
/// - configs: The available widget configurations.
/// - Returns: The code.
func generate(
config: WidgetConfiguration,
genConfig: GenerationConfiguration,
classes: [Self],
configs: [WidgetConfiguration]
) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd.MM.yy"
let widgetName = config.name ?? config.class
let definition: String
if config.dynamicWidget == nil {
definition = "\(widgetName): Widget"
} else {
definition = "\(widgetName)<Element>: Widget where Element: Identifiable"
}
return """
//
// \(widgetName).swift
// Adwaita
//
// Created by auto-generation on \(dateFormatter.string(from: .now)).
//
import CAdw
import LevenshteinTransformations
\(doc.docComment())
public struct \(definition) {
/// Additional update functions for type extensions.
var updateFunctions: [(ViewStorage) -> Void] = []
/// Additional appear functions for type extensions.
var appearFunctions: [(ViewStorage) -> Void] = []
\(generateProperties(config: config, genConfig: genConfig, classes: classes, configs: configs))
/// Initialize `\(widgetName)`.
\(generateAdwaitaInitializer(config: config, genConfig: genConfig, classes: classes, configs: configs))
/// Get the widget's view storage.
/// - Parameter modifiers: The view modifiers.
/// - Returns: The view storage.
public func container(modifiers: [(View) -> View]) -> ViewStorage {
let storage = ViewStorage(\(generateInitializer(name: widgetName, config: config, classes: classes, configs: configs))?.opaque())
update(storage, modifiers: modifiers, updateProperties: true)
\(generateWidgetAssignments(config: config, genConfig: genConfig, classes: classes, configs: configs))
\(generateBindingAssignments(config: config, genConfig: genConfig, classes: classes, configs: configs))
for function in appearFunctions {
function(storage)
}
return storage
}
/// Update the widget's view storage.
/// - Parameters:
/// - storage: The view storage.
/// - modifiers: The view modifiers.
/// - updateProperties: Whether to update the view's properties.
public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) {\(generateSignalModifications(config: config, genConfig: genConfig, classes: classes))
storage.modify { widget in
\(generateModifications(config: config, genConfig: genConfig, classes: classes, configs: configs))
\(generateDynamicWidgetUpdate(config: config, genConfig: genConfig))
}
for function in updateFunctions {
function(storage)
}
}
\(generateModifiers(config: config, genConfig: genConfig, classes: classes, configs: configs))
}
"""
}
// swiftlint:enable function_body_length line_length
}