Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split: represent a newline alternative differently #2011

Merged
merged 6 commits into from
Jun 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ private class BestFirstSearch private (

implicit val style = styleMap.at(splitToken)

if (curr.split != null && curr.split.modification.isNewline) {
if (curr.split != null && curr.split.isNL) {
val tokenHash = hash(splitToken.left)
if (
emptyQueueSpots.contains(tokenHash) ||
Expand Down Expand Up @@ -195,7 +195,7 @@ private class BestFirstSearch private (
actualSplit.foreach { split =>
val nextState = curr.next(split, splitToken)
val updateBest = !keepSlowStates && depth == 0 &&
split.modification.isNewline && !best.contains(curr.depth)
split.isNL && !best.contains(curr.depth)
if (updateBest) {
best.update(curr.depth, nextState)
}
Expand Down Expand Up @@ -285,7 +285,7 @@ private class BestFirstSearch private (
if (activeSplits.isEmpty) null else state // dead end if empty
else {
val split = activeSplits.head
if (split.modification.isNewline) state
if (split.isNL) state
else {
runner.event(Enqueue(split))
val ft = tokens(state.depth)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ case class Decision(formatToken: FormatToken, splits: Seq[Split]) {
import org.scalafmt.util.TokenOps._

def noNewlines: Seq[Split] =
splits.filterNot(_.modification.isNewline)
splits.filterNot(_.isNL)

def onlyNewlinesWithFallback(default: => Split): Seq[Split] = {
val filtered = onlyNewlineSplits
Expand All @@ -26,7 +26,7 @@ case class Decision(formatToken: FormatToken, splits: Seq[Split]) {
onlyNewlineSplits

private def onlyNewlineSplits: Seq[Split] =
splits.filter(_.modification.isNewline)
splits.filter(_.isNL)

def withSplits(splits: Seq[Split]): Decision = copy(splits = splits)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -389,17 +389,15 @@ class FormatOps(val tree: Tree, baseStyle: ScalafmtConfig) {
Policy(matching(tok.left)) {
case Decision(t @ FormatToken(_, _: T.Comma, _), splits)
if owner == t.meta.rightOwner && !next(t).right.is[T.Comment] =>
splits.map { x =>
if (x.modification != NoSplit) x else x.copy(modification = Newline)
}
splits.map(x => if (x.modExt.mod ne NoSplit) x else x.withMod(Newline))

case Decision(t @ FormatToken(_: T.Comma, right, _), splits)
if owner == t.meta.leftOwner &&
!right.is[T.LeftBrace] &&
// If comment is bound to comma, see unit/Comment.
(!right.is[T.Comment] || t.hasBreak) =>
val isNewline = right.is[T.Comment]
splits.filter(_.modification.isNewline == isNewline)
splits.filter(_.isNL == isNewline)
}
}

Expand All @@ -417,7 +415,7 @@ class FormatOps(val tree: Tree, baseStyle: ScalafmtConfig) {
// If comment is bound to comma, see unit/Comment.
(!right.is[T.Comment] || t.hasBreak) =>
if (!right.is[T.LeftBrace])
splits.filter(_.modification.isNewline)
splits.filter(_.isNL)
else if (!style.activeForEdition_2020_03)
splits
else
Expand Down Expand Up @@ -447,7 +445,7 @@ class FormatOps(val tree: Tree, baseStyle: ScalafmtConfig) {
(penalizeLambdas || !tok.left.is[T.RightArrow]) && !ignore(tok) =>
s.map {
case split
if split.modification.isNewline ||
if split.isNL ||
(penaliseNewlinesInsideTokens && tok.leftHasNewline) =>
split.withPenalty(penalty)
case x => x
Expand All @@ -468,7 +466,7 @@ class FormatOps(val tree: Tree, baseStyle: ScalafmtConfig) {
nestedSelect(t.meta.leftOwner) + nestedApplies(t.meta.rightOwner) +
nonBoolPenalty
s.map {
case split if split.modification.isNewline =>
case split if split.isNL =>
split.withPenalty(penalty)
case x => x
}
Expand Down Expand Up @@ -660,7 +658,7 @@ class FormatOps(val tree: Tree, baseStyle: ScalafmtConfig) {
x => {
val indent = Indent(Num(2), x.tokens.last, ExpiresOn.After)
getInfixSplitsBeforeLhsOrRhs(app, ft, fullInfix).map { s =>
if (s.modification.isNewline) s.withIndent(indent) else s
if (s.isNL) s.withIndent(indent) else s
}
}
)
Expand Down Expand Up @@ -731,10 +729,7 @@ class FormatOps(val tree: Tree, baseStyle: ScalafmtConfig) {
if (isSingleLineComment(t.right)) // will break
s.map(_.switch(firstInfixOp))
else
s.map { x =>
if (!x.modification.isNewline) x
else x.switch(firstInfixOp)
}
s.map(x => if (x.isNL) x.switch(firstInfixOp) else x)
}

val singleLineExpire = if (isFirst) fullExpire else expires.head._1
Expand Down Expand Up @@ -799,7 +794,7 @@ class FormatOps(val tree: Tree, baseStyle: ScalafmtConfig) {
val exclude =
if (breakMany) Set.empty[Range]
else insideBlockRanges[LeftParenOrBrace](nextFT, expire)
Split(newStmtMod.getOrElse(Space), cost)
Split(ModExt(newStmtMod.getOrElse(Space)), cost)
.withSingleLine(expire, exclude)
}
}
Expand Down Expand Up @@ -1045,7 +1040,7 @@ class FormatOps(val tree: Tree, baseStyle: ScalafmtConfig) {
lastToken: Token,
indentLen: Int
)(implicit line: sourcecode.Line, style: ScalafmtConfig): Seq[Split] = {
val nlMod = NewlineT(acceptSpace = true)
val nlMod = NewlineT(alt = Some(Space))
val owners = chain.fold[Set[Tree]](Set(_), x => x.toSet)
val nlPolicy = ctorWithChain(owners, lastToken)
val nlOnelineTag = style.binPack.parentConstructors match {
Expand Down Expand Up @@ -1081,7 +1076,7 @@ class FormatOps(val tree: Tree, baseStyle: ScalafmtConfig) {
private def unapplyImpl(d: Decision): Option[Seq[Split]] = {
var replaced = false
def decisionPf(s: Split): Split =
if (!s.modification.isNewline) s
if (!s.isNL) s
else {
replaced = true
s.orElsePolicy(onBreakPolicy)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ class FormatWriter(formatOps: FormatOps) {
val prev = state.prev
val idx = prev.depth
val ft = toks(idx)
val breaks = state.split.modification.isNewline || ft.leftHasNewline
val breaks = state.split.isNL || ft.leftHasNewline
val newLineId = lineId + (if (breaks) 1 else 0)
result(idx) = FormatLocation(ft, state, styleMap.at(ft), newLineId)
iter(prev, newLineId)
Expand Down Expand Up @@ -134,12 +134,12 @@ class FormatWriter(formatOps: FormatOps) {
val inParentheses = loc.style.spaces.inParentheses
// remove space before "{"
val prevBegState =
if (0 == idx || (state.prev.split.modification ne Space))
if (0 == idx || (state.prev.split.modExt.mod ne Space))
state.prev
else {
val prevloc = locations(idx - 1)
val prevState = state.prev
.copy(split = state.prev.split.copy(modification = NoSplit))
val prevState =
state.prev.copy(split = state.prev.split.withMod(NoSplit))
locations(idx - 1) = prevloc.copy(
shift = prevloc.shift - 1,
state = prevState
Expand All @@ -156,7 +156,7 @@ class FormatWriter(formatOps: FormatOps) {
)
else {
// remove space after "{"
val split = state.split.copy(modification = NoSplit)
val split = state.split.withMod(NoSplit)
loc.copy(
replace = "(",
shift = loc.shift - 1,
Expand All @@ -170,7 +170,7 @@ class FormatWriter(formatOps: FormatOps) {
if (inParentheses) prevEndState
else {
// remove space before "}"
val split = prevEndState.split.copy(modification = NoSplit)
val split = prevEndState.split.withMod(NoSplit)
val newState = prevEndState.copy(split = split)
locations(end - 1) = prevEndLoc
.copy(shift = prevEndLoc.shift - 1, state = newState)
Expand Down Expand Up @@ -209,7 +209,7 @@ class FormatWriter(formatOps: FormatOps) {
@inline def tok = curr.formatToken
@inline def state = curr.state
@inline def prevState = curr.state.prev
@inline def lastModification = prevState.split.modification
@inline def lastModification = prevState.split.modExt.mod

def getWhitespace(alignOffset: Int): String = {
// TODO this could get slow for really long comment blocks. If that
Expand All @@ -220,23 +220,14 @@ class FormatWriter(formatOps: FormatOps) {
if (0 > nonCommentIdx) None else Some(locations(nonCommentIdx))
}

state.split.modification match {
state.split.modExt.mod match {
case Space =>
val previousAlign =
if (lastModification != NoSplit) 0
else getAlign(previous.formatToken)
val currentAlign = getAlign(tok, alignOffset)
getIndentation(1 + currentAlign + previousAlign)

case nl: NewlineT
if nl.acceptNoSplit && !tok.left.isInstanceOf[T.Comment] &&
state.indentation >= prevState.column =>
""

case nl: NewlineT
if nl.acceptSpace && state.indentation >= prevState.column =>
" "

case _: NewlineT
if tok.right.isInstanceOf[T.Comment] &&
nextNonComment.exists(
Expand Down Expand Up @@ -289,7 +280,7 @@ class FormatWriter(formatOps: FormatOps) {
val right = nextNonCommentTok.right
def isNewline =
Seq(curr, locations(math.min(i + skip, locations.length - 1)))
.exists(_.state.split.modification.isNewline)
.exists(_.state.split.isNL)

// Scala syntax allows commas before right braces in weird places,
// like constructor bodies:
Expand Down Expand Up @@ -356,8 +347,7 @@ class FormatWriter(formatOps: FormatOps) {
case _: T.Constant.String =>
TreeOps.getStripMarginChar(tok.meta.leftOwner).map { pipe =>
def isPipeFirstChar = text.find(_ != '"').contains(pipe)
val noAlign = !style.align.stripMargin ||
prevState.split.modification.isNewline
val noAlign = !style.align.stripMargin || prevState.split.isNL
val pipeOffset =
if (style.align.stripMargin && isPipeFirstChar) 1 else 0
val indent = pipeOffset +
Expand Down Expand Up @@ -449,7 +439,7 @@ class FormatWriter(formatOps: FormatOps) {
if (breakBefore) 0
else
prevState.prev.column - prevState.prev.indentation +
prevState.split.modification.length
prevState.split.length

protected final def canRewrite =
style.comments.wrap match {
Expand Down Expand Up @@ -808,7 +798,7 @@ class FormatWriter(formatOps: FormatOps) {
def processLine: FormatLocation = {
val floc = locations(idx)
idx += 1
val ok = !floc.state.split.modification.isNewline
val ok = !floc.state.split.isNL
if (!ok || floc.formatToken.leftHasNewline) columnShift = 0
columnShift += floc.shift
if (!ok) floc
Expand All @@ -828,7 +818,7 @@ class FormatWriter(formatOps: FormatOps) {
}

implicit val location = processLine
val doubleNewline = location.state.split.modification.newlines > 1
val doubleNewline = location.state.split.modExt.mod.newlines > 1
if (alignContainer eq null) {
getBlockToFlush(
getAlignContainer(location.formatToken.meta.rightOwner),
Expand Down Expand Up @@ -1010,15 +1000,15 @@ class FormatWriter(formatOps: FormatOps) {

private def shiftStateColumnIndent(startIdx: Int, offset: Int): Unit = {
// look for StateColumn; it returns indent=0 for withStateOffset(0)
val stateIndentOpt = locations(startIdx).state.split.indents
val stateIndentOpt = locations(startIdx).state.split.modExt.indents
.flatMap(_.withStateOffset(0).filter(_.length == 0))
stateIndentOpt.headOption.foreach { indent =>
@tailrec
def updateLocation(idx: Int): Unit = {
val floc = locations(idx)
if (indent.notExpiredBy(floc.formatToken)) {
val state = floc.state
if (state.split.modification.isNewline) {
if (state.split.isNL) {
locations(idx) = floc.copy(state =
state.copy(indentation = state.indentation + offset)
)
Expand Down Expand Up @@ -1078,7 +1068,7 @@ class FormatWriter(formatOps: FormatOps) {
if (minLines <= 0) true
else if (i >= toks.length || toks(i).formatToken.left == end) false
else {
val hasNL = toks(i).state.split.modification.isNewline
val hasNL = toks(i).state.split.isNL
isMultiline(end, i + 1, if (hasNL) minLines - 1 else minLines)
}
val formatToken = toks(i).formatToken
Expand Down Expand Up @@ -1238,9 +1228,9 @@ object FormatWriter {
alignHashKey: Int = 0,
replace: String = null
) {
def hasBreakAfter: Boolean = state.split.modification.isNewline
def hasBreakAfter: Boolean = state.split.isNL
def hasBreakBefore: Boolean =
state.prev.split.modification.isNewline || formatToken.left.start == 0
state.prev.split.isNL || formatToken.left.start == 0
def isStandalone: Boolean = hasBreakAfter && hasBreakBefore
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package org.scalafmt.internal

import scala.language.implicitConversions
import scala.meta.tokens.Token

/**
* @param mod Is this a space, no space, newline or 2 newlines?
* @param indents Does this add indentation?
*/
case class ModExt(
mod: Modification,
indents: Seq[Indent] = Seq.empty
) {
lazy val indentation = indents.mkString("[", ", ", "]")

def withIndent(length: => Length, expire: => Token, when: ExpiresOn): ModExt =
length match {
case Length.Num(0) => this
case x => withIndentImpl(Indent(x, expire, when))
}

def withIndentOpt(
length: => Length,
expire: Option[Token],
when: ExpiresOn
): ModExt =
expire.fold(this)(withIndent(length, _, when))

def withIndent(indent: => Indent): ModExt =
indent match {
case Indent.Empty => this
case x => withIndentImpl(x)
}

def withIndentOpt(indent: => Option[Indent]): ModExt =
indent.fold(this)(withIndent(_))

def withIndents(indents: Seq[Indent]): ModExt =
indents.foldLeft(this)(_ withIndent _)

private def withIndentImpl(indent: Indent): ModExt =
copy(indents = indent +: indents)

def switch(switchObject: AnyRef): ModExt = {
val newIndents = indents.map(_.switch(switchObject))
copy(indents = newIndents.filter(_ ne Indent.Empty))
}

}

object ModExt {

implicit def implicitModToModExt(mod: Modification): ModExt = ModExt(mod)

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@ case object NoSplit extends Modification {
* @param isDouble Insert a blank line?
* @param noIndent Should no indentation follow? For example in commented out
* code.
* @param acceptNoSplit Is it ok to replace this newline with a [[NoSplit]]
* if the newline will indent beyond the current column?
* For example, used by select chains in [[Router]].
* @param alt
* Is it ok to replace this newline with a [[NoSplit]] or [[Space]] (with
* an optional additional set of indents) if the newline will indent beyond
* the current column? For example, used by select chains in [[Router]].
*/
case class NewlineT(
isDouble: Boolean = false,
noIndent: Boolean = false,
acceptSpace: Boolean = false,
acceptNoSplit: Boolean = false
alt: Option[ModExt] = None
) extends Modification {
override def toString = {
val double = if (isDouble) "Double" else ""
Expand Down
Loading