Skip to content
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
1 change: 0 additions & 1 deletion compiler/src/dotty/tools/dotc/config/ScalaSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ trait AllScalaSettings extends CommonScalaSettings, PluginSettings, VerboseSetti

/* Decompiler settings */
val printTasty: Setting[Boolean] = BooleanSetting(RootSetting, "print-tasty", "Prints the raw tasty.", aliases = List("--print-tasty"))
val printLines: Setting[Boolean] = BooleanSetting(RootSetting, "print-lines", "Show source code line numbers.", aliases = List("--print-lines"))

/* Scala.js-related settings */
val scalajsGenStaticForwardersForNonTopLevelObjects: Setting[Boolean] = BooleanSetting(RootSetting, "scalajs-genStaticForwardersForNonTopLevelObjects", "Generate static forwarders even for non-top-level objects (Scala.js only).")
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Decorators.scala
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ object Decorators {
end extension

extension (text: Text)
def show(using Context): String = text.mkString(ctx.settings.pageWidth.value, ctx.settings.printLines.value)
def show(using Context): String = text.mkString(ctx.settings.pageWidth.value)

/** Test whether a list of strings representing phases contains
* a given phase. See [[config.CompilerCommand#explainAdvanced]] for the
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3579,7 +3579,7 @@ object TypeComparer {
}

object MatchReducer:
import printing.*, Texts.*
import printing.*, Texts.{*, given}
enum MatchResult extends Showable:
case Reduced(tp: Type)
case Disjoint
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import util.Spans.*
import dotty.tools.dotc.ast.{tpd, untpd}, ast.tpd.*
import ast.untpd.Modifiers
import backend.sjs.JSDefinitions
import printing.Texts.*
import printing.Texts.{*, given}
import printing.Printer
import io.AbstractFile
import util.common.*
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ class PlainPrinter(_ctx: Context) extends Printer {

protected def controlled(op: => Text): Text = limiter.controlled(op)

def Str(str: String, lineRange: LineRange = EmptyLineRange): Str =
def Str(str: String): Str =
limiter.register(str)
Texts.Str(str, lineRange)
Texts.Str(str)

given stringToText: Conversion[String, Text] = Str(_)

Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/printing/Printer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package dotc
package printing

import core.*
import Texts.*, ast.Trees.*
import Texts.{*, given}, ast.Trees.*
import Types.{Type, SingletonType, LambdaParam, LambdaType, NamedType, RefinedType},
Symbols.Symbol, Scopes.Scope, Constants.Constant,
Names.Name, Denotations._, Annotations.Annotation, Contexts.Context
Expand Down
28 changes: 7 additions & 21 deletions compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
private var enclosingDef: untpd.Tree = untpd.EmptyTree
private var myCtx: Context = super.printerContext
private var printPos = ctx.settings.YprintPos.value
private val printLines = ctx.settings.printLines.value

override def printerContext: Context = myCtx

Expand Down Expand Up @@ -481,7 +480,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
case id @ Ident(name) =>
val txt = tree.typeOpt match {
case tp: NamedType if name != nme.WILDCARD =>
toTextPrefixOf(tp) ~ withPos(selectionString(tp), tree.sourcePos)
toTextPrefixOf(tp) ~ selectionString(tp)
case _ =>
toText(name)
}
Expand Down Expand Up @@ -517,8 +516,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
typeApplyText(tree)
case Literal(c) =>
tree.typeOpt match {
case ConstantType(tc) => withPos(toText(tc), tree.sourcePos)
case _ => withPos(toText(c), tree.sourcePos)
case ConstantType(tc) => toText(tc)
case _ => toText(c)
}
case New(tpt) =>
keywordStr("new ") ~ {
Expand Down Expand Up @@ -729,8 +728,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
case SymbolLit(str) =>
"'" + str
case InterpolatedString(id, segments) =>
def strText(str: Literal) = withPos(escapedString(str.const.stringValue), tree.sourcePos)
def segmentText(segment: Tree) = segment match {
def strText(str: Literal) = escapedString(str.const.stringValue)
def segmentText(segment: Tree): Text = segment match {
case Thicket(List(str: Literal, expr)) => strText(str) ~ "{" ~ toTextGlobal(expr) ~ "}"
case str: Literal => strText(str)
}
Expand Down Expand Up @@ -968,14 +967,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
tree.hasType && tree.symbol.exists && ctx.settings.YprintSyms.value

protected def nameIdText[T <: Untyped](tree: NameTree[T]): Text =
if (tree.hasType && tree.symbol.exists && tree.symbol.isType == tree.name.isTypeName) {
val str = nameString(tree.symbol)
tree match {
case tree: RefTree => withPos(str, tree.sourcePos)
case tree: MemberDef => withPos(str, tree.sourcePos.withSpan(tree.nameSpan))
case _ => str
}
}
if tree.hasType && tree.symbol.exists && tree.symbol.isType == tree.name.isTypeName
then nameString(tree.symbol)
else toText(tree.name) ~ idText(tree)

private def toTextOwner(tree: Tree[?]) =
Expand Down Expand Up @@ -1280,11 +1273,4 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
}

override def plain: PlainPrinter = new PlainPrinter(_ctx)

private def withPos(txt: Text, pos: SourcePosition): Text =
if (!printLines || !pos.exists) txt
else txt match {
case Str(s, _) => Str(s, LineRange(pos.line, pos.endLine))
case _ => txt
}
}
144 changes: 51 additions & 93 deletions compiler/src/dotty/tools/dotc/printing/Texts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ object Texts {
@sharable
private val ansi = java.util.regex.Pattern.compile("\u001b\\[\\d+m")

sealed abstract class Text {
private val indentMargin: Int = 2

protected def indentMargin: Int = 2
sealed abstract class Text {

/** The sub-elements of this text in reverse order */
def relems: List[Text]

def isEmpty: Boolean = this match {
case Str(s, _) => s.isEmpty
def isEmpty: Boolean = this match
case Str(s) => s.isEmpty
case Fluid(relems) => relems forall (_.isEmpty)
case Vertical(relems) => relems.isEmpty
}

// Str Ver Clo Flu
// isVertical F T F F
Expand All @@ -29,10 +29,11 @@ object Texts {
def isFluid: Boolean = isInstanceOf[Fluid]
def isSplittable: Boolean = isFluid && !isClosed

def close: Text = if isSplittable then Closed(relems) else this
def close: Text =
if isSplittable then Closed(relems) else this

def remaining(width: Int): Int = this match {
case Str(s, _) =>
def remaining(width: Int): Int = this match
case Str(s) =>
width - lengthWithoutAnsi(s)
case Fluid(Nil) =>
width
Expand All @@ -41,113 +42,86 @@ object Texts {
if (r < 0) r else Fluid(prevs).remaining(r)
case Vertical(_) =>
-1
}

def lastLine: String = this match {
case Str(s, _) => s
def lastLine: String = this match
case Str(s) => s
case _ => relems.head.lastLine
}

def appendToLastLine(that: Text): Text = that match {
case Str(s2, lines1) =>
this match {
case Str(s1, lines2) => Str(s1 + s2, lines1 `union` lines2)
case Fluid(Str(s1, lines2) :: prev) => Fluid(Str(s1 + s2, lines1 `union` lines2) :: prev)
def appendToLastLine(that: Text): Text = that match
case Str(s2) =>
this match
case Str(s1) => Str(s1 + s2)
case Fluid(Str(s1) :: prev) => Fluid(Str(s1 + s2) :: prev)
case Fluid(relems) => Fluid(that :: relems)
case Vertical(_) => throw new IllegalArgumentException("Unexpected Vertical.appendToLastLine")
}
case Fluid(relems) =>
relems.reverse.foldLeft(this)(_.appendToLastLine(_))
case Vertical(_) => throw new IllegalArgumentException("Unexpected Text.appendToLastLine(Vertical(...))")
}

private def appendIndented(that: Text)(width: Int): Text =
Fluid(that.layout(width - indentMargin).indented :: this.relems)

private def append(width: Int)(that: Text): Text =
if (this.isEmpty) that.layout(width)
else if (that.isEmpty) this
else if (that.isVertical) appendIndented(that)(width)
else if (this.isVertical) Fluid(that.layout(width) :: this.relems)
else if (that.remaining(width - lengthWithoutAnsi(lastLine)) >= 0) appendToLastLine(that)
else if (that.isSplittable) that.relems.reverse.foldLeft(this)(_.append(width)(_))
if this.isEmpty then that.layout(width)
else if that.isEmpty then this
else if that.isVertical then appendIndented(that)(width)
else if this.isVertical then Fluid(that.layout(width) :: this.relems)
else if that.remaining(width - lengthWithoutAnsi(lastLine)) >= 0 then appendToLastLine(that)
else if that.isSplittable then that.relems.reverse.foldLeft(this)(_.append(width)(_))
else appendIndented(that)(width)

private def lengthWithoutAnsi(str: String): Int =
ansi.matcher(str).replaceAll("").length

def layout(width: Int): Text = this match {
case Str(s, _) =>
def layout(width: Int): Text = this match
case Str(s) =>
this
case Fluid(relems) =>
relems.reverse.foldLeft(Str(""): Text)(_.append(width)(_))
case Vertical(relems) =>
Vertical(relems.map(_.layout(width)))
}

def map(f: String => String): Text = this match {
case Str(s, lines) => Str(f(s), lines)
def map(f: String => String): Text = this match
case Str(s) => Str(f(s))
case Fluid(relems) => Fluid(relems.map(_.map(f)))
case Vertical(relems) => Vertical(relems.map(_.map(f)))
}

def stripPrefix(pre: String): Text = this match {
case Str(s, _) =>
if (s.startsWith(pre)) s.drop(pre.length) else s
def stripPrefix(pre: String): Text = this match
case Str(s) =>
if s.startsWith(pre) then s.drop(pre.length) else s
case Fluid(relems) =>
val elems = relems.reverse
val head = elems.head.stripPrefix(pre)
if (head eq elems.head) this else Fluid((head :: elems.tail).reverse)
if head eq elems.head then this else Fluid((head :: elems.tail).reverse)
case Vertical(relems) =>
val elems = relems.reverse
val head = elems.head.stripPrefix(pre)
if (head eq elems.head) this else Vertical((head :: elems.tail).reverse)
}

private def indented: Text = this match {
case Str(s, lines) => Str((" " * indentMargin) + s, lines)
private def indented: Text = this match
case Str(s) => Str((" " * indentMargin) + s)
case Fluid(relems) => Fluid(relems map (_.indented))
case Vertical(relems) => Vertical(relems map (_.indented))
}

def print(sb: StringBuilder, numberWidth: Int): Unit = this match {
case Str(s, lines) =>
if (numberWidth != 0) {
val ln = lines.show
if (ln.nonEmpty) {
val pad = (numberWidth - ln.length - 1)
assert(pad >= 0)
sb.append(" " * pad)
sb.append(ln)
sb.append("|")
}
}

def print(sb: StringBuilder): Unit = this match
case Str(s) =>
sb.append(s.replaceAll("[ ]+$", ""))
case _ =>
var follow = false
for (elem <- relems.reverse) {
if (follow) sb.append(System.lineSeparator)
elem.print(sb, numberWidth)
for elem <- relems.reverse do
if follow then sb.append(System.lineSeparator)
elem.print(sb)
follow = true
}
}

def maxLine: Int = this match {
case Str(_, lines) => lines.end
case _ => relems.foldLeft(-1)((acc, relem) => acc max relem.maxLine)
}

def mkString(width: Int = Int.MaxValue, withLineNumbers: Boolean = false): String = {
def mkString(width: Int = Int.MaxValue): String =
val sb = new StringBuilder
// width display can be upto a range "n-n" where 1 <= n <= maxLine+1
val numberWidth = if (withLineNumbers) (2 * (maxLine + 1).toString.length) + 2 else 0
layout(width - numberWidth).print(sb, numberWidth)
layout(width).print(sb)
sb.toString
}

def ~ (that: Text): Text =
if (this.isEmpty) that
else if (that.isEmpty) this
if this.isEmpty then that
else if that.isEmpty then this
else this match
case Fluid(relems1) if !isClosed => that match
case Fluid(relems2) if !that.isClosed => Fluid(relems2 ++ relems1)
Expand All @@ -157,12 +131,12 @@ object Texts {
case _ => Fluid(that :: this :: Nil)

def ~~ (that: Text): Text =
if (this.isEmpty) that
else if (that.isEmpty) this
if this.isEmpty then that
else if that.isEmpty then this
else Fluid(that :: Str(" ") :: this :: Nil)

def over (that: Text): Vertical =
if (this.isVertical) Vertical(that :: this.relems)
if this.isVertical then Vertical(that :: this.relems)
else Vertical(that :: this :: Nil)
}

Expand All @@ -175,44 +149,28 @@ object Texts {
* separator strings `sep`.
*/
def apply(xs: Iterable[Text], sep: String = " "): Text =
if (sep == "\n") lines(xs)
else {
if sep == "\n" then lines(xs)
else
val ys = xs.filterNot(_.isEmpty)
if (ys.isEmpty) Str("")
if ys.isEmpty then Str("")
else ys.reduceRight((a, b) => (a ~ sep).close ~ b)
}

/** The given texts `xs`, each on a separate line */
def lines(xs: Iterable[Text]): Vertical = Vertical(xs.toList.reverse)

extension (text: => Text)
def provided(cond: Boolean): Text = if (cond) text else Str("")

}

case class Str(s: String, lineRange: LineRange = EmptyLineRange) extends Text {
case class Str(s: String) extends Text:
override def relems: List[Text] = List(this)
override def toString = this match
case Str(s, EmptyLineRange) => s"Str($s)"
case Str(s, lineRange) => s"Str($s, $lineRange)"
}
override def toString = s"Str($s)"

case class Vertical(relems: List[Text]) extends Text
case class Fluid(relems: List[Text]) extends Text

class Closed(relems: List[Text]) extends Fluid(relems):
override def productPrefix = "Closed"

implicit def stringToText(s: String): Text = Str(s)

/** Inclusive line range */
case class LineRange(start: Int, end: Int) {
def union(that: LineRange): LineRange = LineRange(start min that.start, end max that.end)
def show: String =
if (start == end) (start + 1).toString
else if (start < end) s"${start + 1}-${end + 1}"
else "" // empty range
}

object EmptyLineRange extends LineRange(Int.MaxValue, Int.MinValue)
given Conversion[String, Text] = Str(_)
}
4 changes: 2 additions & 2 deletions compiler/test/dotty/tools/dotc/CompilationTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -274,9 +274,9 @@ class CompilationTests {

// Set -sourceroot such that the source code cannot be found by the compiler
val libOptions = tastSourceOptions.and("-sourceroot", "tests/init-global/special")
val lib = compileFile("tests/init-global/special/tastySource/A.scala", libOptions)(group).keepOutput.checkCompile()
val lib = compileFile("tests/init-global/special/tastySource/A.scala", libOptions)(using group).keepOutput.checkCompile()

compileFile("tests/init-global/special/tastySource/B.scala", tastSourceOptions.withClasspath(outDirLib))(group).checkWarnings()
compileFile("tests/init-global/special/tastySource/B.scala", tastSourceOptions.withClasspath(outDirLib))(using group).checkWarnings()

lib.delete()
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/test/dotty/tools/dotc/TupleShowTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class TupleShowTests extends DottyTest:
))
chkEq(exp, obt)

@Test def tup3_show10 = chkEq("(Int,\n Long,\n Short)".normEOL, tup3.toText(ctx.printer).mkString(10, false))
@Test def tup3_show10 = chkEq("(Int,\n Long,\n Short)".normEOL, tup3.toText(ctx.printer).mkString(10))

val res21: String =
"""|(Int, Int, Int, Int, Int, Long, Long, Long, Long, Long, Int, Int, Int, Int,
Expand Down
Loading