diff options
Diffstat (limited to 'src/repl/scala/tools/nsc/interpreter/Tabulators.scala')
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/Tabulators.scala | 112 |
1 files changed, 112 insertions, 0 deletions
diff --git a/src/repl/scala/tools/nsc/interpreter/Tabulators.scala b/src/repl/scala/tools/nsc/interpreter/Tabulators.scala new file mode 100644 index 0000000000..75bec168eb --- /dev/null +++ b/src/repl/scala/tools/nsc/interpreter/Tabulators.scala @@ -0,0 +1,112 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.tools.nsc.interpreter + +trait Tabulator { + def isAcross: Boolean + def width: Int + def marginSize: Int + + protected def fits(items: Seq[String], width: Int): Boolean = ( + (items map (_.length)).sum + (items.length - 1) * marginSize < width + ) + def tabulate(items: Seq[String]): Seq[Seq[String]] = ( + if (fits(items, width)) Seq(Seq(items mkString " " * marginSize)) + else printMultiLineColumns(items) + ) + protected def columnize(ss: Seq[String]): Seq[Seq[String]] = ss map (s => Seq(s)) + protected def printMultiLineColumns(items: Seq[String]): Seq[Seq[String]] = { + import scala.tools.nsc.interpreter.SimpleMath._ + val longest = (items map (_.length)).max + val columnWidth = longest + marginSize + val maxcols = ( + if (columnWidth >= width) 1 + else 1 max (width / columnWidth) // make sure it doesn't divide to 0 + ) + val nrows = items.size /% maxcols + val ncols = items.size /% nrows + val groupSize = ncols + val padded = items map (s"%-${columnWidth}s" format _) + val xwise = isAcross || ncols >= items.length + val grouped: Seq[Seq[String]] = + if (groupSize == 1) columnize(items) + else if (xwise) (padded grouped groupSize).toSeq + else { + val h = 1 max padded.size /% groupSize + val cols = (padded grouped h).toList + for (i <- 0 until h) yield + for (j <- 0 until groupSize) yield + if (i < cols(j).size) cols(j)(i) else "" + } + grouped + } +} + +/** Adjust the column width and number of columns to minimize the row count. */ +trait VariColumnTabulator extends Tabulator { + override protected def printMultiLineColumns(items: Seq[String]): Seq[Seq[String]] = { + import scala.tools.nsc.interpreter.SimpleMath._ + val longest = (items map (_.length)).max + val shortest = (items map (_.length)).min + val fattest = longest + marginSize + val skinny = shortest + marginSize + + // given ncols, calculate nrows and a list of column widths, or none if not possible + // if ncols > items.size, then columnWidths.size == items.size + def layout(ncols: Int): Option[(Int, Seq[Int], Seq[Seq[String]])] = { + val nrows = items.size /% ncols + val xwise = isAcross || ncols >= items.length + // max width item in each column + def maxima(rows: Seq[Seq[String]]) = + (0 until (ncols min items.size)) map { col => + val widths = for (r <- rows if r.size > col) yield r(col).length + widths.max + } + def resulting(rows: Seq[Seq[String]]) = { + val columnWidths = maxima(rows) map (_ + marginSize) + val linelen = columnWidths.sum + if (linelen <= width) Some((nrows, columnWidths, rows)) + else None + } + if (ncols == 1) resulting(columnize(items)) + else if (xwise) resulting((items grouped ncols).toSeq) + else { + val cols = (items grouped nrows).toList + val rows = + for (i <- 0 until nrows) yield + for (j <- 0 until ncols) yield + if (j < cols.size && i < cols(j).size) cols(j)(i) else "" + resulting(rows) + } + } + + if (fattest >= width) { + columnize(items) + } else { + // if every col is widest, we have at least this many cols + val mincols = 1 max (width / fattest) + // if every other col is skinniest, we have at most this many cols + val maxcols = 1 + ((width - fattest) / skinny) + val possibles = (mincols to maxcols).map(n => layout(n)).flatten + val minrows = (possibles map (_._1)).min + + // select the min ncols that results in minrows + val (_, columnWidths, sss) = (possibles find (_._1 == minrows)).get + + // format to column width + sss map (ss => ss.zipWithIndex map { + case (s, i) => s"%-${columnWidths(i)}s" format s + }) + } + } +} + +private[interpreter] object SimpleMath { + implicit class DivRem(private val i: Int) extends AnyVal { + /** i/n + if (i % n != 0) 1 else 0 */ + def /%(n: Int): Int = (i + n - 1) / n + } +} |