From bd3c9d0d465a1a1c9c0a1040dc35aa890274a594 Mon Sep 17 00:00:00 2001 From: Albert Meltzer <7529386+kitbellew@users.noreply.github.com> Date: Sat, 4 May 2024 04:02:36 -0700 Subject: [PATCH 1/2] Docstrings: use keep/unfold in Wrap and add fold For now, `fold` is the same as `unfold`. --- docs/configuration.md | 8 +++++++ .../org/scalafmt/config/Docstrings.scala | 17 ++++++++------ .../org/scalafmt/internal/FormatWriter.scala | 10 ++++---- .../src/test/resources/test/JavaDoc.stat | 23 +++++++++++++++++++ .../test/scala/org/scalafmt/CommentTest.scala | 2 +- 5 files changed, 46 insertions(+), 14 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index cb0f262309..fb1adc2042 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -4079,6 +4079,14 @@ val a = 1 ### `docstrings.wrap` Will parse scaladoc comments and reformat them. +Takes the following values: + +- `keep`: preserves scaladoc comments as-is and will not reformat them + (replaced `no` in v3.8.2) +- `fold`: will use a more compact, horizontal formatting + (added in v3.8.2) +- `unfold`: will use a more expanded, vertical formatting + (replaced `yes` in v3.8.2) This functionality is generally limited to [standard scaladoc elements](https://docs.scala-lang.org/overviews/scaladoc/for-library-authors.html) diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/config/Docstrings.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/config/Docstrings.scala index bdfde87744..79c2a25b2e 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/config/Docstrings.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/config/Docstrings.scala @@ -19,7 +19,7 @@ import metaconfig._ case class Docstrings( oneline: Docstrings.Oneline = Docstrings.Oneline.keep, removeEmpty: Boolean = false, - wrap: Docstrings.Wrap = Docstrings.Wrap.yes, + wrap: Docstrings.Wrap = Docstrings.Wrap.unfold, private[config] val wrapMaxColumn: Option[Int] = None, forceBlankLineBefore: Option[Boolean] = None, blankFirstLine: Option[Docstrings.BlankFirstLine] = None, @@ -28,7 +28,7 @@ case class Docstrings( import Docstrings._ def withoutRewrites: Docstrings = - copy(removeEmpty = false, wrap = Wrap.no, style = Preserve) + copy(removeEmpty = false, wrap = Wrap.keep, style = Preserve) def skipFirstLineIf(wasBlank: Boolean): Boolean = style.skipFirstLine .orElse(blankFirstLine).exists { @@ -80,12 +80,15 @@ object Docstrings { sealed abstract class Wrap object Wrap { - case object no extends Wrap - case object yes extends Wrap + case object keep extends Wrap + case object fold extends Wrap + case object unfold extends Wrap implicit val codec: ConfCodecEx[Wrap] = ReaderUtil - .oneOfCustom[Wrap](no, yes) { - case Conf.Bool(true) => Configured.Ok(yes) - case Conf.Bool(false) => Configured.Ok(no) + .oneOfCustom[Wrap](keep, fold, unfold) { + case Conf.Str("no") => Configured.Ok(keep) + case Conf.Bool(false) => Configured.Ok(keep) + case Conf.Str("yes") => Configured.Ok(unfold) + case Conf.Bool(true) => Configured.Ok(unfold) } } diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatWriter.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatWriter.scala index b6fbd82f18..97023fda36 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatWriter.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatWriter.scala @@ -596,11 +596,9 @@ class FormatWriter(formatOps: FormatOps) { matcher.start(3) == -1 }) && { val content = matcher.group(2) - val folding = style.docstrings.wrap match { - case Docstrings.Wrap.yes => content.length <= // 7 is the length of "/** " and " */" - style.docstringsWrapMaxColumn - prevState.indentation - 7 - case _ => true - } + val folding = (style.docstrings.wrap eq Docstrings.Wrap.keep) || + content.length <= // 7 is the length of "/** " and " */" + style.docstringsWrapMaxColumn - prevState.indentation - 7 if (folding) sb.append("/** ").append(content).append(" */") folding } @@ -828,7 +826,7 @@ class FormatWriter(formatOps: FormatOps) { if (style.docstrings.style eq Docstrings.AsteriskSpace) 1 else 0, ) { def this(text: String)(implicit sb: StringBuilder) = this( - (style.docstrings.wrap eq Docstrings.Wrap.yes) && curr.isStandalone, + (style.docstrings.wrap ne Docstrings.Wrap.keep) && curr.isStandalone, )(text) private val spaces: String = getIndentation(indent + extraIndent) diff --git a/scalafmt-tests/src/test/resources/test/JavaDoc.stat b/scalafmt-tests/src/test/resources/test/JavaDoc.stat index 6ec43ec3eb..d57629f1a2 100644 --- a/scalafmt-tests/src/test/resources/test/JavaDoc.stat +++ b/scalafmt-tests/src/test/resources/test/JavaDoc.stat @@ -3434,3 +3434,26 @@ def foo = () * }}} */ def foo = () +<<< #1387 add fold +docstrings.wrap = fold +docstrings.style = Asterisk +maxColumn = 30 +=== +/** Start the comment here + * + * @param d a b c d e f g h i jj k l m n + * @param ee a b c d e f g h i j k l m n + */ +def foo = () +>>> +/** + * Start the comment here + * + * @param d + * a b c d e f g h i jj k l + * m n + * @param ee + * a b c d e f g h i j k l m + * n + */ +def foo = () diff --git a/scalafmt-tests/src/test/scala/org/scalafmt/CommentTest.scala b/scalafmt-tests/src/test/scala/org/scalafmt/CommentTest.scala index ff0e14c7e5..669ff68a54 100644 --- a/scalafmt-tests/src/test/scala/org/scalafmt/CommentTest.scala +++ b/scalafmt-tests/src/test/scala/org/scalafmt/CommentTest.scala @@ -10,7 +10,7 @@ class CommentTest extends FunSuite { private val javadocStyle: ScalafmtConfig = ScalafmtConfig.default .copy(docstrings = ScalafmtConfig.default.docstrings - .copy(style = Docstrings.Asterisk, wrap = Docstrings.Wrap.no), + .copy(style = Docstrings.Asterisk, wrap = Docstrings.Wrap.keep), ) test("remove trailing space in comments") { From 98dd6405075cfac808823d53acc52e848c168e16 Mon Sep 17 00:00:00 2001 From: Albert Meltzer <7529386+kitbellew@users.noreply.github.com> Date: Sat, 4 May 2024 04:29:12 -0700 Subject: [PATCH 2/2] Docstrings: implement the fold option to Wrap --- .../org/scalafmt/internal/FormatWriter.scala | 28 +++++++++++++------ .../src/test/resources/test/JavaDoc.stat | 10 +++---- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatWriter.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatWriter.scala index 97023fda36..a40a82b813 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatWriter.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatWriter.scala @@ -818,15 +818,16 @@ class FormatWriter(formatOps: FormatOps) { } } - private class FormatMlDoc(isWrap: Boolean)(text: String)(implicit + private class FormatMlDoc(wrap: Docstrings.Wrap)(text: String)(implicit sb: StringBuilder, ) extends FormatCommentBase( - if (isWrap) style.docstringsWrapMaxColumn else style.maxColumn, + if (wrap eq Docstrings.Wrap.keep) style.maxColumn + else style.docstringsWrapMaxColumn, if (style.docstrings.style eq Docstrings.SpaceAsterisk) 2 else 1, if (style.docstrings.style eq Docstrings.AsteriskSpace) 1 else 0, ) { def this(text: String)(implicit sb: StringBuilder) = this( - (style.docstrings.wrap ne Docstrings.Wrap.keep) && curr.isStandalone, + if (curr.isStandalone) style.docstrings.wrap else Docstrings.Wrap.keep, )(text) private val spaces: String = getIndentation(indent + extraIndent) @@ -834,7 +835,8 @@ class FormatWriter(formatOps: FormatOps) { def format(): Unit = { val docOpt = - if (isWrap) ScaladocParser.parse(tok.meta.left.text) else None + if (wrap eq Docstrings.Wrap.keep) None + else ScaladocParser.parse(tok.meta.left.text) docOpt.fold(formatNoWrap())(formatWithWrap) } @@ -870,6 +872,7 @@ class FormatWriter(formatOps: FormatOps) { sb.setLength(sb.length - 1) // remove space appendBreak() } + val sbInit = sb.length if (sbNonEmpty) sb.append(termIndent) term match { case t: Scaladoc.CodeBlock => @@ -900,10 +903,17 @@ class FormatWriter(formatOps: FormatOps) { case t: Scaladoc.Tag => sb.append(t.tag.tag) t.label.foreach(x => sb.append(' ').append(x.syntax)) - appendBreak() - if (t.desc.nonEmpty) { + if (t.desc.isEmpty) appendBreak() + else { val tagIndent = getIndentation(2 + termIndent.length) - t.desc.foreach(formatTerm(_, tagIndent, sbNonEmpty = true)) + t.desc match { + case Seq(text: Scaladoc.Text) + if wrap eq Docstrings.Wrap.fold => + formatTextAfterMargin(text, tagIndent, sb.length - sbInit) + case desc => + appendBreak() + desc.foreach(formatTerm(_, tagIndent, sbNonEmpty = true)) + } } case t: Scaladoc.ListBlock => // outputs margin space and appends new line, too @@ -923,6 +933,7 @@ class FormatWriter(formatOps: FormatOps) { private def formatTextAfterMargin( text: Scaladoc.Text, termIndent: String, + lineLengthSoFar: Int = 0, ): Unit = { def prefixFirstWord(word: String): String = { def likeNonText = word.startsWith("```") || @@ -937,7 +948,8 @@ class FormatWriter(formatOps: FormatOps) { val wf = new WordFormatter(appendBreak, termIndent, prefixFirstWord) val words = text.parts.iterator.map(_.syntax) - wf(words, termIndent.length, true, false) + val lineLength = math.max(lineLengthSoFar, termIndent.length) + wf(words, lineLength, lineLengthSoFar == 0, false) appendBreak() } diff --git a/scalafmt-tests/src/test/resources/test/JavaDoc.stat b/scalafmt-tests/src/test/resources/test/JavaDoc.stat index d57629f1a2..9c7294f7cf 100644 --- a/scalafmt-tests/src/test/resources/test/JavaDoc.stat +++ b/scalafmt-tests/src/test/resources/test/JavaDoc.stat @@ -3449,11 +3449,9 @@ def foo = () /** * Start the comment here * - * @param d - * a b c d e f g h i jj k l - * m n - * @param ee - * a b c d e f g h i j k l m - * n + * @param d a b c d e f g h i + * jj k l m n + * @param ee a b c d e f g h i + * j k l m n */ def foo = ()