diff --git a/CHANGELOG b/CHANGELOG
index 036978b5..2a325eb1 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,7 @@
+0.2.5 (27/October/17)
+
+* Added support for single case pattern without forced newline (fixes #29)
+
0.2.4 (25/October/17)
* Updated to Scala 2.12.4
diff --git a/README.rst b/README.rst
index c526e8e4..467e4c81 100644
--- a/README.rst
+++ b/README.rst
@@ -52,7 +52,7 @@ Usage within a project
Have a use for the scalariform source code directly? You can use it as a build dependency: ::
- "org.scalariform" %% "scalariform" % "0.2.4"
+ "org.scalariform" %% "scalariform" % "0.2.5"
Integration with Eclipse
------------------------
@@ -765,6 +765,20 @@ is formatted as:
case 1 ⇒ println("odd")
}
+singleCasePatternOnNewline
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Default: ``true``
+
+When ``singleCasePatternOnNewline`` is ``false`` the default behavior of forcing
+a single case pattern onto a newline is disabled. This allows for the following formatting style:
+
+.. code:: scala
+
+ items.map { case (key, value) =>
+ (key, transform(value))
+ }
+
spaceBeforeColon
~~~~~~~~~~~~~~~~
diff --git a/formatterPreferences.properties b/formatterPreferences.properties
index 14e08ba7..22eec8b2 100644
--- a/formatterPreferences.properties
+++ b/formatterPreferences.properties
@@ -21,6 +21,7 @@ danglingCloseParenthesis=Force
#placeScaladocAsterisksBeneathSecondAsterisk=false
#preserveSpaceBeforeArguments=false
#rewriteArrowSymbols=false
+#singleCasePatternOnNewline=true
#spaceBeforeColon=false
#spaceBeforeContextColon=false
#spaceInsideBrackets=false
diff --git a/pom.xml b/pom.xml
index 0d600a86..18d396fe 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
4.0.0
org.scalariform
scalariform.parent
- 0.2.4
+ 0.2.5
pom
diff --git a/scalariform.feature/feature.xml b/scalariform.feature/feature.xml
index 10196cf1..35a67875 100644
--- a/scalariform.feature/feature.xml
+++ b/scalariform.feature/feature.xml
@@ -2,7 +2,7 @@
+ version="0.2.5">
Scala Code formatter
diff --git a/scalariform.feature/pom.xml b/scalariform.feature/pom.xml
index 525884ae..8deb9e33 100644
--- a/scalariform.feature/pom.xml
+++ b/scalariform.feature/pom.xml
@@ -8,6 +8,6 @@
scalariform.parent
org.scalariform
- 0.2.4
+ 0.2.5
diff --git a/scalariform.update/pom.xml b/scalariform.update/pom.xml
index 8918266b..f7b0a74b 100644
--- a/scalariform.update/pom.xml
+++ b/scalariform.update/pom.xml
@@ -8,6 +8,6 @@
scalariform.parent
org.scalariform
- 0.2.4
+ 0.2.5
diff --git a/scalariform.update/site.xml b/scalariform.update/site.xml
index 7d366693..128e251e 100644
--- a/scalariform.update/site.xml
+++ b/scalariform.update/site.xml
@@ -1,10 +1,10 @@
+ url="https://github.com/scala-ide/scalariform/tree/0.2.5/scalariform.update/target/site">
Scalariform Update Site
-
+
diff --git a/scalariform.update/target/site.zip b/scalariform.update/target/site.zip
index fc9707f0..da6256a9 100644
Binary files a/scalariform.update/target/site.zip and b/scalariform.update/target/site.zip differ
diff --git a/scalariform.update/target/site/artifacts.jar b/scalariform.update/target/site/artifacts.jar
index 2260a6a5..3bc24044 100644
Binary files a/scalariform.update/target/site/artifacts.jar and b/scalariform.update/target/site/artifacts.jar differ
diff --git a/scalariform.update/target/site/content.jar b/scalariform.update/target/site/content.jar
index 27a50674..5d2c0b5a 100644
Binary files a/scalariform.update/target/site/content.jar and b/scalariform.update/target/site/content.jar differ
diff --git a/scalariform.update/target/site/features/scalariform.feature_0.2.4.jar b/scalariform.update/target/site/features/scalariform.feature_0.2.4.jar
deleted file mode 100644
index a12450a7..00000000
Binary files a/scalariform.update/target/site/features/scalariform.feature_0.2.4.jar and /dev/null differ
diff --git a/scalariform.update/target/site/features/scalariform.feature_0.2.5.jar b/scalariform.update/target/site/features/scalariform.feature_0.2.5.jar
new file mode 100644
index 00000000..c8ce62f4
Binary files /dev/null and b/scalariform.update/target/site/features/scalariform.feature_0.2.5.jar differ
diff --git a/scalariform.update/target/site/plugins/scalariform_0.2.4.jar b/scalariform.update/target/site/plugins/scalariform_0.2.5.jar
similarity index 87%
rename from scalariform.update/target/site/plugins/scalariform_0.2.4.jar
rename to scalariform.update/target/site/plugins/scalariform_0.2.5.jar
index bfb9c244..26c473e4 100644
Binary files a/scalariform.update/target/site/plugins/scalariform_0.2.4.jar and b/scalariform.update/target/site/plugins/scalariform_0.2.5.jar differ
diff --git a/scalariform.update/target/site/site.xml b/scalariform.update/target/site/site.xml
index 7d366693..128e251e 100644
--- a/scalariform.update/target/site/site.xml
+++ b/scalariform.update/target/site/site.xml
@@ -1,10 +1,10 @@
+ url="https://github.com/scala-ide/scalariform/tree/0.2.5/scalariform.update/target/site">
Scalariform Update Site
-
+
diff --git a/scalariform.update/target/site_assembly.zip b/scalariform.update/target/site_assembly.zip
index cde75b80..dd1e9f36 100644
Binary files a/scalariform.update/target/site_assembly.zip and b/scalariform.update/target/site_assembly.zip differ
diff --git a/scalariform/META-INF/MANIFEST.MF b/scalariform/META-INF/MANIFEST.MF
index 8cd0d743..ae9211db 100644
--- a/scalariform/META-INF/MANIFEST.MF
+++ b/scalariform/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Scalariform
Bundle-SymbolicName: scalariform
-Bundle-Version: 0.2.4
+Bundle-Version: 0.2.5
Require-Bundle: org.scala-lang.scala-library,
org.scala-lang.modules.scala-xml
Bundle-ClassPath: .
diff --git a/scalariform/pom.xml b/scalariform/pom.xml
index 94534787..71cc1009 100644
--- a/scalariform/pom.xml
+++ b/scalariform/pom.xml
@@ -8,7 +8,7 @@
scalariform.parent
org.scalariform
- 0.2.4
+ 0.2.5
diff --git a/scalariform/src/main/scala/scalariform/formatter/CaseClauseFormatter.scala b/scalariform/src/main/scala/scalariform/formatter/CaseClauseFormatter.scala
index b0c56fcb..36cf1407 100644
--- a/scalariform/src/main/scala/scalariform/formatter/CaseClauseFormatter.scala
+++ b/scalariform/src/main/scala/scalariform/formatter/CaseClauseFormatter.scala
@@ -18,6 +18,7 @@ trait CaseClauseFormatter { self: HasFormattingPreferences with ExprFormatter wi
var formatResult: FormatResult = NoFormatResult
var isFirstCaseClause = true
+ val hasSingleCaseClause = clauseGroups.size == 1
// We have to decide whether to indent the hidden tokens before the CASE token (or possibly a preceding
// NEWLINE token from a prior case block).
@@ -35,7 +36,7 @@ trait CaseClauseFormatter { self: HasFormattingPreferences with ExprFormatter wi
def formatSingleCaseClause(caseClause: CaseClause) {
handleCaseIndent(caseClause)
- formatResult ++= formatCaseClause(caseClause)
+ formatResult ++= formatCaseClause(caseClause, None, hasSingleCaseClause)
isFirstCaseClause = false
}
@@ -46,7 +47,9 @@ trait CaseClauseFormatter { self: HasFormattingPreferences with ExprFormatter wi
for (caseClause @ CaseClause(casePattern, statSeq) ← caseClauses) {
handleCaseIndent(caseClause)
val arrowInstruction = PlaceAtColumn(formatterState.indentLevel, largestCasePatternLength + 1)
- formatResult ++= formatCaseClause(caseClause, Some(arrowInstruction))
+ formatResult ++= formatCaseClause(
+ caseClause, Some(arrowInstruction), hasSingleCaseClause
+ )
isFirstCaseClause = false
}
} else {
@@ -104,22 +107,55 @@ trait CaseClauseFormatter { self: HasFormattingPreferences with ExprFormatter wi
formatResult
}
- private def formatCaseClause(caseClause: CaseClause, arrowInstructionOpt: Option[PlaceAtColumn] = None)(implicit formatterState: FormatterState): FormatResult = {
+ private def formatCaseClause(
+ caseClause: CaseClause,
+ arrowInstructionOpt: Option[PlaceAtColumn],
+ hasSingleCaseClause: Boolean
+ )(implicit formatterState: FormatterState): FormatResult = {
+
val CaseClause(casePattern, statSeq) = caseClause
var formatResult: FormatResult = NoFormatResult
formatResult ++= formatCasePattern(casePattern, arrowInstructionOpt)
+ val hasNewline = caseClause.casePattern.caseToken.associatedWhitespaceAndComments.containsNewline
+ val singleCaseWithoutNewline = (
+ hasSingleCaseClause && !hasNewline && !formattingPreferences(SingleCasePatternOnNewline)
+ )
val singleExpr =
cond(statSeq.firstStatOpt) { case Some(Expr(_)) ⇒ true } &&
cond(statSeq.otherStats) { case Nil | List((_, None)) ⇒ true }
val indentBlock =
statSeq.firstTokenOption.isDefined && newlineBefore(statSeq) ||
containsNewline(statSeq) && !singleExpr
- if (indentBlock)
- formatResult = formatResult.before(statSeq.firstToken, formatterState.nextIndentLevelInstruction)
- val stateForStatSeq = if (singleExpr && !indentBlock) formatterState else formatterState.indent
- formatResult ++= format(statSeq)(stateForStatSeq)
+ def unindent(x: Map[Token, IntertokenFormatInstruction]) = x.map {
+ case (k, v @ EnsureNewlineAndIndent(indentLevel, relativeTo)) =>
+ k -> EnsureNewlineAndIndent(indentLevel - 1, relativeTo)
+ case x => x
+ }
+
+ if (indentBlock) {
+ val result = formatResult.before(statSeq.firstToken, formatterState.nextIndentLevelInstruction)
+ formatResult =
+ if(!singleCaseWithoutNewline) result
+ else
+ result.copy(
+ predecessorFormatting =
+ unindent(result.predecessorFormatting) + ( // unindent first token in case body
+ caseClause.casePattern.caseToken -> CompactEnsuringGap // remove `case` leading newline
+ )
+ )
+ }
+ val stateForStatSeq = if (singleExpr && !indentBlock) formatterState else formatterState.indent
+ formatResult ++= {
+ val result = format(statSeq)(stateForStatSeq)
+ if(!singleCaseWithoutNewline) result
+ else
+ result.copy( // unindent body tokens
+ predecessorFormatting = unindent(result.predecessorFormatting),
+ inferredNewlineFormatting = unindent(result.inferredNewlineFormatting)
+ )
+ }
formatResult
}
diff --git a/scalariform/src/main/scala/scalariform/formatter/ScalaFormatter.scala b/scalariform/src/main/scala/scalariform/formatter/ScalaFormatter.scala
index e307171c..7a52bb4d 100755
--- a/scalariform/src/main/scala/scalariform/formatter/ScalaFormatter.scala
+++ b/scalariform/src/main/scala/scalariform/formatter/ScalaFormatter.scala
@@ -91,26 +91,6 @@ abstract class ScalaFormatter
var suspendFormatting = false
var edits: List[TextEdit] = Nil // Stored in reverse
- def printableFormattingInstruction(previousTokenOption: Option[Token], token: Token, nextTokenOption: Option[Token]) = {
- val maybePredecessorFormatting = predecessorFormatting.get(token)
- val isGaplessAssignment =
- // avoid `foreach(_.id= ..)` and `foreach(foo= _)` gapless assignment (see MutateTest.scala)
- maybePredecessorFormatting exists {
- case x @ PlaceAtColumn(_, _, Some(Token(USCORE, _, _, _))) => token.tokenType == EQUALS
- case _ => token.tokenType == EQUALS && nextTokenOption.exists(_.tokenType == USCORE)
- }
- val maybeInstruction =
- if (isGaplessAssignment) Some(CompactEnsuringGap)
- else
- maybePredecessorFormatting.orElse(
- previousTokenOption.map(defaultFormattingInstruction(_, token))
- )
- maybeInstruction.getOrElse(
- if (token.tokenType == EOF) EnsureNewlineAndIndent(0) /* <-- to allow formatting of files with just a scaladoc comment */
- else Compact
- )
- }
-
for ((previousTokenOption, token, nextTokenOption) ← Utils.withPreviousAndNext(tokens)) {
val previousTokenIsPrintable = previousTokenOption exists { !isInferredNewline(_) }
if (isInferredNewline(token)) {
@@ -130,7 +110,9 @@ abstract class ScalaFormatter
basicFormattingInstruction
val nextTokenUnindents = nextTokenOption exists { _.tokenType == RBRACE }
val includeBufferBeforeNextToken = nextTokenOption exists { nextToken ⇒
- !printableFormattingInstruction(Some(token), nextToken, None).isInstanceOf[EnsureNewlineAndIndent]
+ !printableFormattingInstruction(
+ Some(token), nextToken, None, predecessorFormatting
+ ).isInstanceOf[EnsureNewlineAndIndent]
}
edits :::= writeHiddenTokens(builder, inferredNewlines(token), formattingInstruction, nextTokenUnindents,
includeBufferBeforeNextToken, previousTokenIsPrintable, tokenIndentMap).toList
@@ -142,7 +124,10 @@ abstract class ScalaFormatter
tokenIndentMap += (token -> builder.currentColumn)
builder.append(token.rawText)
} else {
- val formattingInstruction = printableFormattingInstruction(previousTokenOption, token, nextTokenOption)
+ val formattingInstruction =
+ printableFormattingInstruction(
+ previousTokenOption, token, nextTokenOption, predecessorFormatting
+ )
val nextTokenUnindents = token.tokenType == RBRACE
val includeBufferBeforeNextToken = true // <-- i.e. current token
val hiddenTokens = hiddenPredecessors(token)
@@ -444,6 +429,31 @@ abstract class ScalaFormatter
result
}
+ private def printableFormattingInstruction(
+ previousTokenOption: Option[Token],
+ token: Token,
+ nextTokenOption: Option[Token],
+ predecessorFormatting: Map[Token, IntertokenFormatInstruction]): IntertokenFormatInstruction = {
+
+ val maybePredecessorFormatting = predecessorFormatting.get(token)
+ val isGaplessAssignment =
+ maybePredecessorFormatting match {
+ // `foreach(_.id= ..)`
+ case x @ Some(PlaceAtColumn(_, _, Some(Token(USCORE, _, _, _)))) => token.tokenType == EQUALS
+ // `foreach(foo= _)`
+ case _ => token.tokenType == EQUALS && nextTokenOption.exists(_.tokenType == USCORE)
+ }
+ val maybeInstruction =
+ if(isGaplessAssignment) Some(CompactEnsuringGap)
+ else maybePredecessorFormatting.orElse(
+ previousTokenOption.map(defaultFormattingInstruction(_, token))
+ )
+ maybeInstruction.getOrElse(
+ if (token.tokenType == EOF) EnsureNewlineAndIndent(0) /* <-- to allow formatting of files with just a scaladoc comment */
+ else Compact
+ )
+ }
+
private def defaultFormattingInstruction(token1: Token, token2: Token): IntertokenFormatInstruction = {
val result = actualDefaultFormattingInstruction(token1, token2)
// println("defaultFormattingInstruction(" + token1 + ", " + token2 + ") = " + result)
diff --git a/scalariform/src/main/scala/scalariform/formatter/preferences/PreferenceDescriptor.scala b/scalariform/src/main/scala/scalariform/formatter/preferences/PreferenceDescriptor.scala
index 1f36591c..271e4f18 100755
--- a/scalariform/src/main/scala/scalariform/formatter/preferences/PreferenceDescriptor.scala
+++ b/scalariform/src/main/scala/scalariform/formatter/preferences/PreferenceDescriptor.scala
@@ -93,9 +93,10 @@ object AllPreferences {
AlignArguments, AlignParameters, AlignSingleLineCaseStatements, AlignSingleLineCaseStatements.MaxArrowIndent,
AllowParamGroupsOnNewlines, CompactControlReadability, CompactStringConcatenation, DanglingCloseParenthesis,
DoubleIndentClassDeclaration, DoubleIndentConstructorArguments, DoubleIndentMethodDeclaration, FirstArgumentOnNewline,
- FirstParameterOnNewline, FormatXml, IndentLocalDefs, IndentPackageBlocks, IndentSpaces, IndentWithTabs, MultilineScaladocCommentsStartOnFirstLine,
- NewlineAtEndOfFile, PlaceScaladocAsterisksBeneathSecondAsterisk, PreserveSpaceBeforeArguments, RewriteArrowSymbols,
- SpaceBeforeColon, SpaceBeforeContextColon, SpaceInsideBrackets, SpaceInsideParentheses, SpacesAroundMultiImports, SpacesWithinPatternBinders
+ FirstParameterOnNewline, FormatXml, IndentLocalDefs, IndentPackageBlocks, IndentSpaces, IndentWithTabs,
+ MultilineScaladocCommentsStartOnFirstLine, NewlineAtEndOfFile, PlaceScaladocAsterisksBeneathSecondAsterisk,
+ PreserveSpaceBeforeArguments, RewriteArrowSymbols, SingleCasePatternOnNewline, SpaceBeforeColon,
+ SpaceBeforeContextColon, SpaceInsideBrackets, SpaceInsideParentheses, SpacesAroundMultiImports, SpacesWithinPatternBinders
)
val preferencesByKey: Map[String, PreferenceDescriptor[_]] =
@@ -245,6 +246,12 @@ case object RewriteArrowSymbols extends BooleanPreferenceDescriptor {
val defaultValue = false
}
+case object SingleCasePatternOnNewline extends BooleanPreferenceDescriptor {
+ val key = "singleCasePatternOnNewline"
+ val description = "Single case in pattern match block on a new line"
+ val defaultValue = true
+}
+
case object SpaceBeforeColon extends BooleanPreferenceDescriptor {
val key = "spaceBeforeColon"
val description = "Space before colons"
diff --git a/scalariform/src/test/scala/scalariform/formatter/CaseClausesFormatterTest.scala b/scalariform/src/test/scala/scalariform/formatter/CaseClausesFormatterTest.scala
index f59fa5c7..731352dc 100644
--- a/scalariform/src/test/scala/scalariform/formatter/CaseClausesFormatterTest.scala
+++ b/scalariform/src/test/scala/scalariform/formatter/CaseClausesFormatterTest.scala
@@ -84,6 +84,48 @@ class CaseClausesFormatterTest extends AbstractExpressionFormatterTest {
| d
|}"""
+ {
+ implicit val formattingPreferences = FormattingPreferences.setPreference(
+ SingleCasePatternOnNewline, false
+ )
+ """a match { case a => b; c;
+ |d }""" ==>
+ """a match { case a =>
+ | b; c;
+ | d
+ |}"""
+
+ """a match { case b =>
+ |val c = d
+ |case e =>
+ |}""" ==>
+ """a match {
+ | case b =>
+ | val c = d
+ | case e =>
+ |}"""
+
+ """list.foreach { case (key, value) =>
+ |val boo = 1
+ |boo
+ |}""" ==>
+ """list.foreach { case (key, value) =>
+ | val boo = 1
+ | boo
+ |}"""
+
+ """list.foreach {
+ |case (key, value) =>
+ |val boo = 1
+ |boo
+ |}""" ==>
+ """list.foreach {
+ | case (key, value) =>
+ | val boo = 1
+ | boo
+ |}"""
+ }
+
"a match { case b => ; c }" ==> "a match { case b => ; c }"
// See issue #60
diff --git a/version.sbt b/version.sbt
index a8aa224f..39eecbcb 100644
--- a/version.sbt
+++ b/version.sbt
@@ -1 +1 @@
-version in ThisBuild := "0.2.5-SNAPSHOT"
+version in ThisBuild := "0.2.5"