Skip to content

Adding Documentation #19

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 17, 2025
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 .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -136,4 +136,5 @@ Support/*/macOS.entitlements
vendor/ruby
public
.mint
*.lcov
*.lcov
.docc-build
6 changes: 6 additions & 0 deletions Sources/SyntaxKit/Assignment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,15 @@

import SwiftSyntax

/// An assignment expression.
public struct Assignment: CodeBlock {
private let target: String
private let value: String

/// Creates an assignment expression.
/// - Parameters:
/// - target: The variable to assign to.
/// - value: The value to assign.
public init(_ target: String, _ value: String) {
self.target = target
self.value = value
Expand Down
5 changes: 5 additions & 0 deletions Sources/SyntaxKit/Case.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,15 @@

import SwiftSyntax

/// A `case` in a `switch` statement with tuple-style patterns.
public struct Case: CodeBlock {
private let patterns: [String]
private let body: [CodeBlock]

/// Creates a `case` for a `switch` statement.
/// - Parameters:
/// - patterns: The patterns to match for the case.
/// - content: A ``CodeBlockBuilder`` that provides the body of the case.
public init(_ patterns: String..., @CodeBlockBuilderResult content: () -> [CodeBlock]) {
self.patterns = patterns
self.body = content()
Expand Down
2 changes: 2 additions & 0 deletions Sources/SyntaxKit/CodeBlock+Generate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import Foundation
import SwiftSyntax

extension CodeBlock {
/// Generates the Swift code for the ``CodeBlock``.
/// - Returns: The generated Swift code as a string.
public func generateCode() -> String {
let statements: CodeBlockItemListSyntax
if let list = self.syntax.as(CodeBlockItemListSyntax.self) {
Expand Down
13 changes: 13 additions & 0 deletions Sources/SyntaxKit/CodeBlock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,39 +30,52 @@
import Foundation
import SwiftSyntax

/// A protocol for types that can be represented as a SwiftSyntax node.
public protocol CodeBlock {
/// The SwiftSyntax representation of the code block.
var syntax: SyntaxProtocol { get }
}

/// A protocol for types that can build a ``CodeBlock``.
public protocol CodeBlockBuilder {
/// The type of ``CodeBlock`` that this builder creates.
associatedtype Result: CodeBlock
/// Builds the ``CodeBlock``.
func build() -> Result
}

/// A result builder for creating arrays of ``CodeBlock``s.
@resultBuilder
public struct CodeBlockBuilderResult {
/// Builds a block of ``CodeBlock``s.
public static func buildBlock(_ components: CodeBlock...) -> [CodeBlock] {
components
}

/// Builds an optional ``CodeBlock``.
public static func buildOptional(_ component: CodeBlock?) -> CodeBlock {
component ?? EmptyCodeBlock()
}

/// Builds a ``CodeBlock`` from an `if` statement.
public static func buildEither(first: CodeBlock) -> CodeBlock {
first
}

/// Builds a ``CodeBlock`` from an `else` statement.
public static func buildEither(second: CodeBlock) -> CodeBlock {
second
}

/// Builds an array of ``CodeBlock``s from a `for` loop.
public static func buildArray(_ components: [CodeBlock]) -> [CodeBlock] {
components
}
}

/// An empty ``CodeBlock``.
public struct EmptyCodeBlock: CodeBlock {
/// The syntax for an empty code block.
public var syntax: SyntaxProtocol {
StringSegmentSyntax(content: .unknown(""))
}
Expand Down
10 changes: 8 additions & 2 deletions Sources/SyntaxKit/CommentBuilderResult.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,20 @@
// OTHER DEALINGS IN THE SOFTWARE.
//

/// A result builder for creating arrays of ``Line``s for comments.
@resultBuilder
public enum CommentBuilderResult {
/// Builds a block of ``Line``s.
public static func buildBlock(_ components: Line...) -> [Line] { components }
}

// MARK: - Public DSL surface

extension CodeBlock {
/// Attach comments to the current `CodeBlock`.
/// Attaches comments to the current ``CodeBlock``.
///
/// The provided lines are injected as leading trivia to the declaration produced by this ``CodeBlock``.
///
/// Usage:
/// ```swift
/// Struct("MyStruct") { ... }
Expand All @@ -44,7 +49,8 @@ extension CodeBlock {
/// Line(.doc, "This is a documentation comment")
/// }
/// ```
/// The provided lines are injected as leading trivia to the declaration produced by this `CodeBlock`.
/// - Parameter content: A ``CommentBuilderResult`` that provides the comment lines.
/// - Returns: A new ``CodeBlock`` with the comments attached.
public func comment(@CommentBuilderResult _ content: () -> [Line]) -> CodeBlock {
CommentedCodeBlock(base: self, lines: content())
}
Expand Down
6 changes: 6 additions & 0 deletions Sources/SyntaxKit/ComputedProperty.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,17 @@

import SwiftSyntax

/// A Swift `var` declaration with a computed value.
public struct ComputedProperty: CodeBlock {
private let name: String
private let type: String
private let body: [CodeBlock]

/// Creates a computed property declaration.
/// - Parameters:
/// - name: The name of the property.
/// - type: The type of the property.
/// - content: A ``CodeBlockBuilder`` that provides the body of the getter.
public init(_ name: String, type: String, @CodeBlockBuilderResult _ content: () -> [CodeBlock]) {
self.name = name
self.type = type
Expand Down
4 changes: 4 additions & 0 deletions Sources/SyntaxKit/Default.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,12 @@

import SwiftSyntax

/// A `default` case in a `switch` statement.
public struct Default: CodeBlock {
private let body: [CodeBlock]

/// Creates a `default` case for a `switch` statement.
/// - Parameter content: A ``CodeBlockBuilder`` that provides the body of the case.
public init(@CodeBlockBuilderResult _ content: () -> [CodeBlock]) {
self.body = content()
}
Expand Down
218 changes: 218 additions & 0 deletions Sources/SyntaxKit/Documentation.docc/Documentation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
# ``SyntaxKit``

SyntaxKit provides a declarative way to generate Swift code structures using SwiftSyntax.

## Overview

SyntaxKit allows developers to build Swift code using result builders which enable the creation of Swift code structures in a declarative way. Here's an example:

```swift
import SyntaxKit

let code = Struct("BlackjackCard") {
Enum("Suit") {
EnumCase("spades").equals("♠")
EnumCase("hearts").equals("♡")
EnumCase("diamonds").equals("♢")
EnumCase("clubs").equals("♣")
}
.inherits("Character")
.comment("nested Suit enumeration")
}

let generatedCode = code.generateCode()
```

This will generate the following Swift code:

```swift
struct BlackjackCard {
// nested Suit enumeration
enum Suit: Character {
case spades = "♠"
case hearts = "♡"
case diamonds = "♢"
case clubs = "♣"
}
}
```

## Full Example

Here is a more comprehensive example that demonstrates many of SyntaxKit's features to generate a `BlackjackCard` struct.

### DSL Code

```swift
import SyntaxKit

let structExample = Struct("BlackjackCard") {
Enum("Suit") {
EnumCase("spades").equals("♠")
EnumCase("hearts").equals("♡")
EnumCase("diamonds").equals("♢")
EnumCase("clubs").equals("♣")
}
.inherits("Character")
.comment("nested Suit enumeration")

Enum("Rank") {
EnumCase("two").equals(2)
EnumCase("three")
EnumCase("four")
EnumCase("five")
EnumCase("six")
EnumCase("seven")
EnumCase("eight")
EnumCase("nine")
EnumCase("ten")
EnumCase("jack")
EnumCase("queen")
EnumCase("king")
EnumCase("ace")

Struct("Values") {
Variable(.let, name: "first", type: "Int")
Variable(.let, name: "second", type: "Int?")
}

ComputedProperty("values") {
Switch("self") {
SwitchCase(".ace") {
Return {
Init("Values") {
Parameter(name: "first", value: "1")
Parameter(name: "second", value: "11")
}
}
}
SwitchCase(".jack", ".queen", ".king") {
Return {
Init("Values") {
Parameter(name: "first", value: "10")
Parameter(name: "second", value: "nil")
}
}
}
Default {
Return {
Init("Values") {
Parameter(name: "first", value: "self.rawValue")
Parameter(name: "second", value: "nil")
}
}
}
}
}
}
.inherits("Int")
.comment("nested Rank enumeration")

Variable(.let, name: "rank", type: "Rank")
Variable(.let, name: "suit", type: "Suit")
.comment("BlackjackCard properties and methods")

ComputedProperty("description") {
VariableDecl(.var, name: "output", equals: "\"suit is \\(suit.rawValue),\"")
PlusAssign("output", "\" value is \\(rank.values.first)\"")
If(Let("second", "rank.values.second"), then: {
PlusAssign("output", "\" or \\(second)\"")
})
Return {
VariableExp("output")
}
}
}
```

### Generated Code

```swift
import Foundation

struct BlackjackCard {
// nested Suit enumeration
enum Suit: Character {
case spades = "♠"
case hearts = "♡"
case diamonds = "♢"
case clubs = "♣"
}

// nested Rank enumeration
enum Rank: Int {
case two = 2
case three
case four
case five
case six
case seven
case eight
case nine
case ten
case jack
case queen
case king
case ace

struct Values {
let first: Int, second: Int?
}

var values: Values {
switch self {
case .ace:
return Values(first: 1, second: 11)
case .jack, .queen, .king:
return Values(first: 10, second: nil)
default:
return Values(first: self.rawValue, second: nil)
}
}
}

// BlackjackCard properties and methods
let rank: Rank
let suit: Suit
var description: String {
var output = "suit is \\(suit.rawValue),"
output += " value is \\(rank.values.first)"
if let second = rank.values.second {
output += " or \\(second)"
}
return output
}
}
```

## Topics

### Declarations

- ``Struct``
- ``Enum``
- ``EnumCase``
- ``Function``
- ``Init``
- ``ComputedProperty``
- ``VariableDecl``
- ``Let``
- ``Variable``

### Expressions & Statements
- ``Assignment``
- ``PlusAssign``
- ``Return``
- ``VariableExp``

### Control Flow
- ``If``
- ``Switch``
- ``SwitchCase``
- ``Default``

### Building Blocks
- ``CodeBlock``
- ``Parameter``
- ``Literal``

Loading
Loading