diff --git a/CHANGELOG b/CHANGELOG index 3913a1ff..805f6ca8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,8 +1,16 @@ +0.1.5 (..) +* FIX: Type formatter crash on multiline types +* Track scalac parser -- allow trait-position parents to have parameter blocks +* Sync changes to InferredSemicolonParser + +0.1.4 (24/April/13) +* FIX: Allow declarations as last statement in case block (issue #60) +* Update to build 2.10 final + 0.1.3 (3/October/12) * Add EOF to ComplilationUnit, ensuring entire source is represented in the tree * Support $this references in String interpolation * Update build to sbt 0.12.1 -* FIX: Allow declarations as last statement in case block (issue #60). 0.1.2 (7/May/12) diff --git a/README.rst b/README.rst old mode 100644 new mode 100755 index 5fbc3945..0246c4cd --- a/README.rst +++ b/README.rst @@ -28,6 +28,31 @@ You can also configure formatting to be run as a save action (Window -> Preferen To set preferences, go to Window -> Preferences -> Scala -> Formatter +Integration with IntelliJ +------------------------- + +IntelliJ already has a built-in Scala code formatter (C-L). +I use the original settings plus `Wraping and Braces` -> `Align columns in case branches`. +In order to achieve exactly the same formatting use the following options:: + + + def formattingPreferences = { + import scalariform.formatter.preferences._ + FormattingPreferences() + .setPreference(AlignParameters, true) + .setPreference(AlignSingleLineCaseStatements, true) + .setPreference(AlignSingleLineCaseStatements.AlignMultiLineCaseStatements, true) + .setPreference(AlignSingleLineCaseStatements.MaxArrowIndent, 120) + .setPreference(AlignSingleLineCaseStatements.GroupByNewLine, true) // IntelliJ compatible + .setPreference(CompactControlReadability, false) + .setPreference(NoSpacesAroundMultiImports, true) + .setPreference(FormatXml, false) + .setPreference(PreserveSpaceBeforeArguments, true) + .setPreference(IndentWithTabs, false) + .setPreference(SpacesWithinPatternBinders, false) // IntelliJ compatible + } + + Integration with Emacs/ENSIME ----------------------------- @@ -59,7 +84,7 @@ Usage:: org.scalariform scalariform-maven-plugin - 0.1.3 + 0.1.4 process-sources @@ -103,7 +128,7 @@ the following to ``.vimrc`` :: The executable scalariform.jar can be downloaded from: - https://github.com/downloads/mdr/scalariform/scalariform.jar + https://s3.amazonaws.com/scalariform/scalariform.jar Command line tool ----------------- @@ -474,6 +499,22 @@ If ``false``,:: case elem@Multi(values@_*) => +chainedPackageClauses +~~~~~~~~~~~~~~~~~~~~~ + +Default: ``false`` + +Example:: +``` + package com.company.analytics.math.curves.interpolators +``` + +Will be reformatted to:: +``` + package com.company.analytics.math + package curves + package interpolators +``` Scala Style Guide ~~~~~~~~~~~~~~~~~ diff --git a/cli/.classpath b/cli/.classpath index 9b3704ea..e92a111b 100644 --- a/cli/.classpath +++ b/cli/.classpath @@ -1,12 +1,11 @@ - - - - - - - - - - - + + + + + + + + + + \ No newline at end of file diff --git a/cli/src/main/scala/scalariform/commandline/Main.scala b/cli/src/main/scala/scalariform/commandline/Main.scala index a549d067..3cd343b6 100644 --- a/cli/src/main/scala/scalariform/commandline/Main.scala +++ b/cli/src/main/scala/scalariform/commandline/Main.scala @@ -254,26 +254,11 @@ object Main { private def transformFilesInPlace(files: Seq[File], encoding: String, doFormat: String ⇒ Option[String], log: String ⇒ Unit): Boolean = { var problems = false - // val futures = - // for (file ← files) yield asyncExec { - // problems &= transformFileInPlace(file, encoding, doFormat, log) - // } - // futures.map(_.get()) for (file ← files) problems &= transformFileInPlace(file, encoding, doFormat, log) problems } - // private val lock = new ReentrantLock - // private lazy val executorService = Executors.newCachedThreadPool() - // private def asyncExec(x: ⇒ Unit): Future[_] = executorService.submit(new Runnable { def run() = x }) - // private def withLock[T](x: ⇒ T): T = { - // lock.lock() - // try - // x - // finally - // lock.unlock() - // } /** * @return true iff file is already formatted correctly */ diff --git a/docs/source/conf.py b/docs/source/conf.py index 63da561a..c08bd721 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -45,9 +45,9 @@ # built documents. # # The short X.Y version. -version = '0.1.4-SNAPSHOT' +version = '0.1.5-SNAPSHOT' # The full version, including alpha/beta/rc tags. -release = '0.1.4-SNAPSHOT' +release = '0.1.5-SNAPSHOT' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/misc/.classpath b/misc/.classpath index 3f447726..c89ac363 100644 --- a/misc/.classpath +++ b/misc/.classpath @@ -1,14 +1,13 @@ - - - - - - - - - - - - - + + + + + + + + + + + + \ No newline at end of file diff --git a/misc/src/main/scala/scalariform/corpusscan/CorpusScanner.scala b/misc/src/main/scala/scalariform/corpusscan/CorpusScanner.scala index 2d127a92..143b3deb 100644 --- a/misc/src/main/scala/scalariform/corpusscan/CorpusScanner.scala +++ b/misc/src/main/scala/scalariform/corpusscan/CorpusScanner.scala @@ -1,16 +1,17 @@ package scalariform.corpusscan -import scalariform.lexer.{ Token ⇒ _, _ } -import scalariform.formatter._ -import scalariform.formatter.preferences._ -import scalariform.parser._ -import scala.util.parsing.input._ -import scala.util.parsing.combinator._ import java.io.File + import scala.io.Source -import scalariform.utils.Utils.writeText + import org.apache.commons.io.FileUtils + import scalariform.commandline.ScalaFileWalker +import scalariform.formatter._ +import scalariform.formatter.preferences.FormattingPreferences +import scalariform.lexer._ +import scalariform.parser._ +import scalariform.utils.Utils.writeText sealed trait ParseFault case object TokensDoNotCoverSource extends ParseFault @@ -31,9 +32,9 @@ object CorpusScanner extends SpecificFormatter { val tokens = ScalaLexer.tokenise(source) try { val result = new ScalaParser(tokens.toArray).compilationUnitOrScript() - if (result.tokens != tokens.init) /* drop EOF */ + if (result.tokens != tokens) { Some(BadAstTokens) - else + } else None } catch { case e: ScalaParserException ⇒ Some(UnsuccessfulParse) @@ -66,9 +67,9 @@ object CorpusScanner extends SpecificFormatter { } -object Runner { +object Runner extends App { - val corpusDir = "/home/matt/coding/scala-corpus" + val corpusDir = "/home/matthew/coding/scala-corpus/repos2" // val corpusDir = "/home/matt/scala-corpus" def checkParser() { @@ -93,14 +94,14 @@ object Runner { for (file ← ScalaFileWalker.findScalaFiles(corpusDir)) { print("Formatting: " + file) CorpusScanner.formatFile(file) - val parsed = CorpusScanner.attemptToParse(file) - require(parsed == None, parsed.toString) + val parseFaultOpt = CorpusScanner.attemptToParse(file) + require(parseFaultOpt == None, parseFaultOpt.toString) println() count += 1 } println(count + " files formatted.") } - def main(args: Array[String]) = formatInPlace() + formatInPlace() } diff --git a/misc/src/main/scala/scalariform/gui/FormatterFrame.scala b/misc/src/main/scala/scalariform/gui/FormatterFrame.scala index edafd1be..f12d1c27 100644 --- a/misc/src/main/scala/scalariform/gui/FormatterFrame.scala +++ b/misc/src/main/scala/scalariform/gui/FormatterFrame.scala @@ -21,6 +21,10 @@ import scalariform.formatter._ import scalariform.formatter.preferences._ import scalariform.lexer._ import scalariform.lexer.Tokens._ +import scalariform.gui.SwingUtils._ +import scalariform.parser._ +import scala.util.parsing.input._ +import scala.util.parsing.combinator._ class FormatterFrame extends JFrame with SpecificFormatter { @@ -101,10 +105,9 @@ class FormatterFrame extends JFrame with SpecificFormatter { val textFont = new Font("monospaced", Font.PLAIN, 14) val astTree = new JTree - val tokensTable = new JTable - tokensTable.setCellSelectionEnabled(false) - tokensTable.setRowSelectionAllowed(true) - tokensTable.setColumnSelectionAllowed(false) + + val tokensTable = new TokenTable + val rawTokensTable = new TokenTable val outputTextPane = new JTextPane setFont(outputTextPane, textFont) @@ -155,8 +158,10 @@ class FormatterFrame extends JFrame with SpecificFormatter { case e: RuntimeException ⇒ if (showAstCheckBox.isSelected) { val tokens = ScalaLexer.tokenise(inputText, scalaVersion = SCALA_VERSION) + val rawTokens = ScalaLexer.rawTokenise(inputText, scalaVersion = SCALA_VERSION) val tableModel = new TokenTableModel(tokens, FormatResult(Map(), Map(), Map())) - tokensTable.setModel(tableModel) + tokensTable.setTokens(tokens) + rawTokensTable.setTokens(rawTokens) try { val parseResult = specificFormatter.parse(new ScalaParser(tokens.toArray)) val treeModel = new ParseTreeModel(parseResult) @@ -168,21 +173,19 @@ class FormatterFrame extends JFrame with SpecificFormatter { } val duration = System.currentTimeMillis - startTime val tokens = ScalaLexer.tokenise(inputText, scalaVersion = SCALA_VERSION) + val rawTokens = ScalaLexer.rawTokenise(inputText, scalaVersion = SCALA_VERSION) val tokenCount = tokens.size setTitle("Scalariform " + scalariform.BuildInfo.version + " -- " + duration + "ms, " + tokenCount + " tokens, speed = " + (1000 * tokenCount / (duration + 1)) + " tokens/second") outputTextPane.setText(outputText) if (showAstCheckBox.isSelected) { - import scalariform.parser._ - import scala.util.parsing.input._ - import scala.util.parsing.combinator._ val parseResult = try specificFormatter.parse(new ScalaParser(tokens.toArray)) catch { case e: RuntimeException ⇒ - val tableModel = new TokenTableModel(tokens, FormatResult(Map(), Map(), Map())) - tokensTable.setModel(tableModel) + tokensTable.setTokens(tokens) + rawTokensTable.setTokens(rawTokens) throw e } val treeModel = new ParseTreeModel(parseResult) @@ -191,12 +194,12 @@ class FormatterFrame extends JFrame with SpecificFormatter { val (outputText, formatResult) = specificFormatter.fullFormat(inputText, scalaVersion = SCALA_VERSION)(OptionsPanel.getFormattingPreferences) - val tableModel = new TokenTableModel(tokens, formatResult) - tokensTable.setModel(tableModel) + tokensTable.setTokens(tokens, formatResult) + rawTokensTable.setTokens(rawTokens) } } catch { - case e ⇒ - outputTextPane.setText(e.toString + "\n" + e.getStackTrace.mkString("\n")) + case t: Throwable ⇒ + outputTextPane.setText(t.toString + "\n" + t.getStackTrace.mkString("\n")) outputTextPane.setCaretPosition(0) } @@ -211,6 +214,7 @@ class FormatterFrame extends JFrame with SpecificFormatter { resultTabbedPane.addTab("Output", new JScrollPane(outputTextPane)) resultTabbedPane.addTab("AST", new JScrollPane(astTree)) resultTabbedPane.addTab("Tokens", new JScrollPane(tokensTable)) + resultTabbedPane.addTab("Raw Tokens", new JScrollPane(rawTokensTable)) val splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT) splitPane.setTopComponent(new JScrollPane(inputTextPane)) @@ -304,26 +308,36 @@ class FormatterFrame extends JFrame with SpecificFormatter { val parseTreeModel = astTree.getModel.asInstanceOf[ParseTreeModel] for { lastComponent ← Option(astTree.getLastSelectedPathComponent) - (from, to) ← parseTreeModel.getDocumentRange(lastComponent) + range ← parseTreeModel.getDocumentRange(lastComponent) } { - inputTextPane.setSelectionStart(from) - inputTextPane.setSelectionEnd(to + 1) + inputTextPane.setSelectionStart(range.offset) + inputTextPane.setSelectionEnd(range.offset + range.length) + inputTextPane.requestFocusInWindow() + astTree.requestFocusInWindow() } } }) - tokensTable.getSelectionModel.addListSelectionListener(new ListSelectionListener() { - def valueChanged(e: ListSelectionEvent) { - val tableModel = tokensTable.getModel.asInstanceOf[TokenTableModel] - val selectionIndex = e.getFirstIndex - if (selectionIndex >= 0) { - val (from, to) = tableModel.getDocumentRange(selectionIndex) - inputTextPane.setSelectionStart(from) - inputTextPane.setSelectionEnd(to + 1) - } + tokensTable.getSelectionModel.addListSelectionListener { e: ListSelectionEvent ⇒ + for (token ← tokensTable.getSelectedToken) { + val range = token.range + inputTextPane.setSelectionStart(range.offset) + inputTextPane.setSelectionEnd(range.offset + range.length) + inputTextPane.requestFocusInWindow() + tokensTable.requestFocusInWindow() } - }) + } + + rawTokensTable.getSelectionModel.addListSelectionListener { e: ListSelectionEvent ⇒ + for (token ← rawTokensTable.getSelectedToken) { + val range = token.range + inputTextPane.setSelectionStart(range.offset) + inputTextPane.setSelectionEnd(range.offset + range.length) + inputTextPane.requestFocusInWindow() + rawTokensTable.requestFocusInWindow() + } + } { val menuBar = new JMenuBar @@ -350,13 +364,13 @@ class FormatterFrame extends JFrame with SpecificFormatter { } def specificFormatter: SpecificFormatter = - productionComboBox.getSelectedItem.asInstanceOf[ProductionComboBoxModel.ProductionItem].formatter + productionComboBox.getSelectedItem.asInstanceOf[ProductionItem].formatter - object ProductionComboBoxModel extends DefaultComboBoxModel { + class ProductionItem(name: String, val formatter: SpecificFormatter) { + override def toString = name + } - class ProductionItem(name: String, val formatter: SpecificFormatter) { - override def toString = name - } + object ProductionComboBoxModel extends DefaultComboBoxModel[ProductionItem] { val compilationUnitFormatter = new SpecificFormatter { @@ -412,31 +426,4 @@ object Samples { |}""".stripMargin } // format: ON -class TokenTableModel(tokens: List[Token], formatResult: FormatResult) extends AbstractTableModel { - def getColumnCount = 5 - def getRowCount = tokens.size - def getValueAt(row: Int, col: Int): AnyRef = { - val token = tokens(row) - col match { - case 0 ⇒ token.tokenType - case 1 ⇒ token.text - case 2 ⇒ token.offset.asInstanceOf[java.lang.Integer] - case 3 ⇒ token.lastCharacterOffset.asInstanceOf[java.lang.Integer] - case 4 ⇒ { - formatResult.predecessorFormatting.get(token) orElse formatResult.inferredNewlineFormatting.get(token) getOrElse "" - } - } - } - override def getColumnName(col: Int) = col match { - case 0 ⇒ "Type" - case 1 ⇒ "Token text" - case 2 ⇒ "Start" - case 3 ⇒ "Finish" - case 4 ⇒ "Instruction" - } - def getDocumentRange(index: Int): (Int, Int) = { - val token = tokens(index) - (token.offset, token.lastCharacterOffset) - } -} diff --git a/misc/src/main/scala/scalariform/gui/Main.scala b/misc/src/main/scala/scalariform/gui/Main.scala index 7f6d360f..35599e9c 100644 --- a/misc/src/main/scala/scalariform/gui/Main.scala +++ b/misc/src/main/scala/scalariform/gui/Main.scala @@ -3,15 +3,13 @@ package scalariform.gui import scalariform.utils.Utils._ import javax.swing.JFrame -object Main { +object Main extends App { - def main(args: Array[String]) { - onSwingThread { - val frame = new FormatterFrame - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) - frame.setSize(1280, 600) - frame.setVisible(true) - } + onSwingThread { + val frame = new FormatterFrame + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) + frame.setSize(1280, 600) + frame.setVisible(true) } } diff --git a/misc/src/main/scala/scalariform/gui/ParseTreeModel.scala b/misc/src/main/scala/scalariform/gui/ParseTreeModel.scala index 224f1fc9..edc26d7d 100644 --- a/misc/src/main/scala/scalariform/gui/ParseTreeModel.scala +++ b/misc/src/main/scala/scalariform/gui/ParseTreeModel.scala @@ -1,85 +1,90 @@ package scalariform.gui -import java.awt.event._ -import net.miginfocom.layout._ -import net.miginfocom.swing._ -import javax.swing._ -import javax.swing.text._ -import java.awt.event._ -import java.awt.{ List ⇒ _, _ } - -import javax.swing._ -import javax.swing.event._ -import javax.swing.text._ +import javax.swing.event.TreeModelListener import javax.swing.tree._ - -import javax.swing.{ JMenu, JMenuItem, JMenuBar } - -import scalariform.utils.Utils._ -import scalariform.parser._ -import scalariform.formatter._ -import scalariform.lexer._ -import scalariform.lexer.Tokens._ +import scalariform.lexer.Token +import scalariform.parser.AstNode +import scalariform.utils.Range class ParseTreeModel(rootAstNode: AstNode) extends TreeModel { - def getDocumentRange(obj: AnyRef): Option[(Int, Int)] = obj.asInstanceOf[TreeNode].range + def getDocumentRange(obj: AnyRef): Option[Range] = obj.asInstanceOf[TreeNode].range abstract sealed class TreeNode(name: String) { + def children: List[TreeNode] - override def toString = name - lazy val range: Option[(Int, Int)] = { - val childRanges = children flatMap { _.range } - for { - firstRange ← childRanges.headOption - lastRange ← childRanges.lastOption - } yield (firstRange._1, lastRange._2) + + lazy val range: Option[Range] = { + val childRanges = children.flatMap(_.range) + if (childRanges.isEmpty) + None + else + Some(childRanges.reduceLeft(_ mergeWith _)) } + override def toString = name + } case class AstNodeNode(name: String, astNode: AstNode) extends TreeNode(name) { + val fields = astNode.getFields + lazy val children = fields flatMap { - case (_, None) | (_, Nil) ⇒ None + case (_, None) | (_, Nil) ⇒ None case (fieldName, value) ⇒ Some(makeTreeNode(value.asInstanceOf[AnyRef], fieldName)) } + override def toString = { val typeName = astNode.getClass.getSimpleName (if (name != "") name + ": " else "") + typeName } + } case class TokenNode(name: String, token: Token) extends TreeNode(name) { + val children = Nil + + override lazy val range = Some(token.range) + override def toString = name + ": " + token - override lazy val range = Some(token.offset, token.lastCharacterOffset) } case class ListNode(name: String, list: List[Any]) extends TreeNode(name) { - lazy val children = list map { x ⇒ makeTreeNode(x) } + + lazy val children = list.zipWithIndex map { case (x, i) ⇒ makeTreeNode(x, i.toString) } + } case class OptionNode(name: String, opt: Option[Any]) extends TreeNode(name) { - lazy val children = opt map { x ⇒ makeTreeNode(x) } toList + + lazy val children = opt map { x ⇒ makeTreeNode(x, "Some") } toList + } case class EitherNode(name: String, either: Either[Any, Any]) extends TreeNode(name) { + lazy val children = either match { - case Left(obj) ⇒ List(makeTreeNode(obj)) - case Right(obj) ⇒ List(makeTreeNode(obj)) + case Left(obj) ⇒ List(makeTreeNode(obj, "Left")) + case Right(obj) ⇒ List(makeTreeNode(obj, "Right")) } } case class PairNode(name: String, pair: (Any, Any)) extends TreeNode(name) { - lazy val children = List(makeTreeNode(pair._1), makeTreeNode(pair._2)) + + lazy val children = List(makeTreeNode(pair._1, "_1"), makeTreeNode(pair._2, "_2")) + } case class TodoNode(name: String, obj: Any) extends TreeNode(name) { + val children = Nil + override def toString = name + ": " + obj + } def makeTreeNode(obj: Any, name: String = ""): TreeNode = obj match { @@ -89,8 +94,10 @@ class ParseTreeModel(rootAstNode: AstNode) extends TreeModel { TokenNode(name, token) case list: List[_] ⇒ ListNode(name, list) - case option: Option[_] ⇒ - OptionNode(name, option) + case Some(x) ⇒ + makeTreeNode(x, name) + case None ⇒ + OptionNode(name, None) case either: Either[_, _] ⇒ EitherNode(name, either) case pair: (_, _) ⇒ @@ -103,8 +110,6 @@ class ParseTreeModel(rootAstNode: AstNode) extends TreeModel { def getChildCount(obj: AnyRef) = getChildren(obj).length - private def getChildren(obj: AnyRef) = obj.asInstanceOf[TreeNode].children - def getChild(parent: AnyRef, index: Int): AnyRef = getChildren(parent)(index) def getIndexOfChild(parent: AnyRef, child: AnyRef) = getChildren(parent).indexOf(child) @@ -112,8 +117,12 @@ class ParseTreeModel(rootAstNode: AstNode) extends TreeModel { def isLeaf(obj: AnyRef): Boolean = getChildCount(obj) == 0 def addTreeModelListener(l: TreeModelListener) {} + def removeTreeModelListener(l: TreeModelListener) {} + def valueForPathChanged(path: TreePath, newValue: Any) {} + private def getChildren(obj: AnyRef) = obj.asInstanceOf[TreeNode].children + } diff --git a/misc/src/main/scala/scalariform/gui/SwingUtils.scala b/misc/src/main/scala/scalariform/gui/SwingUtils.scala new file mode 100644 index 00000000..7b5180d2 --- /dev/null +++ b/misc/src/main/scala/scalariform/gui/SwingUtils.scala @@ -0,0 +1,13 @@ +package scalariform.gui + +import javax.swing.event.ListSelectionListener +import javax.swing.event.ListSelectionEvent + +object SwingUtils { + + implicit def fn2ListSelectionListener(handler: ListSelectionEvent ⇒ Unit): ListSelectionListener = new ListSelectionListener() { + def valueChanged(e: ListSelectionEvent) = handler(e) + } + + +} \ No newline at end of file diff --git a/misc/src/main/scala/scalariform/gui/TokenTable.scala b/misc/src/main/scala/scalariform/gui/TokenTable.scala new file mode 100644 index 00000000..c71ef964 --- /dev/null +++ b/misc/src/main/scala/scalariform/gui/TokenTable.scala @@ -0,0 +1,55 @@ +package scalariform.gui + +import scalariform.formatter.FormatResult +import javax.swing.JTable +import scalariform.lexer.Token +import javax.swing.table.AbstractTableModel +import scalariform.utils.Range + +class TokenTable extends JTable(new TokenTableModel(Nil, FormatResult.EMPTY)) { + + setCellSelectionEnabled(false) + setRowSelectionAllowed(true) + setColumnSelectionAllowed(false) + + def setTokens(tokens: List[Token], formatResult: FormatResult = FormatResult.EMPTY) { + val tableModel = new TokenTableModel(tokens, formatResult) + setModel(tableModel) + } + + override def getModel = super.getModel.asInstanceOf[TokenTableModel] + + def getSelectedToken: Option[Token] = + getSelectedRow() match { + case -1 ⇒ None + case n ⇒ Some(getModel.tokens(n)) + } + +} + +class TokenTableModel(val tokens: List[Token], formatResult: FormatResult) extends AbstractTableModel { + + def getColumnCount = 5 + + def getRowCount = tokens.size + + def getValueAt(row: Int, col: Int): AnyRef = { + val token = tokens(row) + col match { + case 0 ⇒ token.tokenType + case 1 ⇒ token.text + case 2 ⇒ token.offset.asInstanceOf[java.lang.Integer] + case 3 ⇒ token.lastCharacterOffset.asInstanceOf[java.lang.Integer] + case 4 ⇒ formatResult.predecessorFormatting.get(token) orElse formatResult.inferredNewlineFormatting.get(token) getOrElse "" + } + } + + override def getColumnName(col: Int) = col match { + case 0 ⇒ "Type" + case 1 ⇒ "Token text" + case 2 ⇒ "Start" + case 3 ⇒ "Finish" + case 4 ⇒ "Instruction" + } + +} \ No newline at end of file diff --git a/notes/scala-sync.txt b/notes/scala-sync.txt new file mode 100644 index 00000000..30a3f4f0 --- /dev/null +++ b/notes/scala-sync.txt @@ -0,0 +1,4 @@ +scalac changes checked 26/April/2013 + +Scanners.scala: cdffcf8962c9fa606c027fcb5a50a4273976a576 +Parsers.scala: cd148d97225fc7738f7a8ff9d4479cd46d632371 diff --git a/pom.xml b/pom.xml index 7bec643d..e990a1df 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 org.scalariform scalariform.parent - 0.1.4-SNAPSHOT + 0.1.5-SNAPSHOT pom @@ -14,11 +14,20 @@ - 0.12.0 - 2.8.0 - http://download.scala-ide.org + 0.18.1 + 2.10.2 UTF-8 + 3.0.4 + 2.11.0-M5 + 1.0.0-RC5 + 2_10 + + + ${maven.version} + + + scalariform scalariform.feature @@ -49,15 +58,11 @@ - - org.codehaus.mojo - buildnumber-maven-plugin - 1.1 - org.codehaus.mojo buildnumber-maven-plugin + 1.1 validate @@ -78,14 +83,14 @@ tycho-packaging-plugin ${tycho.version} - yyyyMMddHHmm'-${buildNumber}' + '${version.suffix}-'yyyyMMddHHmm'-${buildNumber}' true - org.scala-tools - maven-scala-plugin - 2.14 + net.alchim31.maven + scala-maven-plugin + 3.1.5 @@ -106,9 +111,9 @@ - galileo + kepler p2 - http://download.eclipse.org/releases/galileo + http://download.eclipse.org/releases/kepler sonatype.release @@ -126,71 +131,25 @@ - - release-ide-29 - - - scala-toolchain-scalaide-2.0 - Scala Toolchain for Scala IDE 2.0 p2 repository - p2 - http://download.scala-ide.org/incoming-release-29/dependencies/osgi-toolchain - - - - - release-ide-210 - - - scala-toolchain-scalaide-2.1 - Scala Toolchain for Scala IDE 2.0 p2 repository - p2 - http://download.scala-ide.org/incoming-release-210/dependencies/osgi-toolchain - - - - - release-ide-211 - - - scala-toolchain-scalaide-2.1 - Scala Toolchain for Scala IDE 2.0 p2 repository - p2 - http://download.scala-ide.org/incoming-release-211/dependencies/osgi-toolchain - - - - - scala-2.9.x - - - scala-toolchain-2.9.x - Scala Toolchain 2.9.x p2 repository - p2 - ${repo.scala-ide}/scala-eclipse-toolchain-osgi-29x - - - scala-2.10.x - - - scala-toolchain-2.10.x - Scala Toolchain trunk p2 repository - p2 - ${repo.scala-ide}/scala-eclipse-toolchain-osgi-210x - - + + 2_10 + scala-2.11.x - - - scala-toolchain-2.11.x - Scala Toolchain trunk p2 repository - p2 - ${repo.scala-ide}/scala-eclipse-toolchain-osgi-211x - - + + 2.11.0-SNAPSHOT + 2_11 + + + + org.scala-lang.modules + scala-xml_${scala.binary.version} + ${scala.xml.version} + + diff --git a/project/Build.scala b/project/Build.scala index 8a13ddc9..b84ef8d1 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -3,22 +3,21 @@ import sbt.Keys._ import com.github.retronym.SbtOneJar import com.typesafe.sbteclipse.core.EclipsePlugin.EclipseKeys._ import com.typesafe.sbteclipse.core.EclipsePlugin._ -import com.typesafe.sbtscalariform.ScalariformPlugin -import com.typesafe.sbtscalariform.ScalariformPlugin.ScalariformKeys +import com.typesafe.sbt.SbtScalariform +import com.typesafe.sbt.SbtScalariform.ScalariformKeys import scalariform.formatter.preferences._ object ScalariformBuild extends Build { - - lazy val commonSettings = Defaults.defaultSettings ++ ScalariformPlugin.defaultScalariformSettings ++ Seq( + lazy val commonSettings = Defaults.defaultSettings ++ SbtScalariform.defaultScalariformSettings ++ Seq( organization := "org.scalariform", - version := "0.1.4-SNAPSHOT", - scalaVersion := "2.9.2", + version := "0.1.5-SNAPSHOT", + scalaVersion := "2.10.0", crossScalaVersions := Seq( - "2.10.0-M7", - "2.9.2", "2.9.1-1", "2.9.1", "2.9.0-1", "2.9.0", - "2.8.2", "2.8.1", "2.8.0" - ), + // "2.11.0-M2", + "2.10.0", "2.10.1", + "2.9.3", "2.9.2", "2.9.1-1", "2.9.1", "2.9.0-1", "2.9.0", + "2.8.2", "2.8.1", "2.8.0"), exportJars := true, // Needed for cli oneJar retrieveManaged := true, scalacOptions += "-deprecation", @@ -36,9 +35,11 @@ object ScalariformBuild extends Build { publishLocal := ())) aggregate (scalariform, cli, misc) def getScalaTestDependency(scalaVersion: String) = scalaVersion match { - case "2.8.0" ⇒ "org.scalatest" %% "scalatest" % "1.3.1.RC2" % "test" - case "2.10.0-M7" ⇒ "org.scalatest" % "scalatest_2.10.0-M7" % "1.9-2.10.0-M7-B1" % "test" - case _ ⇒ "org.scalatest" %% "scalatest" % "1.7.2" % "test" + case "2.8.0" ⇒ "org.scalatest" %% "scalatest" % "1.3.1.RC2" % "test" + case "2.10.0" ⇒ "org.scalatest" %% "scalatest" % "1.9.1" % "test" + case "2.10.1" ⇒ "org.scalatest" %% "scalatest" % "1.9.1" % "test" + case "2.9.3" ⇒ "org.scalatest" %% "scalatest" % "1.9.1" % "test" + case _ ⇒ "org.scalatest" %% "scalatest" % "1.7.2" % "test" } lazy val scalariform: Project = Project("scalariform", file("scalariform"), settings = diff --git a/project/build.properties b/project/build.properties index 4474a03e..9b860e23 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=0.12.1 +sbt.version=0.12.3 diff --git a/project/plugins.sbt b/project/plugins.sbt old mode 100644 new mode 100755 index 25bb8511..4a817d0f --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -4,15 +4,15 @@ addSbtPlugin("com.github.retronym" % "sbt-onejar" % "0.8") resolvers += Classpaths.typesafeSnapshots -addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.1.0-SNAPSHOT") +addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.2.0") -addSbtPlugin("com.typesafe.sbtscalariform" % "sbtscalariform" % "0.5.1") +addSbtPlugin("com.typesafe.sbt" % "sbt-scalariform" % "1.0.1") -// resolvers += Resolver.url("sbt-plugin-releases", new URL("http://scalasbt.artifactoryonline.com/scalasbt/sbt-plugin-releases/"))(Resolver.ivyStylePatterns) +resolvers += Resolver.url("sbt-plugin-releases", new URL("http://scalasbt.artifactoryonline.com/scalasbt/sbt-plugin-releases/"))(Resolver.ivyStylePatterns) -addSbtPlugin("com.jsuereth" % "xsbt-gpg-plugin" % "0.6") +addSbtPlugin("com.typesafe.sbt" % "sbt-pgp" % "0.8") -addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.2.0") +addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.2.2") retrieveManaged := true diff --git a/project/project/plugins.sbt b/project/project/plugins.sbt old mode 100644 new mode 100755 index 1baaef62..d0201e8a --- a/project/project/plugins.sbt +++ b/project/project/plugins.sbt @@ -1,4 +1,3 @@ resolvers += Classpaths.typesafeSnapshots -addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.1.0-SNAPSHOT") - +addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.1.2") diff --git a/scalariform.feature/feature.xml b/scalariform.feature/feature.xml index d056b45e..d10adaad 100644 --- a/scalariform.feature/feature.xml +++ b/scalariform.feature/feature.xml @@ -2,7 +2,7 @@ + version="0.1.5.qualifier"> Code formatter for Scala 2.8 diff --git a/scalariform.feature/pom.xml b/scalariform.feature/pom.xml index e3f1c6a5..6f78f8d9 100644 --- a/scalariform.feature/pom.xml +++ b/scalariform.feature/pom.xml @@ -5,11 +5,11 @@ 4.0.0 scalariform.feature eclipse-feature - 0.1.4-SNAPSHOT + 0.1.5-SNAPSHOT scalariform.parent org.scalariform - 0.1.4-SNAPSHOT + 0.1.5-SNAPSHOT diff --git a/scalariform.update/pom.xml b/scalariform.update/pom.xml index 190ed28c..8ed0b120 100644 --- a/scalariform.update/pom.xml +++ b/scalariform.update/pom.xml @@ -5,11 +5,11 @@ 4.0.0 scalariform.update eclipse-update-site - 0.1.4-SNAPSHOT + 0.1.5-SNAPSHOT scalariform.parent org.scalariform - 0.1.4-SNAPSHOT + 0.1.5-SNAPSHOT diff --git a/scalariform.update/site.xml b/scalariform.update/site.xml index dd8f0a1a..0f1492e9 100644 --- a/scalariform.update/site.xml +++ b/scalariform.update/site.xml @@ -3,7 +3,7 @@ Scalariform Update Site - + diff --git a/scalariform/.classpath b/scalariform/.classpath index 41ff9ff0..93d8e858 100644 --- a/scalariform/.classpath +++ b/scalariform/.classpath @@ -1,13 +1,14 @@ - - - - - - - - - - - - + + + + + + + + + + + + + \ No newline at end of file diff --git a/scalariform/.project b/scalariform/.project index 369bc479..f848d8a7 100644 --- a/scalariform/.project +++ b/scalariform/.project @@ -1,35 +1,12 @@ - - scalariform - scalariform 0.1 - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.scala-ide.sdt.core.scalabuilder - - - - - org.scalastyle.scalastyleplugin.core.ScalastyleBuilder - - - - - - org.scala-ide.sdt.core.scalanature - org.eclipse.jdt.core.javanature - org.eclipse.pde.PluginNature - org.scalastyle.scalastyleplugin.core.ScalastyleNature - - + scalariform + + + org.scala-ide.sdt.core.scalabuilder + + + + org.scala-ide.sdt.core.scalanature + org.eclipse.jdt.core.javanature + + \ No newline at end of file diff --git a/scalariform/META-INF/MANIFEST.MF b/scalariform/META-INF/MANIFEST.MF index 7b688db4..fe283350 100644 --- a/scalariform/META-INF/MANIFEST.MF +++ b/scalariform/META-INF/MANIFEST.MF @@ -2,8 +2,9 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Scalariform Bundle-SymbolicName: scalariform -Bundle-Version: 0.1.4.qualifier -Require-Bundle: org.scala-ide.scala.library +Bundle-Version: 0.1.5.qualifier +Require-Bundle: org.scala-lang.scala-library +Import-Package: scala.xml.parsing Bundle-ClassPath: . Export-Package: scalariform, scalariform.astselect, diff --git a/scalariform/notes/0.1.4.markdown b/scalariform/notes/0.1.4.markdown new file mode 100644 index 00000000..61370721 --- /dev/null +++ b/scalariform/notes/0.1.4.markdown @@ -0,0 +1,3 @@ +* FIX: Allow declarations as last statement in case block (issue #60) +* Update to build 2.10 final + diff --git a/scalariform/pom.xml b/scalariform/pom.xml index dfc3b2de..2381a734 100644 --- a/scalariform/pom.xml +++ b/scalariform/pom.xml @@ -4,20 +4,20 @@ 4.0.0 org.scalariform scalariform - 0.1.4-SNAPSHOT + 0.1.5-SNAPSHOT eclipse-plugin scalariform.parent org.scalariform - 0.1.4-SNAPSHOT + 0.1.5-SNAPSHOT - org.sonatype.tycho - maven-osgi-compiler-plugin + org.eclipse.tycho + tycho-compiler-plugin ${tycho.version} diff --git a/scalariform/src/main/scala/scalariform/ScalaVersions.scala b/scalariform/src/main/scala/scalariform/ScalaVersions.scala index 71c34cf0..3aedf44c 100644 --- a/scalariform/src/main/scala/scalariform/ScalaVersions.scala +++ b/scalariform/src/main/scala/scalariform/ScalaVersions.scala @@ -12,12 +12,12 @@ object ScalaVersion { def parse(s: String): Option[ScalaVersion] = s match { - case VersionPattern(majorStr, minorStr, extra) => + case VersionPattern(majorStr, minorStr, extra) ⇒ for { - major <- majorStr.toIntOpt - minor <- minorStr.toIntOpt + major ← majorStr.toIntOpt + minor ← minorStr.toIntOpt } yield ScalaVersion(major, minor, extra) - case _ => + case _ ⇒ None } diff --git a/scalariform/src/main/scala/scalariform/formatter/CaseClauseFormatter.scala b/scalariform/src/main/scala/scalariform/formatter/CaseClauseFormatter.scala old mode 100644 new mode 100755 index 9255a4bc..fedb2ba8 --- a/scalariform/src/main/scala/scalariform/formatter/CaseClauseFormatter.scala +++ b/scalariform/src/main/scala/scalariform/formatter/CaseClauseFormatter.scala @@ -1,6 +1,6 @@ package scalariform.formatter -import scalariform.lexer.Token +import scalariform.lexer.{TokenType, Token} import scalariform.lexer.Tokens._ import scalariform.parser._ import scalariform.utils.Utils @@ -81,16 +81,18 @@ trait CaseClauseFormatter { self: HasFormattingPreferences with ExprFormatter wi val newlineBeforeClause = hiddenPredecessors(caseClause.firstToken).containsNewline || previousCaseClauseEndsWithNewline(caseClause, caseClausesAstNode) - // To evaluate whether a clause body is multiline, we ignore a trailing newline: + // To evaluate whether a clause body is multiline, we ignore a trailing newline: val prunedStatSeq = pruneTrailingNewline(statSeq) val clauseBodyIsMultiline = containsNewline(pruneTrailingNewline(statSeq)) || statSeq.firstTokenOption.exists(hiddenPredecessors(_).containsNewline) - if (formattedCasePattern.contains('\n') || (first && !clausesAreMultiline) || (!first && !newlineBeforeClause) || clauseBodyIsMultiline) + if (first && !clausesAreMultiline || !first && !newlineBeforeClause || + !formattingPreferences(AlignSingleLineCaseStatements.AlignMultiLineCaseStatements) && clauseBodyIsMultiline) Right(caseClause) :: otherClausesGrouped else { val arrowAdjust = (if (formattingPreferences(RewriteArrowSymbols)) 1 else casePattern.arrow.length) + 1 - val casePatternLength = formattedCasePattern.length - arrowAdjust + val casePatternLengthConsideringNewLines = formattedCasePattern.split('\n').last.length + val casePatternLength = casePatternLengthConsideringNewLines - arrowAdjust otherClausesGrouped match { case Left(consecutiveSingleLineCaseClauses) :: otherGroups ⇒ Left(consecutiveSingleLineCaseClauses.prepend(caseClause, casePatternLength)) :: otherGroups @@ -99,7 +101,26 @@ trait CaseClauseFormatter { self: HasFormattingPreferences with ExprFormatter wi } } } - groupClauses(caseClausesAstNode.caseClauses, first = true) + + val caseClauses: List[CaseClause] = caseClausesAstNode.caseClauses + + val ranges: List[(Int, Int)] = if (formattingPreferences(AlignSingleLineCaseStatements.GroupByNewLine)) { + val newLinesAt = (caseClauses.zipWithIndex.collect { + case (c, i) if c.tokens.exists(_.isNewlines) => i + 1 + }) + + val newLinesWithBeginAndEnd = (if (newLinesAt.contains(0)) List() else List(0)) ++ newLinesAt ++ List(caseClauses.length) + + newLinesWithBeginAndEnd.zip(newLinesWithBeginAndEnd.tail) + } else { + List(0 -> caseClauses.length) + } + + ranges.foldLeft(List[Either[ConsecutiveSingleLineCaseClauses, CaseClause]]()) { + case (acc, (begin, end)) => + val slice = caseClauses.slice(begin, end) + acc ++ groupClauses(slice, first = true) + } } private case class ConsecutiveSingleLineCaseClauses(clauses: List[CaseClause], largestCasePatternLength: Int, smallestCasePatternLength: Int) { @@ -114,6 +135,21 @@ trait CaseClauseFormatter { self: HasFormattingPreferences with ExprFormatter wi val CasePattern(caseToken: Token, pattern: Expr, guardOption: Option[Guard], arrow: Token) = casePattern var formatResult: FormatResult = NoFormatResult formatResult ++= format(pattern) + + val beginTokensAfterPipe = List(casePattern.pattern.tokens.head) ++ casePattern.pattern.tokens.zip(casePattern.pattern.tokens.tail).collect { + case (Token(PIPE, _, _, _), b) if b.associatedWhitespaceAndComments.containsNewline => b + } + + val alignBeginTokens: Map[Token, IntertokenFormatInstruction] = beginTokensAfterPipe.zip(beginTokensAfterPipe.tail).flatMap { + case (a, b) => if (b.associatedWhitespaceAndComments.containsNewline) { + Map(b -> EnsureNewlineAndIndent(0, Some(a))) + } else { + Map.empty[Token, IntertokenFormatInstruction] + } + }.toMap + + formatResult ++= FormatResult(alignBeginTokens, Map(), Map()) + for (guard ← guardOption) formatResult ++= format(guard) arrowInstructionOpt foreach { instruction ⇒ formatResult = formatResult.before(arrow, instruction) } @@ -149,10 +185,14 @@ trait CaseClauseFormatter { self: HasFormattingPreferences with ExprFormatter wi if separator.isNewline } yield separator - private def previousCaseClauseTrailingNewlineOpt(caseClause: CaseClause, caseClauses: CaseClauses): Option[Token] = - Utils.pairWithPrevious(caseClauses.caseClauses).collect { + private def previousCaseClauseTrailingNewlineOpt(caseClause: CaseClause, caseClauses: CaseClauses): Option[Token] = { + val previousCaseClauseOpt = Utils.pairWithPrevious(caseClauses.caseClauses).collect { case (Some(previousClause), `caseClause`) ⇒ previousClause - }.headOption.flatMap(getTrailingNewline) + }.headOption + val hasNewLine = previousCaseClauseOpt.flatMap(getTrailingNewline) + val hasNewLineAtEnd = previousCaseClauseOpt.flatMap(_.statSeq.tokens.lastOption.filter(_.isNewline)) + hasNewLine.orElse(hasNewLineAtEnd) + } private def previousCaseClauseEndsWithNewline(caseClause: CaseClause, caseClauses: CaseClauses): Boolean = previousCaseClauseTrailingNewlineOpt(caseClause, caseClauses).isDefined diff --git a/scalariform/src/main/scala/scalariform/formatter/ExprFormatter.scala b/scalariform/src/main/scala/scalariform/formatter/ExprFormatter.scala old mode 100644 new mode 100755 index f06d6804..ef59659c --- a/scalariform/src/main/scala/scalariform/formatter/ExprFormatter.scala +++ b/scalariform/src/main/scala/scalariform/formatter/ExprFormatter.scala @@ -636,15 +636,22 @@ trait ExprFormatter { self: HasFormattingPreferences with AnnotationFormatter wi (CompactEnsuringGap, indentedState) formatResult = formatResult.before(statSeq.firstToken, instruction) formatResult ++= format(params) + + val hasNewScopeAfterArrow = { + val arrowIndex = statSeq.tokens.indexWhere(_.tokenType == ARROW) + val tokenAfterArrow = statSeq.tokens.lift(arrowIndex + 1) + tokenAfterArrow.map(_.tokenType == lbrace.tokenType).getOrElse(false) + } for (firstToken ← subStatSeq.firstTokenOption) { val instruction = - if (hiddenPredecessors(firstToken).containsNewline || containsNewline(subStatSeq)) + if (!hasNewScopeAfterArrow && (hiddenPredecessors(firstToken).containsNewline || containsNewline(subStatSeq))) statFormatterState(subStatSeq.firstStatOpt)(subStatState).currentIndentLevelInstruction else CompactEnsuringGap formatResult = formatResult.before(firstToken, instruction) } - formatResult ++= format(subStatSeq)(subStatState) + val subStatStateAfterArrow = if(hasNewScopeAfterArrow) newFormatterState.indent else subStatState + formatResult ++= format(subStatSeq)(subStatStateAfterArrow) case _ ⇒ val instruction = statSeq.selfReferenceOpt match { case Some((selfReference, arrow)) if !hiddenPredecessors(selfReference.firstToken).containsNewline ⇒ @@ -683,7 +690,7 @@ trait ExprFormatter { self: HasFormattingPreferences with AnnotationFormatter wi } for (stat ← firstStatOpt) - formatResult ++= format(stat)(firstStatFormatterState) + formatResult ++= format(stat, true)(firstStatFormatterState) for ((semi, otherStatOption) ← otherStats) { @@ -702,7 +709,7 @@ trait ExprFormatter { self: HasFormattingPreferences with AnnotationFormatter wi CompactEnsuringGap formatResult = formatResult.before(firstToken, instruction) } - formatResult ++= format(otherStat)(otherStatFormatterState) + formatResult ++= format(otherStat, false)(otherStatFormatterState) } } @@ -710,15 +717,47 @@ trait ExprFormatter { self: HasFormattingPreferences with AnnotationFormatter wi formatResult } - private def format(stat: Stat)(implicit formatterState: FormatterState): FormatResult = + private def format(stat: Stat, firstStat: Boolean)(implicit formatterState: FormatterState): FormatResult = stat match { case expr: Expr ⇒ format(expr) case fullDefOrDcl: FullDefOrDcl ⇒ format(fullDefOrDcl) case import_ : ImportClause ⇒ format(import_) case packageBlock: PackageBlock ⇒ format(packageBlock) + case packageStat: PackageStat ⇒ format(packageStat, firstStat) case _ ⇒ NoFormatResult // TODO } + def format(packageStat: PackageStat, firstPackage: Boolean)(implicit formatterState: FormatterState): FormatResult = { + var formatResult: FormatResult = NoFormatResult + + if (formattingPreferences(ChainedPackageClauses)) { + val PackageStat(packageToken: Token, name: CallExpr) = packageStat + + val packageDepth = if(firstPackage) formattingPreferences(ChainedPackageClauses.PackageDepth) else 1 + + def dotsForPackageNames(name: CallExpr): List[Token] = { + name.exprDotOpt match { + case Some((List(c@CallExpr(_, id, _, _, _)), dot)) => + dot :: dotsForPackageNames(c) + case _ => + Nil + } + } + + val dotsBetweenPackageNames = dotsForPackageNames(name) + + val lineBreaksAfter = dotsBetweenPackageNames.reverse.drop(packageDepth-1) + + for { + token <- lineBreaksAfter + } { + formatResult ++= FormatResult(Map(token -> EnsureNewlineAndIndent(0, Some(packageToken))), Map(), Map()) + } + } + + formatResult + } + def format(packageBlock: PackageBlock)(implicit formatterState: FormatterState): FormatResult = { val PackageBlock(packageToken: Token, name: CallExpr, newlineOpt: Option[Token], lbrace: Token, topStats: StatSeq, rbrace: Token) = packageBlock @@ -909,9 +948,13 @@ trait ExprFormatter { self: HasFormattingPreferences with AnnotationFormatter wi val newFormatterState = formatterState.copy(inSingleLineBlock = singleLineBlock) if (singleLineBlock) { + if (formattingPreferences(NoSpacesAroundMultiImports)) + formatResult = formatResult.before(firstImportSelector.firstToken, Compact) formatResult ++= format(firstImportSelector) for ((comma, otherImportSelector) ← otherImportSelectors) formatResult ++= format(otherImportSelector) + if (formattingPreferences(NoSpacesAroundMultiImports)) + formatResult = formatResult.before(rbrace, Compact) } else { formatResult = formatResult.before(firstImportSelector.firstToken, formatterState.nextIndentLevelInstruction) formatResult ++= format(firstImportSelector) diff --git a/scalariform/src/main/scala/scalariform/formatter/FormatResult.scala b/scalariform/src/main/scala/scalariform/formatter/FormatResult.scala index 20da836a..92d0cd54 100644 --- a/scalariform/src/main/scala/scalariform/formatter/FormatResult.scala +++ b/scalariform/src/main/scala/scalariform/formatter/FormatResult.scala @@ -5,6 +5,12 @@ import scalariform.lexer._ import scalariform.parser._ import scalariform.utils._ +object FormatResult { + + val EMPTY = FormatResult(Map(), Map(), Map()) + +} + case class FormatResult(predecessorFormatting: Map[Token, IntertokenFormatInstruction], inferredNewlineFormatting: Map[Token, IntertokenFormatInstruction], xmlRewrites: Map[Token, String]) { diff --git a/scalariform/src/main/scala/scalariform/formatter/ScalaFormatter.scala b/scalariform/src/main/scala/scalariform/formatter/ScalaFormatter.scala old mode 100644 new mode 100755 index 17f945af..e6c1792e --- a/scalariform/src/main/scala/scalariform/formatter/ScalaFormatter.scala +++ b/scalariform/src/main/scala/scalariform/formatter/ScalaFormatter.scala @@ -221,7 +221,12 @@ abstract class ScalaFormatter extends HasFormattingPreferences with TypeFormatte val replacement = builder.substring(startPos) positionHintOption match { case Some(positionHint) if hiddenTokens.isEmpty ⇒ - Some(TextEdit(positionHint, length = 0, replacement = replacement)) + instruction match { + case EnsureNewlineAndIndent(indentLevel, Some(Token(PACKAGE, _, _, _))) => + Some(TextEdit(positionHint, length = 1, replacement = replacement + "package ")) + case _ => + Some(TextEdit(positionHint, length = 0, replacement = replacement)) + } case _ ⇒ for { firstToken ← hiddenTokens.firstTokenOption @@ -348,7 +353,7 @@ abstract class ScalaFormatter extends HasFormattingPreferences with TypeFormatte return CompactEnsuringGap if (type1 == MINUS && (type2 == INTEGER_LITERAL || type2 == FLOATING_POINT_LITERAL)) return Compact - if (Set(IMPLICIT, VAL, VAR, PRIVATE, PROTECTED, OVERRIDE).contains(type2) && type1 == LPAREN) + if (Set(IMPLICIT, VAL, VAR, PRIVATE, PROTECTED, OVERRIDE, FINAL).contains(type2) && type1 == LPAREN) return Compact if ((type1 == PROTECTED || type1 == PRIVATE) && type2 == LBRACKET) return Compact @@ -423,7 +428,7 @@ object ScalaFormatter { FINALLY, FOR, FORSOME, IF, IMPLICIT, IMPORT, LAZY, MATCH, NEW, OBJECT, OVERRIDE, PACKAGE, PRIVATE, PROTECTED, - REQUIRES, RETURN, SEALED, /* SUPER, THIS, */ + RETURN, SEALED, /* SUPER, THIS, */ THROW, TRAIT, TRY, /* TYPE ,*/ VAL, VAR, WHILE, WITH, YIELD, /* USCORE, */ COLON, EQUALS, ARROW, LARROW, SUBTYPE, VIEWBOUND, SUPERTYPE, /* HASH, AT */ @@ -435,7 +440,7 @@ object ScalaFormatter { FINALLY, /* FOR, */ FORSOME, /* IF, */ IMPLICIT, /* IMPORT, */ LAZY, MATCH, /* NEW, */ OBJECT, OVERRIDE, /* PACKAGE, */ PRIVATE, PROTECTED, - /* REQUIRES, RETURN, */ SEALED, /* SUPER, THIS, */ + /* RETURN, */ SEALED, /* SUPER, THIS, */ /* THROW, */ TRAIT, /* TRY, TYPE, */ VAL, VAR, /* WHILE, */ WITH, YIELD, /* USCORE, COLON, */ EQUALS, /* ARROW, */ LARROW, SUBTYPE, VIEWBOUND, SUPERTYPE, /*, HASH, AT, */ diff --git a/scalariform/src/main/scala/scalariform/formatter/SpecificFormatter.scala b/scalariform/src/main/scala/scalariform/formatter/SpecificFormatter.scala index b0b976d2..82bac11a 100644 --- a/scalariform/src/main/scala/scalariform/formatter/SpecificFormatter.scala +++ b/scalariform/src/main/scala/scalariform/formatter/SpecificFormatter.scala @@ -51,10 +51,10 @@ trait SpecificFormatter { } actualFormattingPreferences = actualFormattingPreferences.setPreference(preference, onOrOff) val parsedTokens = parseResult.tokens.filter(_.tokenType != EOF) - require(parsedTokens == tokens.init /* <-- drop EOF */, "Parse tokens differ from expected.\n Actual = \n" + + require(parsedTokens == tokens.init /* <-- drop EOF */ , "Parse tokens differ from expected.\n Actual = \n" + parsedTokens.mkString("\n") + "\n expected = \n" + tokens.init.mkString("\n") + "\n parseResult = \n" + parseResult) - + if (debug) { println("Parse result: " + parseResult) } val elapsedTime = System.currentTimeMillis - startTime // if (debug) diff --git a/scalariform/src/main/scala/scalariform/formatter/TemplateFormatter.scala b/scalariform/src/main/scala/scalariform/formatter/TemplateFormatter.scala index 81a3a9fd..d201fb5b 100644 --- a/scalariform/src/main/scala/scalariform/formatter/TemplateFormatter.scala +++ b/scalariform/src/main/scala/scalariform/formatter/TemplateFormatter.scala @@ -50,16 +50,20 @@ trait TemplateFormatter { self: HasFormattingPreferences with AnnotationFormatte formatResult ++= format(earlyBody)(currentFormatterState) for (templateParents ← templateParentsOpt) { - val TemplateParents(type1: Type, argumentExprss: List[ArgumentExprs], withTypes: List[(Token, Type)]) = templateParents + val TemplateParents((type1: Type, argumentExprss: List[ArgumentExprs]), withTypes: List[(Token, Type, List[ArgumentExprs])]) = templateParents + formatResult ++= format(type1)(currentFormatterState) for (argumentExprs ← argumentExprss) formatResult ++= format(argumentExprs)(currentFormatterState)._1 - for ((withToken, type_) ← withTypes) { + + for ((withToken, type_, argumentExprss2) ← withTypes) { if (hiddenPredecessors(withToken).containsNewline) { currentFormatterState = formatterState.indent(inheritanceIndent) formatResult = formatResult.before(withToken, currentFormatterState.currentIndentLevelInstruction) } formatResult ++= format(type_)(currentFormatterState) + for (argumentExprs2 ← argumentExprss2) + formatResult ++= format(argumentExprs2)(currentFormatterState)._1 } } } @@ -113,8 +117,20 @@ trait TemplateFormatter { self: HasFormattingPreferences with AnnotationFormatte private def format(templateParents: TemplateParents)(implicit formatterState: FormatterState): FormatResult = { var formatResult: FormatResult = NoFormatResult - for (argumentExprs ← templateParents.argumentExprss) + val TemplateParents((type1: Type, argumentExprss: List[ArgumentExprs]), withTypes: List[(Token, Type, List[ArgumentExprs])]) = templateParents + formatResult ++= format(type1) + for (argumentExprs ← argumentExprss) formatResult ++= format(argumentExprs)._1 + + // TODO: Unify with TmplDef code + + val currentFormatterState = formatterState + for ((withToken, type_, argumentExprss2) ← withTypes) { + formatResult ++= format(type_)(currentFormatterState) + for (argumentExprs2 ← argumentExprss2) + formatResult ++= format(argumentExprs2)(currentFormatterState)._1 + } + formatResult } diff --git a/scalariform/src/main/scala/scalariform/formatter/TypeFormatter.scala b/scalariform/src/main/scala/scalariform/formatter/TypeFormatter.scala index d69d6c11..7b2b725a 100644 --- a/scalariform/src/main/scala/scalariform/formatter/TypeFormatter.scala +++ b/scalariform/src/main/scala/scalariform/formatter/TypeFormatter.scala @@ -20,7 +20,7 @@ trait TypeFormatter { self: HasFormattingPreferences with AnnotationFormatter wi previousElement.isInstanceOf[InfixTypeConstructor] || element.isInstanceOf[Refinement] || element.isInstanceOf[InfixTypeConstructor]) - formatResult = formatResult.before(element.firstToken, CompactEnsuringGap) + formatResult = formatResult.formatNewlineOrOrdinary(element.firstToken, CompactEnsuringGap) else if (element.isInstanceOf[Annotation]) { val instruction = previousElement match { diff --git a/scalariform/src/main/scala/scalariform/formatter/preferences/PreferenceDescriptor.scala b/scalariform/src/main/scala/scalariform/formatter/preferences/PreferenceDescriptor.scala old mode 100644 new mode 100755 index 48975161..ed73714d --- a/scalariform/src/main/scala/scalariform/formatter/preferences/PreferenceDescriptor.scala +++ b/scalariform/src/main/scala/scalariform/formatter/preferences/PreferenceDescriptor.scala @@ -62,9 +62,9 @@ trait IntegerPreferenceDescriptor extends PreferenceDescriptor[Int] { object AllPreferences { val preferences: List[PreferenceDescriptor[_]] = List(RewriteArrowSymbols, IndentSpaces, SpaceBeforeColon, CompactStringConcatenation, PreserveSpaceBeforeArguments, AlignParameters, DoubleIndentClassDeclaration, FormatXml, IndentPackageBlocks, - AlignSingleLineCaseStatements, AlignSingleLineCaseStatements.MaxArrowIndent, IndentLocalDefs, PreserveDanglingCloseParenthesis, + AlignSingleLineCaseStatements, AlignSingleLineCaseStatements.MaxArrowIndent, AlignSingleLineCaseStatements.AlignMultiLineCaseStatements, IndentLocalDefs, PreserveDanglingCloseParenthesis, SpaceInsideParentheses, SpaceInsideBrackets, SpacesWithinPatternBinders, MultilineScaladocCommentsStartOnFirstLine, IndentWithTabs, - CompactControlReadability, PlaceScaladocAsterisksBeneathSecondAsterisk) + CompactControlReadability, PlaceScaladocAsterisksBeneathSecondAsterisk, NoSpacesAroundMultiImports, ChainedPackageClauses, ChainedPackageClauses.PackageDepth) val preferencesByKey: Map[String, PreferenceDescriptor[_]] = { var map: Map[String, PreferenceDescriptor[_]] = Map() @@ -142,6 +142,18 @@ case object AlignSingleLineCaseStatements extends BooleanPreferenceDescriptor { val defaultValue = 40 } + case object AlignMultiLineCaseStatements extends BooleanPreferenceDescriptor { + val key = "alignSingleLineCaseStatements.alignMultiLineCaseStatements" + val description = "Align the arrows of consecutive multi-line case statements" + val defaultValue = false + } + + case object GroupByNewLine extends BooleanPreferenceDescriptor { + val key = "alignSingleLineCaseStatements.groupByNewLine" + val description = "Treat blocks that are separated by newlines independently" + val defaultValue = false + } + } case object IndentLocalDefs extends BooleanPreferenceDescriptor { @@ -196,4 +208,23 @@ case object PlaceScaladocAsterisksBeneathSecondAsterisk extends BooleanPreferenc val key = "placeScaladocAsterisksBeneathSecondAsterisk" val description = "Place Scaladoc asterisks beneath the second asterisk in the opening '/**', as opposed to the first" val defaultValue = false -} \ No newline at end of file +} + +case object NoSpacesAroundMultiImports extends BooleanPreferenceDescriptor { + val key = "noSpacesAroundMultiImports" + val description = "Don't place spaces around multi imports (Java-style)" + val defaultValue = false +} + +case object ChainedPackageClauses extends BooleanPreferenceDescriptor { + val key = "chainedPackageClauses" + val description = "Break up chained package clauses" + val defaultValue = false + + case object PackageDepth extends IntegerPreferenceDescriptor { + val key = "chainedPackageClauses.packageDepth" + val description = "Depth of package nesting" + val preferenceType = IntegerPreference(1, 100) + val defaultValue = 4 + } +} diff --git a/scalariform/src/main/scala/scalariform/lexer/Keywords.scala b/scalariform/src/main/scala/scalariform/lexer/Keywords.scala index 552d2f11..1932b4f8 100644 --- a/scalariform/src/main/scala/scalariform/lexer/Keywords.scala +++ b/scalariform/src/main/scala/scalariform/lexer/Keywords.scala @@ -32,7 +32,6 @@ object Keywords { "package" -> PACKAGE, "private" -> PRIVATE, "protected" -> PROTECTED, - "requires" -> REQUIRES, "return" -> RETURN, "sealed" -> SEALED, "super" -> SUPER, @@ -65,4 +64,4 @@ object Keywords { "~" -> TILDE, "!" -> EXCLAMATION) -} \ No newline at end of file +} diff --git a/scalariform/src/main/scala/scalariform/lexer/NewlineInferencer.scala b/scalariform/src/main/scala/scalariform/lexer/NewlineInferencer.scala index 2a37e73a..b008a602 100644 --- a/scalariform/src/main/scala/scalariform/lexer/NewlineInferencer.scala +++ b/scalariform/src/main/scala/scalariform/lexer/NewlineInferencer.scala @@ -100,8 +100,8 @@ class NewlineInferencer(delegate: WhitespaceAndCommentsGrouper) extends Iterator false else if (TOKENS_WHICH_CANNOT_BEGIN_A_STATEMENT contains currentTokenType) false -// else if (currentTokenType == CASE && !followingTokenIsClassOrObject(nextToken)) -// false + // else if (currentTokenType == CASE && !followingTokenIsClassOrObject(nextToken)) + // false else if (regionMarkerStack.nonEmpty && regionMarkerStack.head != RBRACE) false else @@ -138,7 +138,7 @@ object NewlineInferencer { XML_COMMENT, XML_CDATA, XML_UNPARSED, XML_PROCESSING_INSTRUCTION, USCORE, RPAREN, RBRACKET, RBRACE) private val TOKENS_WHICH_CANNOT_BEGIN_A_STATEMENT: Set[TokenType] = Set( - CATCH, ELSE, EXTENDS, FINALLY, FORSOME, MATCH, REQUIRES, WITH, YIELD, COMMA, DOT, SEMI, COLON, /* USCORE, */ EQUALS, + CATCH, ELSE, EXTENDS, FINALLY, FORSOME, MATCH, WITH, YIELD, COMMA, DOT, SEMI, COLON, /* USCORE, */ EQUALS, ARROW, LARROW, SUBTYPE, VIEWBOUND, SUPERTYPE, HASH, LBRACKET, RPAREN, RBRACKET, RBRACE) private val BLANK_LINE_PATTERN = """(?s).*\n\s*\n.*""" diff --git a/scalariform/src/main/scala/scalariform/lexer/Token.scala b/scalariform/src/main/scala/scalariform/lexer/Token.scala index 365122eb..6e5cb72b 100644 --- a/scalariform/src/main/scala/scalariform/lexer/Token.scala +++ b/scalariform/src/main/scala/scalariform/lexer/Token.scala @@ -27,6 +27,8 @@ case class Token(tokenType: TokenType, text: String, offset: Int, rawText: Strin def isNewline = tokenType.isNewline + def isNewlines = tokenType.isNewlines + @deprecated(message = "Use text instead" /*, since = "0.1.2"*/ ) def getText = text diff --git a/scalariform/src/main/scala/scalariform/lexer/TokenType.scala b/scalariform/src/main/scala/scalariform/lexer/TokenType.scala index 2f51e561..fb64f3ba 100644 --- a/scalariform/src/main/scala/scalariform/lexer/TokenType.scala +++ b/scalariform/src/main/scala/scalariform/lexer/TokenType.scala @@ -2,7 +2,9 @@ package scalariform.lexer case class TokenType(name: String, isXml: Boolean = false) { - def isNewline = this == Tokens.NEWLINE || this == Tokens.NEWLINES + def isNewline = this == Tokens.NEWLINE || isNewlines + + def isNewlines = this == Tokens.NEWLINES def isKeyword = Tokens.KEYWORDS contains this diff --git a/scalariform/src/main/scala/scalariform/lexer/Tokens.scala b/scalariform/src/main/scala/scalariform/lexer/Tokens.scala index 3b2be011..4e7b305a 100644 --- a/scalariform/src/main/scala/scalariform/lexer/Tokens.scala +++ b/scalariform/src/main/scala/scalariform/lexer/Tokens.scala @@ -78,7 +78,6 @@ object Tokens { val SEMI = TokenType("SEMI") val COLON = TokenType("COLON") val OTHERID = TokenType("OTHERID") - val REQUIRES = TokenType("REQUIRES") val NEWLINE = TokenType("NEWLINE") val FINALLY = TokenType("FINALLY") val OVERRIDE = TokenType("OVERRIDE") @@ -105,7 +104,7 @@ object Tokens { FINALLY, FOR, FORSOME, IF, IMPLICIT, IMPORT, LAZY, MATCH, NEW, OBJECT, OVERRIDE, PACKAGE, PRIVATE, PROTECTED, - REQUIRES, RETURN, SEALED, SUPER, THIS, + RETURN, SEALED, SUPER, THIS, THROW, TRAIT, TRY, TYPE, VAL, VAR, WHILE, WITH, YIELD) diff --git a/scalariform/src/main/scala/scalariform/parser/AstNodes.scala b/scalariform/src/main/scala/scalariform/parser/AstNodes.scala index 9e05b063..23988e7f 100644 --- a/scalariform/src/main/scala/scalariform/parser/AstNodes.scala +++ b/scalariform/src/main/scala/scalariform/parser/AstNodes.scala @@ -26,6 +26,7 @@ sealed trait AstNode extends CaseClassReflector { protected implicit def listToFlattenable[T <% Flattenable](list: List[T]): Flattenable = new Flattenable { val tokens = list flatMap { _.tokens } } protected implicit def optionToFlattenable[T <% Flattenable](option: Option[T]): Flattenable = new Flattenable { val tokens = option.toList flatMap { _.tokens } } protected implicit def pairToFlattenable[T1 <% Flattenable, T2 <% Flattenable](pair: (T1, T2)): Flattenable = new Flattenable { val tokens = pair._1.tokens ::: pair._2.tokens } + protected implicit def tripleToFlattenable[T1 <% Flattenable, T2 <% Flattenable, T3 <% Flattenable](triple: (T1, T2, T3)): Flattenable = new Flattenable { val tokens = triple._1.tokens ++ triple._2.tokens ++ triple._3.tokens } protected implicit def eitherToFlattenable[T1 <% Flattenable, T2 <% Flattenable](either: T1 Either T2): Flattenable = new Flattenable { val tokens = either match { case Left(f) ⇒ f.tokens @@ -355,8 +356,8 @@ case class StatSeq(selfReferenceOpt: Option[(Expr, Token)], } -case class TemplateParents(type1: Type, argumentExprss: List[ArgumentExprs], withTypes: List[(Token, Type)]) extends AstNode { - lazy val tokens = flatten(type1, argumentExprss, withTypes) +case class TemplateParents(typeAndArgs: (Type, List[ArgumentExprs]), withTypesAndArgs: List[(Token, Type, List[ArgumentExprs])]) extends AstNode { + lazy val tokens = flatten(typeAndArgs, withTypesAndArgs) } case class ImportClause(importToken: Token, importExpr: ImportExpr, otherImportExprs: List[(Token, ImportExpr)]) extends AstNode with Stat { @@ -415,12 +416,15 @@ sealed trait XmlExprElement extends ExprElement case class XmlStartTag(startOpen: Token, name: Token, attributes: List[(Option[Token], XmlAttribute)], whitespaceOption: Option[Token], tagClose: Token) extends XmlExprElement { lazy val tokens = flatten(startOpen, name, attributes, whitespaceOption, tagClose) } + case class XmlAttribute(name: Token, whitespaceOption: Option[Token], equals: Token, whitespaceOption2: Option[Token], valueOrEmbeddedScala: Either[Token, Expr]) extends XmlExprElement { lazy val tokens = flatten(name, whitespaceOption, equals, whitespaceOption2, valueOrEmbeddedScala) } + case class XmlEmptyElement(startOpen: Token, name: Token, attributes: List[(Option[Token], XmlAttribute)], whitespaceOption: Option[Token], emptyClose: Token) extends XmlElement { lazy val tokens = flatten(startOpen, name, attributes, whitespaceOption, emptyClose) } + case class XmlEndTag(endOpen: Token, name: Token, whitespaceOption: Option[Token], tagClose: Token) extends XmlExprElement { lazy val tokens = flatten(endOpen, name, whitespaceOption, tagClose) } diff --git a/scalariform/src/main/scala/scalariform/parser/InferredSemicolonScalaParser.scala b/scalariform/src/main/scala/scalariform/parser/InferredSemicolonScalaParser.scala index 2be86d90..7707f71f 100644 --- a/scalariform/src/main/scala/scalariform/parser/InferredSemicolonScalaParser.scala +++ b/scalariform/src/main/scala/scalariform/parser/InferredSemicolonScalaParser.scala @@ -406,6 +406,10 @@ class InferredSemicolonScalaParser(tokens: Array[Token]) { dropAnyBraces(pattern()) else if (isIdent) ident() + else if (LBRACE) + expr() + else if (THIS) + nextToken() else expr() } @@ -651,7 +655,7 @@ class InferredSemicolonScalaParser(tokens: Array[Token]) { case NEW ⇒ canApply = false nextToken() - template(isTrait = false) + template() case _ ⇒ throw new ScalaParserException("illegal start of simple expression: " + currentToken) } @@ -1259,27 +1263,30 @@ class InferredSemicolonScalaParser(tokens: Array[Token]) { templateOpt(isTrait = false) } - private def templateParents(isTrait: Boolean) = { - startAnnotType() - if (LPAREN && !isTrait) multipleArgumentExprs() - else Nil + private def templateParents() { + def readAppliedParent() { + startAnnotType() + if (LPAREN) + multipleArgumentExprs() + } + readAppliedParent() while (WITH) { nextToken() - startAnnotType() + readAppliedParent() } } - private def template(isTrait: Boolean) { + private def template() { newLineOptWhenFollowedBy(LBRACE) if (LBRACE) { templateBody() if (WITH) { // TODO check cond nextToken() - templateParents(isTrait) + templateParents() templateBodyOpt() } } else { - templateParents(isTrait) + templateParents() templateBodyOpt() } } @@ -1287,7 +1294,7 @@ class InferredSemicolonScalaParser(tokens: Array[Token]) { private def templateOpt(isTrait: Boolean) { if (EXTENDS || SUBTYPE && isTrait) { nextToken() - template(isTrait) + template() } else { // val newLineOpt = newLineOptWhenFollowedBy(LBRACE) // Will be picked up by templateBodyOpt ... TODO: double check this templateBodyOpt() diff --git a/scalariform/src/main/scala/scalariform/parser/ScalaParser.scala b/scalariform/src/main/scala/scalariform/parser/ScalaParser.scala index 3bc845c8..036923a1 100644 --- a/scalariform/src/main/scala/scalariform/parser/ScalaParser.scala +++ b/scalariform/src/main/scala/scalariform/parser/ScalaParser.scala @@ -798,7 +798,7 @@ class ScalaParser(tokens: Array[Token]) { case NEW ⇒ canApply = false val newToken = nextToken() - val template_ = template(isTrait = false) + val template_ = template() List(New(newToken, template_)) case _ ⇒ throw new ScalaParserException("illegal start of simple expression: " + currentToken) @@ -1543,33 +1543,37 @@ class ScalaParser(tokens: Array[Token]) { templateBodyOption = templateOpt_.templateBodyOpt) } - private def templateParents(isTrait: Boolean): TemplateParents = { - val type1 = Type(startAnnotType()) - val argumentExprs_ = - if (LPAREN && !isTrait) multipleArgumentExprs() - else Nil - val withTypes = ListBuffer[(Token, Type)]() + private def templateParents(): TemplateParents = { + def readAppliedParent(): (Type, List[ArgumentExprs]) = { + val parent = Type(startAnnotType()) + val argss = + if (LPAREN) multipleArgumentExprs() + else Nil + (parent, argss) + } + val withTypes = ListBuffer[(Token, Type, List[ArgumentExprs])]() + val (type1, argumentExprs) = readAppliedParent() while (WITH) { val withToken = nextToken() - val withType = Type(startAnnotType()) - withTypes += ((withToken, withType)) + val (type2, argumentExprs2) = readAppliedParent() + withTypes += ((withToken, type2, argumentExprs2)) } - TemplateParents(type1, argumentExprs_, withTypes.toList) + TemplateParents((type1, argumentExprs), withTypes.toList) } - private def template(isTrait: Boolean): Template = { + private def template(): Template = { val newLineOpt = newLineOptWhenFollowedBy(LBRACE) if (LBRACE) { val templateBody_ = templateBody().copy(newlineOpt = newLineOpt) if (WITH) { // TODO check cond val withToken = nextToken() - val templateParents_ = templateParents(isTrait) + val templateParents_ = templateParents() val templateBodyOpt_ = templateBodyOpt() Template(Some(EarlyDefs(templateBody_, Some(withToken))), Some(templateParents_), templateBodyOpt_) } else Template(Some(EarlyDefs(templateBody_, withOpt = None)), templateParentsOpt = None, templateBodyOpt = None) } else { - val templateParents_ = templateParents(isTrait) + val templateParents_ = templateParents() val templateBodyOpt_ = templateBodyOpt() Template(earlyDefsOpt = None, Some(templateParents_), templateBodyOpt_) } @@ -1578,7 +1582,7 @@ class ScalaParser(tokens: Array[Token]) { private def templateOpt(isTrait: Boolean): TemplateOpt = { if (EXTENDS || SUBTYPE && isTrait) { val extendsOrSubtypeToken = nextToken() - template(isTrait) match { + template() match { case Template(earlyDefsOpt, templateParentsOpt, templateBodyOpt) ⇒ TemplateOpt(Some(TemplateInheritanceSection(extendsOrSubtypeToken, earlyDefsOpt, templateParentsOpt)), templateBodyOpt) } diff --git a/scalariform/src/main/scala/scalariform/utils/Utils.scala b/scalariform/src/main/scala/scalariform/utils/Utils.scala index 771b5106..4f796857 100644 --- a/scalariform/src/main/scala/scalariform/utils/Utils.scala +++ b/scalariform/src/main/scala/scalariform/utils/Utils.scala @@ -21,7 +21,7 @@ object Utils { implicit def string2PimpedString(s: String) = new PimpedString(s) class PimpedString(s: String) { - def toIntOpt: Option[Int] = try Some(s.toInt) catch { case _: NumberFormatException => None } + def toIntOpt: Option[Int] = try Some(s.toInt) catch { case _: NumberFormatException ⇒ None } } def stagger[T](iterable: Iterable[T]) = iterable zip iterable.tail diff --git a/scalariform/src/test/scala/scalariform/formatter/CaseClausesFormatterTest.scala b/scalariform/src/test/scala/scalariform/formatter/CaseClausesFormatterTest.scala old mode 100644 new mode 100755 index a2e106a6..06b20136 --- a/scalariform/src/test/scala/scalariform/formatter/CaseClausesFormatterTest.scala +++ b/scalariform/src/test/scala/scalariform/formatter/CaseClausesFormatterTest.scala @@ -306,4 +306,218 @@ class CaseClausesFormatterTest extends AbstractExpressionFormatterTest { | case elem @ Multi(values @ _*) => |}""" + { + implicit val formattingPreferences = FormattingPreferences.setPreference(AlignSingleLineCaseStatements, true) + .setPreference(AlignSingleLineCaseStatements.AlignMultiLineCaseStatements, true) + + """a match { + |case "once" => + |println("einmal") + |println("nochmal") + |case "many times" => + |println("again") + |println("multiline") + |case _ => + |println("again") + |println("multiline") + |}""" ==> + """a match { + | case "once" => + | println("einmal") + | println("nochmal") + | case "many times" => + | println("again") + | println("multiline") + | case _ => + | println("again") + | println("multiline") + |}""" + } + + { + implicit val formattingPreferences = FormattingPreferences.setPreference(AlignSingleLineCaseStatements, true) + .setPreference(AlignSingleLineCaseStatements.AlignMultiLineCaseStatements, true) + .setPreference(AlignSingleLineCaseStatements.GroupByNewLine, false) + + """a match { + |case "once" => + |println("einmal") + |println("nochmal") + |case "many times" => + |println("again") + |println("multiline") + |case _ => + |println("again") + |println("multiline") + | + |case "after a newline" => + |println("IntelliJ") + |println("formats") + |case "next lines" => + |println("separately") + |}""" ==> + """a match { + | case "once" => + | println("einmal") + | println("nochmal") + | case "many times" => + | println("again") + | println("multiline") + | case _ => + | println("again") + | println("multiline") + | + | case "after a newline" => + | println("IntelliJ") + | println("formats") + | case "next lines" => + | println("separately") + |}""" + } + + { + implicit val formattingPreferences = FormattingPreferences.setPreference(AlignSingleLineCaseStatements, true) + .setPreference(AlignSingleLineCaseStatements.AlignMultiLineCaseStatements, true) + .setPreference(AlignSingleLineCaseStatements.GroupByNewLine, true) + + """a match { + |case "once" => + |println("einmal") + |println("nochmal") + |case "many times" => + |println("again") + |println("multiline") + |case _ => + |println("again") + |println("multiline") + | + |case "after a newline" => + |println("IntelliJ") + |println("formats") + |case "next lines" => + |println("separately") + |}""" ==> + """a match { + | case "once" => + | println("einmal") + | println("nochmal") + | case "many times" => + | println("again") + | println("multiline") + | case _ => + | println("again") + | println("multiline") + | + | case "after a newline" => + | println("IntelliJ") + | println("formats") + | case "next lines" => + | println("separately") + |}""" + } + + { + implicit val formattingPreferences = FormattingPreferences.setPreference(AlignSingleLineCaseStatements, true) + .setPreference(AlignSingleLineCaseStatements.MaxArrowIndent, 200) + .setPreference(AlignSingleLineCaseStatements.AlignMultiLineCaseStatements, true) + .setPreference(AlignSingleLineCaseStatements.GroupByNewLine, true) + + """x match { + |case 1 | 2 | + |3 | 4 | + |5 | 6 => 1 + |case 7 | 8 => 1 + |}""" ==> + """x match { + | case 1 | 2 | + | 3 | 4 | + | 5 | 6 => 1 + | case 7 | 8 => 1 + |}""" + } + + { + implicit val formattingPreferences = FormattingPreferences.setPreference(AlignSingleLineCaseStatements, true) + .setPreference(AlignSingleLineCaseStatements.AlignMultiLineCaseStatements, true) + + """x match { case _: X => Y }""" ==> + """x match { case _: X => Y }""" + } + + { + implicit val formattingPreferences = FormattingPreferences.setPreference(AlignSingleLineCaseStatements, true) + .setPreference(AlignSingleLineCaseStatements.AlignMultiLineCaseStatements, true) + .setPreference(AlignSingleLineCaseStatements.MaxArrowIndent, 200) + .setPreference(AlignSingleLineCaseStatements.GroupByNewLine, true) + + """|x match { + | case 1 => A + | case 1234 | + | 5678 => A + | case 5 => A + | case 12345678 => A + |}""" ==> + """|x match { + | case 1 => A + | case 1234 | + | 5678 => A + | case 5 => A + | case 12345678 => A + |}""" + } + + { + implicit val formattingPreferences = FormattingPreferences.setPreference(AlignSingleLineCaseStatements, true) + .setPreference(AlignSingleLineCaseStatements.AlignMultiLineCaseStatements, true) + .setPreference(AlignSingleLineCaseStatements.MaxArrowIndent, 200) + + """|x match { + |case AA => a + |case B => b + |}""" ==> + """|x match { + | case AA => a + | case B => b + |}""" + } + + { + implicit val formattingPreferences = FormattingPreferences.setPreference(AlignSingleLineCaseStatements, true) + .setPreference(AlignSingleLineCaseStatements.AlignMultiLineCaseStatements, true) + + """|x match { + |case AA => (s: Int) => 1 + |case B => (s: Int) => 2 + |}""" ==> + """|x match { + | case AA => (s: Int) => 1 + | case B => (s: Int) => 2 + |}""" + } + + { + implicit val formattingPreferences = FormattingPreferences.setPreference(AlignSingleLineCaseStatements, true) + .setPreference(AlignSingleLineCaseStatements.AlignMultiLineCaseStatements, true) + .setPreference(AlignSingleLineCaseStatements.MaxArrowIndent, 160) + .setPreference(AlignSingleLineCaseStatements.GroupByNewLine, true) + .setPreference(SpacesWithinPatternBinders, false) + + """|pairs.flatMap { + | case (p1@(t1, MandatoryGridPoint(true) | ExtremalGridPoint), + | (t2, MandatoryGridPoint(true) | ExtremalGridPoint)) => + | val fillPointTime = (t1 + t2) / 2 + | val fillPoint = (fillPointTime, FillingGridPoint) + | Seq(p1, fillPoint) + | case (p1, p2) => Seq(p1) + |}""" ==> + """|pairs.flatMap { + | case (p1@(t1, MandatoryGridPoint(true) | ExtremalGridPoint), + | (t2, MandatoryGridPoint(true) | ExtremalGridPoint)) => + | val fillPointTime = (t1 + t2) / 2 + | val fillPoint = (fillPointTime, FillingGridPoint) + | Seq(p1, fillPoint) + | case (p1, p2) => Seq(p1) + |}""" + } + } diff --git a/scalariform/src/test/scala/scalariform/formatter/MiscExpressionFormatterTest.scala b/scalariform/src/test/scala/scalariform/formatter/MiscExpressionFormatterTest.scala old mode 100644 new mode 100755 index 0f319ad6..8b3427ad --- a/scalariform/src/test/scala/scalariform/formatter/MiscExpressionFormatterTest.scala +++ b/scalariform/src/test/scala/scalariform/formatter/MiscExpressionFormatterTest.scala @@ -771,11 +771,44 @@ class MiscExpressionFormatterTest extends AbstractExpressionFormatterTest { | b => c |}""" + """|a map { + |q => { + |2 * q + |} + |}""" ==> + """|a map { + | q => { + | 2 * q + | } + |}""" + + """|a map { + |q => + |{ + |2 * q + |} + |}""" ==> + """|a map { + | q => { + | 2 * q + | } + |}""" + "f()[Foo]" ==> "f()[Foo]" "a [ b . C ] [ d . E ] [ f . G ] " ==> "a[b.C][d.E][f.G]" "{ val P(a, b*c) = p }" ==> "{ val P(a, b * c) = p }" + + """new {} with A(new { + |val x = 42}) with B(new { + |val x = 42})""" ==> + """new {} with A(new { + | val x = 42 + |}) with B(new { + | val x = 42 + |})""" + override val debug = false } diff --git a/scalariform/src/test/scala/scalariform/formatter/PackageFormatterTest.scala b/scalariform/src/test/scala/scalariform/formatter/PackageFormatterTest.scala old mode 100644 new mode 100755 index 4634e8d5..6301657d --- a/scalariform/src/test/scala/scalariform/formatter/PackageFormatterTest.scala +++ b/scalariform/src/test/scala/scalariform/formatter/PackageFormatterTest.scala @@ -63,5 +63,32 @@ class PackageFormatterTest extends AbstractFormatterTest { } + { + implicit val formattingPreferences = FormattingPreferences.setPreference(ChainedPackageClauses, true) + + """package a.b.c.d.e.f + |""" ==> + """package a.b.c.d + |package e + |package f + |""" + + """package com.company.analytics.math.curves.interpolators + |""" ==> + """package com.company.analytics.math + |package curves + |package interpolators + |""" + + """package a.b.c.d.e.f + |package g.h + |""" ==> + """package a.b.c.d + |package e + |package f + |package g + |package h + |""" + } } diff --git a/scalariform/src/test/scala/scalariform/formatter/TemplateFormatterTest.scala b/scalariform/src/test/scala/scalariform/formatter/TemplateFormatterTest.scala index e36c9c59..cc0a2783 100644 --- a/scalariform/src/test/scala/scalariform/formatter/TemplateFormatterTest.scala +++ b/scalariform/src/test/scala/scalariform/formatter/TemplateFormatterTest.scala @@ -195,6 +195,7 @@ class TemplateFormatterTest extends AbstractFormatterTest { "class A(private val b: C)" ==> "class A(private val b: C)" "class A(protected val b: C)" ==> "class A(protected val b: C)" "class A(override val b: C)" ==> "class A(override val b: C)" + "class A(final val b: C)" ==> "class A(final val b: C)" """class C[T <: A {val n: Int |val m :Int}]""" ==> @@ -598,6 +599,15 @@ class TemplateFormatterTest extends AbstractFormatterTest { | @D def e |}""" + """class A extends B(42 , 43) with C(44 , 45) { + | foo() + | bar() + |}""" ==> + """class A extends B(42, 43) with C(44, 45) { + | foo() + | bar() + |}""" + // format: ON override val debug = false diff --git a/scalariform/src/test/scala/scalariform/formatter/TypeFormatterTest.scala b/scalariform/src/test/scala/scalariform/formatter/TypeFormatterTest.scala index ce2d39a8..2906e8e0 100644 --- a/scalariform/src/test/scala/scalariform/formatter/TypeFormatterTest.scala +++ b/scalariform/src/test/scala/scalariform/formatter/TypeFormatterTest.scala @@ -63,6 +63,10 @@ class TypeFormatterTest extends AbstractFormatterTest { "b[c# ::[d]]" ==> "b[c# ::[d]]" + """C :: + | D""" ==> + """C :: D""" // To check that this doesn't blow up -- we should maintain the newline + override val debug = false type Result = Type diff --git a/scalariform/src/test/scala/scalariform/lexer/NewlineInferencerTest.scala b/scalariform/src/test/scala/scalariform/lexer/NewlineInferencerTest.scala index 2ecbd04f..6455dced 100644 --- a/scalariform/src/test/scala/scalariform/lexer/NewlineInferencerTest.scala +++ b/scalariform/src/test/scala/scalariform/lexer/NewlineInferencerTest.scala @@ -14,7 +14,7 @@ import java.io._ class NewlineInferencerTest extends FlatSpec with ShouldMatchers { implicit def string2TestString(s: String)(implicit forgiveErrors: Boolean = false, scalaVersion: ScalaVersion = ScalaVersions.DEFAULT) = - new TestString(s, forgiveErrors, scalaVersion) + new TestString(s, forgiveErrors, scalaVersion); // See issue #60 """ diff --git a/scalariform/src/test/scala/scalariform/parser/ParserTest.scala b/scalariform/src/test/scala/scalariform/parser/ParserTest.scala index 0b000233..4a2063da 100644 --- a/scalariform/src/test/scala/scalariform/parser/ParserTest.scala +++ b/scalariform/src/test/scala/scalariform/parser/ParserTest.scala @@ -25,7 +25,7 @@ class ParserTest extends FlatSpec with ShouldMatchers { parseExpression("{ case List[String]() => 12 }") } - // See issue #60 + // See issue #60 "Parser" should "not throw an exception on case block ending with decl" in { parseExpression(""" args(0) match { @@ -36,7 +36,7 @@ class ParserTest extends FlatSpec with ShouldMatchers { } """) } - + "Parser" should "throw a parse exception in bad package blocks" in { evaluating { parseCompilationUnit("package a {} package b {}") } should produce[ScalaParserException] }