Skip to content

Generate grammar doc comments for layout nodes #1771

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 1 commit into from
Jun 23, 2023
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
63 changes: 63 additions & 0 deletions CodeGeneration/Sources/SyntaxSupport/GrammarGenerator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import SwiftSyntax

/// Generates grammar doc comments for syntax nodes.
struct GrammarGenerator {
private func grammar(for tokenChoice: TokenChoice) -> String {
switch tokenChoice {
case .keyword(text: let text):
return "`'\(text)'`"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel having quotation marks within the code voice makes it a bit ambiguous what the token actually is.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My idea here was to optimize this for rendering in docc and I found that rendering the token name in code voice reads a little nicer in docc. Would you prefer to just use

return "'\(text)'"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think I would prefer just return "'\(text)'" or return "`\(text)`". And if the latter is used, syntactic categories can be italicized like in TSPL, to differentiate from token texts.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The thing is that we use docc links for links to other syntactic categories, which are always monospaced.

The documentation for StructDeclSyntax shows all kinds of references. Which fonts would you use here?

case .token(tokenKind: let tokenKind):
let token = SYNTAX_TOKEN_MAP[tokenKind]!
if let tokenText = token.text {
return "`'\(tokenText)'`"
} else {
return "`<\(token.swiftKind)>`"
}
}
}

private func grammar(for child: Child) -> String {
let optionality = child.isOptional ? "?" : ""
switch child.kind {
case .node(let kind):
return "``\(kind.syntaxType)``\(optionality)"
case .nodeChoices(let choices):
let choicesDescriptions = choices.map { grammar(for: $0) }
return "(\(choicesDescriptions.joined(separator: " | ")))\(optionality)"
case .collection(let kind, _, _):
return "``\(kind.syntaxType)``"
case .token(let choices, _, _):
if choices.count == 1 {
return "\(grammar(for: choices.first!))\(optionality)"
} else {
let choicesDescriptions = choices.map { grammar(for: $0) }
return "(\(choicesDescriptions.joined(separator: " | ")))\(optionality)"
}
}
}

/// Generates a markdown list containing the children’s names and their
/// grammar.
///
/// - Parameter children: The children to show in the list
static func childrenList(for children: [Child]) -> String {
let generator = GrammarGenerator()
return
children
.filter { !$0.isUnexpectedNodes }
.map { " - `\($0.varName)`: \(generator.grammar(for: $0))" }
.joined(separator: "\n")
}
}
14 changes: 14 additions & 0 deletions CodeGeneration/Sources/SyntaxSupport/Node.swift
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,20 @@ public struct LayoutNode {
preconditionFailure("NodeLayoutView must wrap a Node with data `.layout`")
}
}

public var grammar: SwiftSyntax.Trivia {
guard !children.isEmpty else {
return []
}

return docCommentTrivia(
from: """
### Children

\(GrammarGenerator.childrenList(for: children))
Comment on lines +231 to +233
Copy link
Contributor

@WowbaggersLiquidLunch WowbaggersLiquidLunch Jun 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The list of children should already be visible through DocC, under the type/protocol's Instance Properties section. Does it really need to be repeated in the main description too?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it does because these are sorted in source order while the Instance Properties are sorted alphabetically.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK I think this is a valid reason. Although, maybe in the long term we should have the ability to specify to DocC the ordering of properties.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, if we could do that, I agree that we might be able to remove the children list.

"""
)
}
}

/// Provides a view into a collection node that offers access to the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ func syntaxNode(emitKind: SyntaxNodeKind) -> SourceFileSyntax {
// MARK: - \(raw: node.kind.syntaxType)
\(raw: node.documentation)
\(raw: node.documentation.isEmpty ? "" : "///")
\(raw: node.grammar)
public struct \(raw: node.kind.syntaxType): \(raw: node.baseType.syntaxBaseName)Protocol, SyntaxHashable
"""
) {
Expand Down
Loading