Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,8 @@ let package = Package(
),

.target(
name: "CodePrinting"
name: "CodePrinting",
dependencies: ["SwiftJavaConfigurationShared"]
),

.target(
Expand Down
53 changes: 53 additions & 0 deletions Sources/CodePrinting/CodePrinter+Java.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2026 Apple Inc. and the Swift.org project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift.org project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

// ==== -----------------------------------------------------------------------
// MARK: CodePrinter - Java-language helpers

extension CodePrinter where Language == JavaLanguage {

/// Print a Java `if (<condition>) { ... }` block.
public mutating func printIfBlock(
_ condition: Any,
function: String = #function,
file: String = #fileID,
line: UInt = #line,
body: (inout CodePrinter) throws -> Void
) rethrows {
try printBraceBlock("if (\(condition))", function: function, file: file, line: line, body: body)
}

/// Print a Javadoc comment.
///
/// Shape depends on `options.sourceVersion`:
/// - Java 23+: Markdown-style line comments (`///`) based on https://openjdk.org/jeps/467
/// - Earlier versions: classic `/** ... */` block, with each body line
/// prefixed by ` * ` (just ` *` for blank paragraph separators).
///
/// Pass paragraph breaks as blank lines (`\n\n`) inside `text`.
public mutating func printJavadocComment(_ text: String) {
let lines = text.split(separator: "\n", omittingEmptySubsequences: false)
if options.sourceVersion >= 23 {
for line in lines {
print(line.isEmpty ? "///" : "/// \(line)")
}
} else {
print("/**")
for line in lines {
print(line.isEmpty ? " *" : " * \(line)")
}
print(" */")
}
}
}
43 changes: 43 additions & 0 deletions Sources/CodePrinting/CodePrinter+Swift.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2026 Apple Inc. and the Swift.org project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift.org project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

// ==== -----------------------------------------------------------------------
// MARK: CodePrinter - Swift-language helpers

extension CodePrinter where Language == SwiftLanguage {

/// Print a Swift `if <condition> { ... }` block.
/// The condition is emitted verbatim, without surrounding parentheses
/// (Swift convention).
public mutating func printIfBlock(
_ condition: Any,
function: String = #function,
file: String = #fileID,
line: UInt = #line,
body: (inout CodePrinter) throws -> Void
) rethrows {
try printBraceBlock("if \(condition)", function: function, file: file, line: line, body: body)
}

/// Print a Swift `guard <condition> else { ... }` block.
public mutating func printGuardBlock(
_ condition: Any,
function: String = #function,
file: String = #fileID,
line: UInt = #line,
body: (inout CodePrinter) throws -> Void
) rethrows {
try printBraceBlock("guard \(condition) else", function: function, file: file, line: line, body: body)
}
}
43 changes: 14 additions & 29 deletions Sources/CodePrinting/CodePrinter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import Foundation
// ==== -----------------------------------------------------------------------
// MARK: CodePrinter

public struct CodePrinter: Sendable {
public struct CodePrinter<Language: CodePrinterLanguage>: Sendable {
public var contents: String = ""

public var verbose: Bool = false
Expand All @@ -32,11 +32,10 @@ public struct CodePrinter: Sendable {
/// terminator equivalent to `.newLine`.
public var emitSourceLocations: Bool = true

/// Token used to start an inline comment. Defaults to `//` for Swift- and
/// Java-style output. Hash-comment languages can flip this to `.hash` so
/// source-location trailers, `printSeparator` banners, and echo-mode
/// headers render with the right comment lead.
public var inlineCommentStyle: InlineCommentStyle = .slashSlash
/// Token used to start an inline comment. Defaults to the language's
/// `defaultInlineCommentStyle`; can be overridden per instance for
/// special cases (e.g. testing the alternative style).
public var inlineCommentStyle: InlineCommentStyle

public var indentationDepth: Int = 0 {
didSet {
Expand Down Expand Up @@ -65,12 +64,19 @@ public struct CodePrinter: Sendable {
case accumulateAll
case flushToFileOnWrite
}

/// Per-language options (e.g. Java source version). Languages without
/// options use `Void`. Read by language-constrained extensions.
public var options: Language.Options

public init(
mode: PrintMode = .flushToFileOnWrite,
inlineCommentStyle: InlineCommentStyle = .slashSlash
inlineCommentStyle: InlineCommentStyle? = nil,
options: Language.Options = Language.defaultOptions
) {
self.mode = mode
self.inlineCommentStyle = inlineCommentStyle
self.inlineCommentStyle = inlineCommentStyle ?? Language.defaultInlineCommentStyle
self.options = options
}

public mutating func append(_ text: String) {
Expand Down Expand Up @@ -124,27 +130,6 @@ public struct CodePrinter: Sendable {
print("}", terminator, function: function, file: file, line: line)
}

public mutating func printIfBlock(
_ condition: Any,
function: String = #function,
file: String = #fileID,
line: UInt = #line,
body: (inout CodePrinter) throws -> Void
) rethrows {
try printBraceBlock("if (\(condition))", function: function, file: file, line: line, body: body)
}

/// Print a Swift `guard <condition> else { … }` block.
public mutating func printGuardBlock(
_ condition: Any,
function: String = #function,
file: String = #fileID,
line: UInt = #line,
body: (inout CodePrinter) throws -> Void
) rethrows {
try printBraceBlock("guard \(condition) else", function: function, file: file, line: line, body: body)
}

public mutating func printParts(
_ parts: String...,
terminator: PrinterTerminator = .newLine,
Expand Down
75 changes: 75 additions & 0 deletions Sources/CodePrinting/CodePrinterLanguage.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2026 Apple Inc. and the Swift.org project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift.org project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import SwiftJavaConfigurationShared

// ==== -----------------------------------------------------------------------
// MARK: CodePrinterLanguage

/// Phantom-type tag identifying the language a `CodePrinter` emits.
/// Generators pick a concrete language so language-specific helpers
/// (e.g. `printGuardBlock` for Swift, `printIfBlock` shaped for Java)
/// resolve at compile time via constrained extensions.
public protocol CodePrinterLanguage: Sendable {
/// Per-language printer options. Defaults to `Void` for languages
/// that don't need any (e.g. Swift today). Java carries the targeted
/// source version so helpers can pick the right syntax shape.
associatedtype Options: Sendable = Void

/// Default lead used for inline comments (source-location trailers,
/// `printSeparator` banners). May be overridden at the printer instance
/// via `CodePrinter.inlineCommentStyle`.
static var defaultInlineCommentStyle: InlineCommentStyle { get }

/// Default value for `CodePrinter.options`.
static var defaultOptions: Options { get }
}

extension CodePrinterLanguage where Options == Void {
public static var defaultOptions: Void { () }
}

// ==== -----------------------------------------------------------------------
// MARK: Concrete languages

/// Swift output.
public enum SwiftLanguage: CodePrinterLanguage {
public static let defaultInlineCommentStyle: InlineCommentStyle = .slashSlash
}

/// Java output.
public enum JavaLanguage: CodePrinterLanguage {
public struct Options: Sendable {
/// Targeted Java source version.
public var sourceVersion: JavaVersion

public init(sourceVersion: JavaVersion = 8) {
self.sourceVersion = sourceVersion
}
}

public static let defaultInlineCommentStyle: InlineCommentStyle = .slashSlash
public static let defaultOptions = Options()
}

// ==== -----------------------------------------------------------------------
// MARK: Typealiases

/// Convenience for `CodePrinter<SwiftLanguage>`. Use this everywhere a
/// printer's output is Swift code.
public typealias SwiftPrinter = CodePrinter<SwiftLanguage>

/// Convenience for `CodePrinter<JavaLanguage>`. Use this everywhere a
/// printer's output is Java code.
public typealias JavaPrinter = CodePrinter<JavaLanguage>
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import struct Foundation.URL
extension FFMSwift2JavaGenerator {

/// Print Java helper methods for Foundation.Data type
package func printFoundationDataHelpers(_ printer: inout CodePrinter, _ decl: ExtractedNominalType) {
package func printFoundationDataHelpers(_ printer: inout JavaPrinter, _ decl: ExtractedNominalType) {
let typeName = decl.swiftNominal.name
let thunkNameCopyBytes = "swiftjava_\(swiftModuleName)_\(typeName)_copyBytes__"

Expand Down
Loading
Loading