Commits
Click on a commit to change the comparison rangefeat(formatter): support printing comments (#11716)
This PR implements support for printing leading, trailing, and dangling comments based on an approach inspired by [Prettier's algorithm](https://github.com/prettier/prettier/blob/7584432401a47a26943dd7a9ca9e8032ead7285/src/main/comments/attach.js#L135).
## Summary of Changes
### 1. Introduction of `following_node` field in `AstNode`
We've added a `following_node` field of type `SiblingNode` to `AstNode`. This enum includes all AST structs and stores the next node of the current node. This addition is crucial for determining whether a comment is leading or trailing. (Note: This could potentially be replaced by `AstKind` after resolving [#11490](https://github.com/oxc-project/oxc/issues/11490))
### 2. Custom `Comments` Implementation
We've replaced Biome's `Comments` with our own implementation:
```rs
#[derive(Debug, Clone)]
pub struct Comments<'a> {
source_text: &'a str,
comments: &'a Vec<'a, Comment>,
printed_count: usize,
}
```
This implementation avoids heap allocations, shifting the overhead to comment checking operations.
## Design Approach
Our approach is based on Prettier's algorithm but with a key difference: instead of preprocessing comments, we handle comment printing directly during the formatting process.
### Design Rationale
Several constraints informed this design decision:
1. The AST is immutable, preventing direct attachment of comments to nodes
2. `AstNode` instances are generated during formatting, making pre-generation with comments impractical
3. Without unique node identifiers ([AstNodeId #4188](https://github.com/oxc-project/oxc/issues/4188)), mapping comments to nodes for later retrieval is not feasible
### Implementation Details
We've generated a file implementing the `Format` trait for nearly all AstNodes. In each implementation:
- Leading comments are formatted before the node itself
- Trailing comments are formatted after the node
- Dangling comments are printed for empty block-like nodes or `CallExpression` nodes with no arguments
The `printed_count` field in `Comments` tracks how many comments have been printed, preventing duplicate processing, which would significantly impact performance.
#### Comment Classification Logic
1. **Leading Comments**: All comments preceding the current node are treated as leading comments.
2. **Trailing Comments**: Comments between the current node and the next node require careful handling. We determine if they belong to the current node; otherwise, they're left as leading comments for the subsequent node.
3. **Dangling Comments**: These are simply comments inside empty block-like nodes.
## Advantages and Disadvantages
### Advantages
1. Eliminates the need for an additional AST traversal to attach comments, saving time and resources
2. Direct printing of processed comments avoids heap allocations and memory overhead
### Disadvantages
1. Comment checking becomes more complex, requiring iteration through unprinted comments, which may have performance implications (though this is uncommon)
2. The approach may present a steeper learning curve for new contributors, which we'll address through documentation and user-friendly APIs
## Performance Impact
The implementation introduces approximately 4% performance regression, which is significantly better than the estimated 10%+ regression that would result from implementing a separate AST traversal for comment attachment.
<img width="709" alt="image" src="https://github.com/user-attachments/assets/3a8c3e30-c114-4a38-9779-168a2ead2942" />
## Test Improvements
| Language | Before | After | Improvement |
|-------------|----------------|----------------|-------------|
| JavaScript | 295/699 (42.20%) | 324/699 (46.35%) | +4.15% |
| TypeScript | 186/573 (32.46%) | 200/573 (34.90%) | +2.44% |
Currently, we're focusing on JavaScript support, with TypeScript improvements planned for future work.
## Future Work
1. Port special printing cases from [Prettier's comment handling](https://github.com/prettier/prettier/blob/7584432401a47a26943dd7a9ca9a8e032ead7285/src/language-js/comments/handle-comments.js)
2. Develop APIs to facilitate comment checking during the formatting process