Skip to content
This repository has been archived by the owner on Aug 31, 2023. It is now read-only.

Commit

Permalink
feat(rome_js_formatter): Indent Doc comments
Browse files Browse the repository at this point in the history
This PR adds support for indenting js doc comments.

```javascript
/*
     * a comment
     */
```

Becomes
```javascript
/*
 * a comment
 *
```

The PR introduces a new `LeadingCommentRule` type on the `CstFormatContext` that is used to format any leading comment. This allows languages to implement formatting of a comment's content, something that wasn't possible before.

## Tests

**Average compatibility**: 83.70 -> 84.04

<details>
	<summary>Definition</summary>

	$$average = \frac\{\sum_{file}^\{files}compatibility_\{file}}\{files}$$
</details>

**Compatible lines**: 80.79 -> 81.22
  • Loading branch information
MichaReiser committed Aug 29, 2022
1 parent 2ef2fca commit 5b6740c
Show file tree
Hide file tree
Showing 19 changed files with 267 additions and 593 deletions.
3 changes: 3 additions & 0 deletions crates/rome_formatter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,9 @@ pub trait CstFormatContext: FormatContext {
type Language: Language;
type Style: CommentStyle<Self::Language>;

/// Rule for formatting leading comments.
type LeadingCommentRule: FormatRule<SourceComment<Self::Language>, Context = Self> + Default;

/// Customizes how comments are formatted
#[deprecated(note = "Prefer FormatLanguage::comment_style")]
fn comment_style(&self) -> Self::Style;
Expand Down
10 changes: 7 additions & 3 deletions crates/rome_formatter/src/token.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::comments::CommentStyle;
use crate::prelude::*;
use crate::{
format_args, write, Argument, Arguments, CommentKind, CstFormatContext, GroupId, LastTokenKind,
SourceComment,
format_args, write, Argument, Arguments, CommentKind, CstFormatContext, FormatRefWithRule,
GroupId, LastTokenKind, SourceComment,
};
use rome_rowan::{Language, SyntaxToken, SyntaxTriviaPiece};

Expand Down Expand Up @@ -682,6 +682,7 @@ where
.context()
.comment_style()
.get_comment_kind(comment.piece());

last_inline_comment = comment_kind.is_inline() && lines_after == 0;

let format_content = format_with(|f| {
Expand All @@ -691,7 +692,10 @@ where
write!(f, [space()])?;
};

write!(f, [comment.piece()])?;
let format_comment =
FormatRefWithRule::new(comment, C::LeadingCommentRule::default());

write!(f, [format_comment])?;

match comment_kind {
CommentKind::Line => match lines_after {
Expand Down
134 changes: 134 additions & 0 deletions crates/rome_js_formatter/src/comments.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
use crate::prelude::*;
use rome_formatter::{format_args, write};
use rome_formatter::{CommentKind, CommentStyle, SourceComment};
use rome_js_syntax::suppression::{parse_suppression_comment, SuppressionCategory};
use rome_js_syntax::{JsLanguage, JsSyntaxKind};
use rome_rowan::{SyntaxTriviaPieceComments, TextLen};

#[derive(Default)]
pub struct FormatJsLeadingComment;

impl FormatRule<SourceComment<JsLanguage>> for FormatJsLeadingComment {
type Context = JsFormatContext;

fn fmt(
&self,
comment: &SourceComment<JsLanguage>,
f: &mut Formatter<Self::Context>,
) -> FormatResult<()> {
if is_doc_comment(comment.piece()) {
let mut source_offset = comment.piece().text_range().start();

for (index, line) in comment.piece().text().lines().enumerate() {
if index == 0 {
write!(f, [dynamic_text(line.trim_end(), source_offset)])?;
} else {
write!(
f,
[align(
1,
&format_args![
hard_line_break(),
dynamic_text(line.trim(), source_offset)
]
)]
)?;
}
source_offset += line.text_len();
}

Ok(())
} else {
write!(f, [comment.piece()])
}
}
}

/// Returns `true` if `comment` is a multi line block comment:
///
/// # Examples
///
/// ## Doc Comments
///
/// ```javascript
/// /**
/// * Multiline doc comment
/// */
///
/// /*
/// * With single star
/// */
/// ```
///
/// ## Non Doc Comments
///
/// ```javascript
/// /** has no line break */
///
/// /*
/// *
/// this line doesn't start with a star
/// */
/// ```
///
fn is_doc_comment(comment: &SyntaxTriviaPieceComments<JsLanguage>) -> bool {
if !comment.has_newline() {
return false;
}

let text = comment.text();

text.lines().enumerate().all(|(index, line)| {
if index == 0 {
line.starts_with("/*")
} else {
line.trim_start().starts_with('*')
}
})
}

#[derive(Eq, PartialEq, Copy, Clone, Debug, Default)]
pub struct JsCommentStyle;

impl CommentStyle<JsLanguage> for JsCommentStyle {
fn is_suppression(&self, text: &str) -> bool {
parse_suppression_comment(text)
.flat_map(|suppression| suppression.categories)
.any(|(category, _)| category == SuppressionCategory::Format)
}

fn get_comment_kind(&self, comment: &SyntaxTriviaPieceComments<JsLanguage>) -> CommentKind {
if comment.text().starts_with("/*") {
if comment.has_newline() {
CommentKind::Block
} else {
CommentKind::InlineBlock
}
} else {
CommentKind::Line
}
}

fn is_group_start_token(&self, kind: JsSyntaxKind) -> bool {
matches!(
kind,
JsSyntaxKind::L_PAREN
| JsSyntaxKind::L_BRACK
| JsSyntaxKind::L_CURLY
| JsSyntaxKind::DOLLAR_CURLY
)
}

fn is_group_end_token(&self, kind: JsSyntaxKind) -> bool {
matches!(
kind,
JsSyntaxKind::R_BRACK
| JsSyntaxKind::R_CURLY
| JsSyntaxKind::R_PAREN
| JsSyntaxKind::COMMA
| JsSyntaxKind::SEMICOLON
| JsSyntaxKind::DOT
| JsSyntaxKind::EOF
)
}
}
56 changes: 5 additions & 51 deletions crates/rome_js_formatter/src/context.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use crate::comments::{FormatJsLeadingComment, JsCommentStyle};
use rome_formatter::printer::PrinterOptions;
use rome_formatter::{
CommentKind, CommentStyle, Comments, CstFormatContext, FormatContext, FormatOptions,
IndentStyle, LineWidth, TransformSourceMap,
Comments, CstFormatContext, FormatContext, FormatOptions, IndentStyle, LineWidth,
TransformSourceMap,
};
use rome_js_syntax::suppression::{parse_suppression_comment, SuppressionCategory};
use rome_js_syntax::{JsLanguage, JsSyntaxKind, SourceType};
use rome_rowan::SyntaxTriviaPieceComments;
use rome_js_syntax::{JsLanguage, SourceType};
use std::fmt;
use std::fmt::Debug;
use std::rc::Rc;
Expand Down Expand Up @@ -66,6 +65,7 @@ impl FormatContext for JsFormatContext {
impl CstFormatContext for JsFormatContext {
type Language = JsLanguage;
type Style = JsCommentStyle;
type LeadingCommentRule = FormatJsLeadingComment;

fn comment_style(&self) -> Self::Style {
JsCommentStyle
Expand Down Expand Up @@ -172,52 +172,6 @@ impl fmt::Display for JsFormatOptions {
}
}

#[derive(Eq, PartialEq, Copy, Clone, Debug, Default)]
pub struct JsCommentStyle;

impl CommentStyle<JsLanguage> for JsCommentStyle {
fn is_suppression(&self, text: &str) -> bool {
parse_suppression_comment(text)
.flat_map(|suppression| suppression.categories)
.any(|(category, _)| category == SuppressionCategory::Format)
}

fn get_comment_kind(&self, comment: &SyntaxTriviaPieceComments<JsLanguage>) -> CommentKind {
if comment.text().starts_with("/*") {
if comment.has_newline() {
CommentKind::Block
} else {
CommentKind::InlineBlock
}
} else {
CommentKind::Line
}
}

fn is_group_start_token(&self, kind: JsSyntaxKind) -> bool {
matches!(
kind,
JsSyntaxKind::L_PAREN
| JsSyntaxKind::L_BRACK
| JsSyntaxKind::L_CURLY
| JsSyntaxKind::DOLLAR_CURLY
)
}

fn is_group_end_token(&self, kind: JsSyntaxKind) -> bool {
matches!(
kind,
JsSyntaxKind::R_BRACK
| JsSyntaxKind::R_CURLY
| JsSyntaxKind::R_PAREN
| JsSyntaxKind::COMMA
| JsSyntaxKind::SEMICOLON
| JsSyntaxKind::DOT
| JsSyntaxKind::EOF
)
}
}

#[derive(Debug, Eq, PartialEq, Clone, Copy)]
#[cfg_attr(
feature = "serde",
Expand Down
4 changes: 3 additions & 1 deletion crates/rome_js_formatter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ mod check_reformat;
#[rustfmt::skip]
mod generated;
pub(crate) mod builders;
mod comments;
pub mod context;
mod parentheses;
pub(crate) mod separated;
Expand All @@ -271,7 +272,8 @@ use rome_rowan::TextRange;
use rome_rowan::{AstNode, SyntaxNode};

use crate::builders::{format_parenthesize, format_suppressed_node};
use crate::context::{JsCommentStyle, JsFormatContext, JsFormatOptions};
use crate::comments::JsCommentStyle;
use crate::context::{JsFormatContext, JsFormatOptions};
use crate::cst::FormatJsSyntaxNode;
use crate::syntax_rewriter::transform;
use std::iter::FusedIterator;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ use crate::parentheses::{
is_arrow_function_body, is_callee, is_member_object, is_spread, is_tag, NeedsParentheses,
};

use crate::context::JsCommentStyle;
use crate::comments::JsCommentStyle;
use crate::js::expressions::static_member_expression::JsAnyStaticMemberLike;
use crate::utils::assignment_like::has_leading_own_line_comment;
use rome_rowan::{declare_node_union, AstNode, SyntaxResult};
Expand Down

This file was deleted.

This file was deleted.

Loading

0 comments on commit 5b6740c

Please sign in to comment.