From 53409ef61cd1e9deff3848bdb919a2287fbec310 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Tue, 2 Apr 2019 00:07:55 -0700 Subject: [PATCH] Add flags to support apache/spark style of configuration Add a `verticalAlignMultilineOperators` boolean flag to vertically align multiline operator chains Make `danglingParentheses` a case class so it can be specified separately for def and call sites (we only enable it for callsites) --- .../scalafmt/config/DanglingParentheses.scala | 34 ++++++++++++++++ .../org/scalafmt/config/ScalafmtConfig.scala | 7 ++-- .../org/scalafmt/internal/FormatOps.scala | 4 +- .../scala/org/scalafmt/internal/Router.scala | 23 ++++++----- .../ContinuationIndentHalfDanglingParens.stat | 34 ++++++++++++++++ .../verticalAlignMultilineOperators.stat | 39 +++++++++++++++++++ 6 files changed, 127 insertions(+), 14 deletions(-) create mode 100644 scalafmt-core/shared/src/main/scala/org/scalafmt/config/DanglingParentheses.scala create mode 100644 scalafmt-tests/src/test/resources/test/ContinuationIndentHalfDanglingParens.stat create mode 100644 scalafmt-tests/src/test/resources/vertical-multiline/verticalAlignMultilineOperators.stat diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/config/DanglingParentheses.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/config/DanglingParentheses.scala new file mode 100644 index 0000000000..b3679dba3c --- /dev/null +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/config/DanglingParentheses.scala @@ -0,0 +1,34 @@ +package org.scalafmt.config + +import metaconfig._ + +case class DanglingParentheses( + callSite: Boolean, + defnSite: Boolean +) { + val decoder: ConfDecoder[DanglingParentheses] = + generic.deriveDecoder(this).noTypos +} +object DanglingParentheses { + val default = DanglingParentheses(true, true) + implicit lazy val surface: generic.Surface[DanglingParentheses] = + generic.deriveSurface + + implicit val decoder: ConfDecoder[DanglingParentheses] = + ConfDecoder.instance[DanglingParentheses] { + case Conf.Bool(true) => Configured.Ok(DanglingParentheses(true, true)) + case Conf.Bool(false) => Configured.Ok(DanglingParentheses(false, false)) + case els => default.decoder.read(els) + } + + implicit val encoder: ConfEncoder[DanglingParentheses] = + ConfEncoder.instance[DanglingParentheses] { + case DanglingParentheses(true, true) => Conf.Bool(true) + case DanglingParentheses(true, false) => + Conf.Obj("callSite" -> Conf.Bool(true), "defnSite" -> Conf.Bool(false)) + case DanglingParentheses(false, true) => + Conf.Obj("callSite" -> Conf.Bool(false), "defnSite" -> Conf.Bool(true)) + case DanglingParentheses(false, false) => Conf.Bool(false) + + } +} diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/config/ScalafmtConfig.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/config/ScalafmtConfig.scala index 8354fbffa0..557ed4b147 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/config/ScalafmtConfig.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/config/ScalafmtConfig.scala @@ -148,7 +148,7 @@ case class ScalafmtConfig( includeCurlyBraceInSelectChains: Boolean = true, includeNoParensInSelectChains: Boolean = false, assumeStandardLibraryStripMargin: Boolean = false, - danglingParentheses: Boolean = true, + danglingParentheses: DanglingParentheses = DanglingParentheses(true, true), poorMansTrailingCommasInConfigStyle: Boolean = false, trailingCommas: TrailingCommas = TrailingCommas.never, @deprecated("Use VerticalMultiline.atDefnSite instead", "1.6.0") @@ -156,6 +156,7 @@ case class ScalafmtConfig( @deprecated("Use VerticalMultiline.arityThreshold instead", "1.6.0") verticalMultilineAtDefinitionSiteArityThreshold: Int = 100, verticalMultiline: VerticalMultiline = VerticalMultiline(), + verticalAlignMultilineOperators: Boolean = false, onTestFailure: String = "", encoding: Codec = "UTF-8", project: ProjectFiles = ProjectFiles() @@ -212,7 +213,7 @@ object ScalafmtConfig { optIn = default.optIn.copy( configStyleArguments = false ), - danglingParentheses = true + danglingParentheses = DanglingParentheses(true, true) ) def addAlign(style: ScalafmtConfig): ScalafmtConfig = style.copy( @@ -286,7 +287,7 @@ object ScalafmtConfig { maxColumn = 79, assumeStandardLibraryStripMargin = false, includeCurlyBraceInSelectChains = false, - danglingParentheses = false, + danglingParentheses = DanglingParentheses(false, false), align = default.align.copy( tokens = Set.empty, openParenCallSite = true, diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatOps.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatOps.scala index cef9afa3de..d22213c80a 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatOps.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatOps.scala @@ -520,7 +520,9 @@ class FormatOps(val tree: Tree, val initStyle: ScalafmtConfig) { rightIsComment = formatToken.right.isInstanceOf[Comment] ) val indent = { - if (style.unindentTopLevelOperators && + if (style.verticalAlignMultilineOperators) { + if (formatToken.left.text == "=") 2 else 0 + } else if (style.unindentTopLevelOperators && !isTopLevelInfixApplication(owner) && style.indentOperator.includeRegexp .findFirstIn(formatToken.left.syntax) diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/Router.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/Router.scala index 9143cfedad..df239b1aaf 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/Router.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/Router.scala @@ -665,18 +665,21 @@ class Router(formatOps: FormatOps) { val tooManyArguments = args.length > 100 - val newlinePolicy: Policy = if (style.danglingParentheses) { - val breakOnClosing = Policy({ - case d @ Decision(FormatToken(_, `close`, _), s) => - d.onlyNewlines - }, close.end) - breakOnClosing - } else { - Policy(PartialFunction.empty[Decision, Decision], close.end) - } + val newlinePolicy: Policy = + if (style.danglingParentheses.defnSite && defnSite || + style.danglingParentheses.callSite && !defnSite) { + val breakOnClosing = Policy({ + case d @ Decision(FormatToken(_, `close`, _), s) => + d.onlyNewlines + }, close.end) + breakOnClosing + } else { + Policy(PartialFunction.empty[Decision, Decision], close.end) + } val noSplitPolicy = - if (style.danglingParentheses) { + if (style.danglingParentheses.defnSite && defnSite || + style.danglingParentheses.callSite && !defnSite) { SingleLineBlock(close, exclude = excludeRanges) } else singleLine(10) diff --git a/scalafmt-tests/src/test/resources/test/ContinuationIndentHalfDanglingParens.stat b/scalafmt-tests/src/test/resources/test/ContinuationIndentHalfDanglingParens.stat new file mode 100644 index 0000000000..73cc1b3b6d --- /dev/null +++ b/scalafmt-tests/src/test/resources/test/ContinuationIndentHalfDanglingParens.stat @@ -0,0 +1,34 @@ +maxColumn = 6 +danglingParentheses = {callSite: true, defnSite: false} + +<<< classes and methods +object Foo3 { + def f(a: A, b: B, c: C) = 1 + class F(a: A, b: B, c: C) + + f(a, b, c) + new F(a, b, c) +} +>>> +object Foo3 { + def f( + a: A, + b: B, + c: C) = + 1 + class F( + a: A, + b: B, + c: C) + + f( + a, + b, + c + ) + new F( + a, + b, + c + ) +} \ No newline at end of file diff --git a/scalafmt-tests/src/test/resources/vertical-multiline/verticalAlignMultilineOperators.stat b/scalafmt-tests/src/test/resources/vertical-multiline/verticalAlignMultilineOperators.stat new file mode 100644 index 0000000000..ed64dfd193 --- /dev/null +++ b/scalafmt-tests/src/test/resources/vertical-multiline/verticalAlignMultilineOperators.stat @@ -0,0 +1,39 @@ +verticalAlignMultilineOperators = true +maxColumn = 10 + +<<< in function call +object Foo1 { + function( + a && + b + ) +} +>>> +object Foo1 { + function( + a && + b + ) +} +<<< after assignment +object Foo2 { + val x = + a + + b +} +>>> +object Foo2 { + val x = + a + + b +} +<<< in template body +object Foo3 { + a + + b +} +>>> +object Foo3 { + a + + b +}