/// Emits the import block shared by every generated file. /// - Parameter isPorticoGtk: When `true`, adds `@_spi(Portico) import Portico` /// because the file lives in the separate `PorticoGtk` module. func generateHeader(isPorticoGtk: Bool) -> String { var out = "// Generated by PorticoGen. DO NOT EDIT. See Sources/PorticoGen to make changes.\n\n" out += "import Adw\nimport Gtk\nimport Gio\nimport Gdk\n" if isPorticoGtk { out += "@_spi(Portico) import Portico\n" } return out + "\n" } /// Converts a Swift wrapper property name to the most likely canonical GObject /// property name, such as `useMarkup` to `use-markup`. /// /// Backticks are dropped because Swift keyword properties are emitted as /// `` `open` ``, while the corresponding GObject property is named `open`. /// The result is only a candidate; the runtime verifies it against the widget /// class before using it as a `notify` detail. func gobjectPropertyName(_ swiftName: String) -> String { var out = "" for character in swiftName where character != "`" { if character.isUppercase { if !out.isEmpty { out.append("-") } out.append(contentsOf: character.lowercased()) } else { out.append(character) } } return out } // MARK: - Doc emission /// Renders a non-DocC provenance marker plus a DocC comment block. /// /// The marker is a plain `//` line placed above the `///` block, so the Swift /// compiler's doc extraction (and therefore DocC's rendered HTML/PDF) ignores it /// while it stays greppable in the generated source. /// /// - Parameters: /// - indent: Leading whitespace for every emitted line (`" "` for members, `""` for top level). /// - chain: PorticoGen function names, outermost first; joined with `" -> "`. /// - sources: Upstream symbols the prose came from; joined with `", "`. /// Emits `| source: synthesized` when empty. /// - summary: Carried-over doc lines, verbatim. /// - notes: Portico-specific paragraphs appended after `summary`, one line each. /// - parameters: `- Parameter` entries, in declaration order. /// - returns: `- Returns:` text, or `nil` for initializers. func docBlock( indent: String, chain: [String], sources: [String], summary: [String], notes: [String] = [], parameters: [(name: String, text: String)] = [], returns: String? = nil ) -> String { var out = "" let chainStr = chain.joined(separator: " -> ") let srcStr = sources.isEmpty ? "synthesized" : sources.joined(separator: ", ") out += "\(indent)// PorticoGen: \(chainStr) | source: \(srcStr)\n" func emitDoc(_ lines: [String]) { for line in lines { if line.isEmpty { out += "\(indent)///\n" } else { out += "\(indent)/// \(line)\n" } } } emitDoc(summary) if !notes.isEmpty { // Blank separator between summary and notes. if !summary.isEmpty { out += "\(indent)///\n" } emitDoc(notes) } if !parameters.isEmpty || returns != nil { // Blank separator before parameter/returns group. out += "\(indent)///\n" for (name, text) in parameters { out += "\(indent)/// - Parameter \(name): \(text)\n" } if let r = returns { out += "\(indent)/// - Returns: \(r)\n" } } return out } /// Best available one-line description for a value parameter named `label` on `widget`. /// /// Lookup order: the same-named property's doc, then `set