Search code examples
scalaformatscalafmt

How to configure the alignToken for the cases in pattern matching syntax using scalafmt?


In scala using scalafmt and having the following content in my .scalafmt.conf file:

style = default
maxColumn = 120
continuationIndent.callSite = 2
continuationIndent.defnSite = 2
align.openParenDefnSite = false
align.openParenCallSite = false
danglingParentheses = true
indentOperator = spray
project.excludeFilters = [".*\\.sbt"]
rewrite.rules = [RedundantBraces, RedundantParens, SortModifiers, prefercurlyfors]
unindentTopLevelOperators = true
importSelectors = singleLine
spaces.afterKeywordBeforeParen = true
lineEndings = unix
newlines.penalizeSingleSelectMultiArgList = false
newlines.alwaysBeforeElseAfterCurlyIf = false
binPack.literalArgumentLists = false
runner.optimizer.forceConfigStyleMinArgCount = 1

it currently aligns the case arrow token from:

object Object {
  def f(s: String): Int = s match {
    case "a" => 1
    case "b" | "c" | "d" => 2
    case "e"=> 3
    case _  => 4
  }
}

to this:

object Object {
  def f(s: String): Int = s match {
      case "a"             => 1
      case "b" | "c" | "d" => 2
      case "e"             => 3
      case _               => 4
  }
}

I don't want the code to align on the arrow in order to minimize whitespace noise in pull requests, especially during renames one one line that would change the indentation level on of the entire block.

Reading the scalafmt docs I only find out about the default case:

align.tokens Default: [caseArrow]

An align token is a pair of code, which is the string literal of an operator of token, and owner, which is the kind of the closest tree node that owns that token. If no owner is provided, then all tree kinds will be matched.

x match {
  case 1  => 1 -> 2
  case 11 => 11 -> 22
}

Config for this example

align.tokens = [{code = "=>", owner = "Case"}]

I want to not only disable the alignment but I want scalafmt to ensure that there is one space before and after the => arrow. (So it basically should work in reverse of what it is currently doing.)

How can I achieve that?


Solution

  • It looks like there isn't a convenient align.tokens.remove method to go with the align.tokens.add method, however equivalent behaviour can be achieved by manually specifying the align.tokens parameter.

    The below lines will - if added to the config - replicate the default align token behaviour for everything but the case => token. It will also not align the case ⇒ token, which should - unless you are doing something unusual - maintain the same align behaviour as => .

    align.tokens = [
        { code = "extends", owner = "Defn.(Class|Trait|Object)" }
        { code = "//", owner = ".*" }
        { code = "{", owner = "Template" }
        { code = "}", owner = "Template" }
        { code = "%", owner = applyInfix }
        { code = "%%",owner =  applyInfix }
        { code = "%%%",owner =  applyInfix }
        { code = "<-", owner = "Enumerator.Generator" }
        { code = "←", owner = "Enumerator.Generator" }
        { code = "->", owner = applyInfix }
        { code = "→", owner = applyInfix }
        { code = "=", owner = "(Enumerator.Val|Defn.(Va(l|r)|Def|Type))" }
    ]
    

    The default list of tokens was taken from the source code file AlignToken.scala, where they are contained in the default object.

    If you want to remove other token formatting instances, just delete their instances from the map.

    Note that if additional default tokens are added in an updated version of scalafmt, you will have to add them manually to this param in your config to get that functionality.