├── docs ├── source │ ├── README.rst │ └── conf.py └── Makefile ├── project ├── build.properties ├── project │ └── plugins.sbt ├── .project ├── plugins.sbt ├── .classpath └── Build.scala ├── scalariform.feature ├── build.properties ├── compile.scalariform.feature.xml ├── .project ├── pom.xml └── feature.xml ├── .travis.yml ├── scalariform ├── notes │ ├── about.markdown │ ├── 0.1.4.markdown │ ├── 0.1.3.markdown │ ├── 0.1.6.markdown │ ├── 0.1.5.markdown │ ├── 0.0.6.markdown │ ├── 0.0.7.markdown │ ├── 0.1.2.markdown │ ├── 0.0.8.markdown │ ├── 0.1.0.markdown │ ├── 0.0.9.markdown │ └── 0.1.1.markdown ├── build.properties ├── src │ ├── main │ │ └── scala │ │ │ └── com │ │ │ └── danieltrinh │ │ │ └── scalariform │ │ │ ├── lexer │ │ │ ├── LexerResult.scala │ │ │ ├── CharConstants.scala │ │ │ ├── ScalaLexerException.scala │ │ │ ├── TagState.scala │ │ │ ├── TokenType.scala │ │ │ ├── ScalaLexerReader.scala │ │ │ ├── HiddenToken.scala │ │ │ ├── LexerMode.scala │ │ │ ├── Chars.scala │ │ │ ├── Token.scala │ │ │ ├── RedundantSemicolonDetector.scala │ │ │ ├── ModeStack.scala │ │ │ ├── HiddenTokens.scala │ │ │ ├── WhitespaceAndCommentsGrouper.scala │ │ │ ├── Keywords.scala │ │ │ ├── Tokens.scala │ │ │ ├── UnicodeEscapeReader.scala │ │ │ └── NewlineInferencer.scala │ │ │ ├── parser │ │ │ └── ScalaParserException.scala │ │ │ ├── utils │ │ │ ├── BooleanLang.scala │ │ │ ├── Trimmed.scala │ │ │ ├── CaseClassReflector.scala │ │ │ ├── Range.scala │ │ │ ├── TextEdits.scala │ │ │ └── Utils.scala │ │ │ ├── formatter │ │ │ ├── AnnotationFormatter.scala │ │ │ ├── FormatterDirectiveParser.scala │ │ │ ├── FormatterState.scala │ │ │ ├── preferences │ │ │ │ ├── IFormattingPreferences.scala │ │ │ │ └── PreferencesImporterExporter.scala │ │ │ ├── Alignment.scala │ │ │ ├── CommentFormatter.scala │ │ │ ├── TypeFormatter.scala │ │ │ ├── FormatResult.scala │ │ │ ├── SpecificFormatter.scala │ │ │ └── TemplateFormatter.scala │ │ │ └── ScalaVersions.scala │ └── test │ │ └── scala │ │ └── com │ │ └── danieltrinh │ │ └── scalariform │ │ ├── formatter │ │ ├── AbstractExpressionFormatterTest.scala │ │ ├── RewriteArrowsTest.scala │ │ ├── ScriptFormatterTest.scala │ │ ├── MiscFormatterTest.scala │ │ ├── FunctionFormatterTest.scala │ │ ├── StringInterpolationFormatterTest.scala │ │ ├── PackageFormatterTest.scala │ │ ├── FormatterDirectiveParserTest.scala │ │ ├── ImportFormatterTest.scala │ │ ├── BlockExprFormatterTest.scala │ │ ├── CompactControlReadabilityTest.scala │ │ ├── WhileExprFormatterTest.scala │ │ ├── TypeFormatterTest.scala │ │ ├── ForExprFormatterTest.scala │ │ ├── TryExprFormatterTest.scala │ │ ├── AbstractFormatterTest.scala │ │ ├── IndentWithTabsTest.scala │ │ ├── CommentFormatterTest.scala │ │ ├── ParenAndBracketSpacingTest.scala │ │ ├── XmlExpressionFormatterTest.scala │ │ ├── CaseClausesFormatterTest.scala │ │ └── DefOrDclFormatterTest.scala │ │ ├── utils │ │ ├── TrimmedTest.scala │ │ └── TextEditTest.scala │ │ ├── parser │ │ ├── ScalaParserTest.scala │ │ └── ParserTest.scala │ │ └── lexer │ │ ├── RedundantSemicolonDetectorTest.scala │ │ └── NewlineInferencerTest.scala ├── .project ├── META-INF │ └── MANIFEST.MF ├── pom.xml └── .classpath ├── scripts ├── count-sloc.sh ├── update-version.sh └── version-list.txt ├── notes └── scala-sync.txt ├── .ensime ├── cli ├── .project ├── .classpath └── src │ └── main │ └── scala │ └── com │ └── danieltrinh │ └── scalariform │ └── commandline │ ├── ScalaFileWalker.scala │ └── CommandLineOptionParser.scala ├── misc ├── .project ├── src │ └── main │ │ └── scala │ │ └── com │ │ └── danieltrinh │ │ └── scalariform │ │ ├── gui │ │ ├── SwingUtils.scala │ │ ├── Main.scala │ │ ├── TokenTable.scala │ │ └── ParseTreeModel.scala │ │ ├── perf │ │ └── LexerPerformanceTest.scala │ │ └── corpusscan │ │ └── CorpusScanner.scala └── .classpath ├── .gitignore ├── scalariform.update ├── site.xml ├── .project ├── pom.xml ├── web │ └── site.css └── index.html ├── CONTRIBUTORS ├── formatterPreferences.properties ├── LICENCE └── pom.xml /docs/source/README.rst: -------------------------------------------------------------------------------- 1 | ../../README.rst -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.7 2 | -------------------------------------------------------------------------------- /scalariform.feature/build.properties: -------------------------------------------------------------------------------- 1 | bin.includes = feature.xml 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: scala 2 | scala: 3 | - 2.11.7 4 | - 2.10.6 5 | - 2.9.3 6 | jdk: 7 | - oraclejdk7 8 | - openjdk7 -------------------------------------------------------------------------------- /scalariform/notes/about.markdown: -------------------------------------------------------------------------------- 1 | [Scalariform][1] is a source code formatter for Scala. 2 | 3 | [1]: http://github.com/mdr/scalariform 4 | -------------------------------------------------------------------------------- /scalariform/notes/0.1.4.markdown: -------------------------------------------------------------------------------- 1 | * FIX: Allow declarations as last statement in case block (issue #60) 2 | * Update to build 2.10 final 3 | 4 | -------------------------------------------------------------------------------- /scalariform/build.properties: -------------------------------------------------------------------------------- 1 | bin.includes = META-INF/,\ 2 | . 3 | source.. = src/main/scala/ 4 | jars.compile.order = . 5 | output.. = bin/ 6 | -------------------------------------------------------------------------------- /scripts/count-sloc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd `dirname $0`/.. 3 | cloc --exclude-dir=src_managed,target,docs '--exclude-lang=XML,XSLT,HTML,CSS,Bourne Shell' . 4 | -------------------------------------------------------------------------------- /project/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | resolvers += Classpaths.typesafeSnapshots 2 | 3 | addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.4.0") 4 | 5 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/lexer/LexerResult.scala: -------------------------------------------------------------------------------- 1 | package scalariform.lexer 2 | 3 | trait LexerResult { 4 | 5 | def tokens: List[Token] 6 | 7 | } 8 | -------------------------------------------------------------------------------- /notes/scala-sync.txt: -------------------------------------------------------------------------------- 1 | scalac changes checked 26/April/2013 2 | 3 | Scanners.scala: cdffcf8962c9fa606c027fcb5a50a4273976a576 4 | Parsers.scala: cd148d97225fc7738f7a8ff9d4479cd46d632371 5 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/lexer/CharConstants.scala: -------------------------------------------------------------------------------- 1 | package scalariform.lexer 2 | 3 | object CharConstants { 4 | 5 | final val SU = '\u001A' 6 | 7 | } 8 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/parser/ScalaParserException.scala: -------------------------------------------------------------------------------- 1 | package scalariform.parser 2 | 3 | class ScalaParserException(message: String) extends RuntimeException(message) 4 | -------------------------------------------------------------------------------- /scalariform/notes/0.1.3.markdown: -------------------------------------------------------------------------------- 1 | * Add `EOF` to `ComplilationUnit`, ensuring entire source is represented in the tree 2 | * Support `$this` references in String interpolation 3 | * Update build to sbt 0.12.0 4 | -------------------------------------------------------------------------------- /scripts/update-version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | PREVIOUS=$1 3 | NEXT=$2 4 | 5 | cd `dirname $0`/.. 6 | 7 | while read p; do 8 | echo "Changing $p" 9 | sed -i s/$PREVIOUS/$NEXT/g $p 10 | done < scripts/version-list.txt 11 | 12 | git diff 13 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/lexer/ScalaLexerException.scala: -------------------------------------------------------------------------------- 1 | package scalariform.lexer 2 | 3 | import scalariform.parser.ScalaParserException 4 | 5 | class ScalaLexerException(message: String) extends ScalaParserException(message) 6 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/lexer/TagState.scala: -------------------------------------------------------------------------------- 1 | package scalariform.lexer 2 | 3 | abstract sealed trait TagState 4 | case object InStartTag extends TagState 5 | case object InEndTag extends TagState 6 | case object Normal extends TagState 7 | 8 | -------------------------------------------------------------------------------- /scalariform.feature/compile.scalariform.feature.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.ensime: -------------------------------------------------------------------------------- 1 | ;; This config was generated using ensime-config-gen. Feel free to customize its contents manually. 2 | 3 | ( 4 | 5 | :project-package "scalariform" 6 | 7 | :use-sbt t 8 | 9 | :formatting-prefs (:rewriteArrowSymbols t :alignParameters t :alignSingleLineCaseStatements t) 10 | 11 | ) 12 | -------------------------------------------------------------------------------- /scripts/version-list.txt: -------------------------------------------------------------------------------- 1 | pom.xml 2 | project/Build.scala 3 | scalariform/pom.xml 4 | scalariform.feature/pom.xml 5 | scalariform.feature/feature.xml 6 | scalariform.update/pom.xml 7 | scalariform.update/site.xml 8 | docs/source/conf.py 9 | scalariform/META-INF/MANIFEST.MF 10 | scalariform/src/main/scala/scalariform/package.scala 11 | README.rst 12 | -------------------------------------------------------------------------------- /cli/.project: -------------------------------------------------------------------------------- 1 | 2 | cli 3 | 4 | 5 | org.scala-ide.sdt.core.scalabuilder 6 | 7 | 8 | 9 | org.scala-ide.sdt.core.scalanature 10 | org.eclipse.jdt.core.javanature 11 | 12 | -------------------------------------------------------------------------------- /misc/.project: -------------------------------------------------------------------------------- 1 | 2 | misc 3 | 4 | 5 | org.scala-ide.sdt.core.scalabuilder 6 | 7 | 8 | 9 | org.scala-ide.sdt.core.scalanature 10 | org.eclipse.jdt.core.javanature 11 | 12 | 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | lib_managed/ 3 | src_managed/ 4 | project/boot/ 5 | bin/ 6 | .scala_dependencies 7 | core/target/ 8 | core/lib_managed/ 9 | core/src_managed/ 10 | gui/target/ 11 | gui/lib_managed/ 12 | gui/src_managed/ 13 | corpusscan/target/ 14 | corpusscan/lib_managed/ 15 | corpusscan/src_managed/ 16 | docs/build 17 | .settings 18 | nohup.out 19 | .history 20 | .cache 21 | .idea/ -------------------------------------------------------------------------------- /project/.project: -------------------------------------------------------------------------------- 1 | 2 | default-719b99 3 | 4 | 5 | org.scala-ide.sdt.core.scalabuilder 6 | 7 | 8 | 9 | org.scala-ide.sdt.core.scalanature 10 | org.eclipse.jdt.core.javanature 11 | 12 | -------------------------------------------------------------------------------- /scalariform/.project: -------------------------------------------------------------------------------- 1 | 2 | scalariform 3 | 4 | 5 | org.scala-ide.sdt.core.scalabuilder 6 | 7 | 8 | 9 | org.scala-ide.sdt.core.scalanature 10 | org.eclipse.jdt.core.javanature 11 | 12 | 13 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/utils/BooleanLang.scala: -------------------------------------------------------------------------------- 1 | package scalariform.utils 2 | 3 | object BooleanLang { 4 | 5 | def not(b: Boolean) = !b 6 | 7 | class PimpedBoolean(b1: Boolean) { 8 | def and(b2: Boolean) = b1 && b2 9 | def or(b2: Boolean) = b1 || b2 10 | } 11 | 12 | implicit def boolean2PimpedBoolean(b: Boolean): PimpedBoolean = new PimpedBoolean(b) 13 | 14 | } 15 | -------------------------------------------------------------------------------- /scalariform/notes/0.1.6.markdown: -------------------------------------------------------------------------------- 1 | * FIX: Bug in parser prevented DOT characters inside type parameters (issue #44) 2 | * Newline no longer added for each function in `=> => =>` scenarios (issue #45) 3 | * Update build to sbt 0.13.7 and update java version validation (PR #42) 4 | * Rewrite -> into → when RewriteArrowSymbols is on (issue #34) 5 | * Sync maven build with sbt build (PR #22) 6 | * Add support for Scala 2.12 in the maven build (PR #20) 7 | -------------------------------------------------------------------------------- /misc/src/main/scala/com/danieltrinh/scalariform/gui/SwingUtils.scala: -------------------------------------------------------------------------------- 1 | package scalariform.gui 2 | 3 | import javax.swing.event.ListSelectionListener 4 | import javax.swing.event.ListSelectionEvent 5 | 6 | object SwingUtils { 7 | 8 | implicit def fn2ListSelectionListener(handler: ListSelectionEvent ⇒ Unit): ListSelectionListener = new ListSelectionListener() { 9 | def valueChanged(e: ListSelectionEvent) = handler(e) 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /misc/src/main/scala/com/danieltrinh/scalariform/gui/Main.scala: -------------------------------------------------------------------------------- 1 | package scalariform.gui 2 | 3 | import scalariform.utils.Utils._ 4 | import javax.swing.JFrame 5 | 6 | object Main { 7 | 8 | def main(args: Array[String]) { 9 | onSwingThread { 10 | val frame = new FormatterFrame 11 | frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) 12 | frame.setSize(1280, 600) 13 | frame.setVisible(true) 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /scalariform.update/site.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Scalariform Update Site 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | resolvers += Classpaths.typesafeReleases 2 | 3 | resolvers += Classpaths.typesafeSnapshots 4 | 5 | addSbtPlugin("org.scalariform" % "sbt-scalariform" % "1.5.1") 6 | 7 | addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0") 8 | 9 | addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.3.0") 10 | 11 | addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "0.2.1") 12 | 13 | addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.13.0") 14 | 15 | retrieveManaged := true -------------------------------------------------------------------------------- /scalariform.feature/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | scalariform.feature 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.pde.FeatureBuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.pde.FeatureNature 16 | 17 | 18 | -------------------------------------------------------------------------------- /scalariform/src/test/scala/com/danieltrinh/scalariform/formatter/AbstractExpressionFormatterTest.scala: -------------------------------------------------------------------------------- 1 | package scalariform.formatter 2 | 3 | import scalariform.parser._ 4 | 5 | abstract class AbstractExpressionFormatterTest extends AbstractFormatterTest { 6 | 7 | type Result = Expr 8 | 9 | def format(formatter: ScalaFormatter, result: Result) = formatter.format(result)(FormatterState()) 10 | 11 | def parse(parser: ScalaParser) = parser.expr() 12 | 13 | } 14 | -------------------------------------------------------------------------------- /scalariform.update/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | scalariform.update 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.pde.UpdateSiteBuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.pde.UpdateSiteNature 16 | 17 | 18 | -------------------------------------------------------------------------------- /scalariform/notes/0.1.5.markdown: -------------------------------------------------------------------------------- 1 | * FIX: Type formatter crash on multiline types 2 | * Track scalac parser -- allow trait-position parents to have parameter blocks 3 | * Sync changes to InferredSemicolonParser 4 | * FIX: Scaladoc formatting wasn't idempotent (issue #62) 5 | * Added "SpacesAroundMultiImports" option 6 | * Modified "AlignParameters" option to align by column -- see readme 7 | * Deprecated "PreserveDanglingCloseParenthesis" in favor of 8 | automatic parentheses level detection. 9 | -------------------------------------------------------------------------------- /scalariform/notes/0.0.6.markdown: -------------------------------------------------------------------------------- 1 | * Lexer performance tweaks 2 | * FIX: whitespace in XML end tags (e.g. ``) 3 | * Tweak multiline case clause indentation 4 | * FIX: indentation problem with xml in StatSeq 5 | * Disallow staircase expression breaks before commas and colons 6 | * FIX: incorrect indentation within multiple param clauses 7 | * Improved formatting for case blocks 8 | * Command line tool learned `--encoding=` option 9 | * FIX: honour previously-ignored formatXml preference 10 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/utils/Trimmed.scala: -------------------------------------------------------------------------------- 1 | package scalariform.utils 2 | 3 | object Trimmed { 4 | 5 | def apply(prefix: String, infix: String, suffix: String): String = prefix + infix + suffix 6 | 7 | def unapply(s: String): Option[(String, String, String)] = { 8 | val (prefix, rest) = s span Character.isWhitespace 9 | val (revSuffix, revInfix) = rest.reverse span Character.isWhitespace 10 | Some(prefix, revInfix.reverse, revSuffix.reverse) 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/lexer/TokenType.scala: -------------------------------------------------------------------------------- 1 | package scalariform.lexer 2 | 3 | case class TokenType(name: String, isXml: Boolean = false) { 4 | 5 | def isNewline = this == Tokens.NEWLINE || this == Tokens.NEWLINES 6 | 7 | def isKeyword = Tokens.KEYWORDS contains this 8 | 9 | def isComment = Tokens.COMMENTS contains this 10 | 11 | def isId = Tokens.IDS contains this 12 | 13 | def isLiteral = Tokens.LITERALS contains this 14 | 15 | override lazy val toString = name 16 | 17 | } 18 | -------------------------------------------------------------------------------- /scalariform/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Bundle-ManifestVersion: 2 3 | Bundle-Name: Scalariform 4 | Bundle-SymbolicName: scalariform 5 | Bundle-Version: 0.1.8.qualifier 6 | Require-Bundle: org.scala-lang.scala-library, 7 | org.scala-lang.modules.scala-xml 8 | Bundle-ClassPath: . 9 | Export-Package: scalariform, 10 | scalariform.astselect, 11 | scalariform.formatter, 12 | scalariform.formatter.preferences, 13 | scalariform.lexer, 14 | scalariform.parser, 15 | scalariform.utils 16 | Bundle-RequiredExecutionEnvironment: J2SE-1.5 17 | -------------------------------------------------------------------------------- /scalariform/notes/0.0.7.markdown: -------------------------------------------------------------------------------- 1 | * Rewrite parser; formatter is now ~60% faster 2 | * Add IndentPackageBlocks formatting preference 3 | * Allow newline before self type declaration 4 | * FIX: avoid abutting `@` and an operator, otherwise it merges into a single identifer 5 | * FIX: formatting for newline between `private[foo]` and trait/class/def etc 6 | * Update parser to match changes from Scala trac #3672 (types on implicit parameters in function expressions) 7 | * FIX: avoid hash and operator merging (e.g. `b[c# ::[d]]`) 8 | * Tweaks to else clause formatting 9 | -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | Scalariform is maintained by Matt Russell (https://github.com/mdr/scalariform). 2 | 3 | * A command line option to read a list of files was contributed by Olivier Michallat (https://github.com/olim7t). 4 | * The Maven formatter plugin was written by Adam Crain (https://github.com/jadamcrain). 5 | * Better Vim integration via a command line option to return the input unchanged on parse error was contributed by Oliver Braun (https://github.com/obcode). 6 | * CompactControlReadability patch by Owein Reese (https://github.com/wheaties) and Rose Toomey (https://github.com/rktoomey) 7 | -------------------------------------------------------------------------------- /scalariform.feature/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | scalariform.feature 7 | eclipse-feature 8 | 0.1.8-SNAPSHOT 9 | 10 | scalariform.parent 11 | org.scalariform 12 | 0.1.8-SNAPSHOT 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /scalariform.update/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | scalariform.update 7 | eclipse-update-site 8 | 0.1.8-SNAPSHOT 9 | 10 | scalariform.parent 11 | com.danieltrinh 12 | 0.1.8-SNAPSHOT 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /scalariform/notes/0.1.2.markdown: -------------------------------------------------------------------------------- 1 | * Revamp command-line tool with more intuitive behaviour 2 | * Add `--quiet`, `--recurse`, `--stdin`, `--stdout` options to command-line tool 3 | * FIX: Scaladoc comment formatting could break nested comments (issue #36) 4 | * Tidy up, optimise lexer code 5 | * FIX: Parse `5.f`, `5.d` as floating points, unless in Scala 2.11+ mode 6 | * FIX: Bug with line-per-annotation style 7 | * Add support for String interpolation 8 | * Add support for macros 9 | * Add `--scalaVersion=` flag to command-line tool 10 | * Support `expr[T1, T2][T3, T4]` and `g()[String]` syntaxes 11 | * Fix AST selection for prefix expression 12 | -------------------------------------------------------------------------------- /formatterPreferences.properties: -------------------------------------------------------------------------------- 1 | #Scalariform formatter preferences 2 | #Fri Apr 01 21:09:37 BST 2011 3 | alignParameters=true 4 | compactStringConcatenation=false 5 | indentPackageBlocks=true 6 | formatXml=true 7 | preserveSpaceBeforeArguments=false 8 | doubleIndentClassDeclaration=false 9 | rewriteArrowSymbols=false 10 | alignSingleLineCaseStatements=true 11 | alignSingleLineCaseStatements.maxArrowIndent=40 12 | spaceBeforeColon=false 13 | spaceInsideBrackets=false 14 | spaceInsideParentheses=false 15 | preserveDanglingCloseParenthesis=false 16 | indentSpaces=2 17 | indentLocalDefs=false 18 | spacesWithinPatternBinders=true 19 | spacesAroundMultiImports=true -------------------------------------------------------------------------------- /scalariform/notes/0.0.8.markdown: -------------------------------------------------------------------------------- 1 | * FIX: Correctly handle use of `+`/`-` as type parameters in defs 2 | * Add Maven formatter plugin (contributed by Adam Crain -- [Adam Crain][1]) 3 | * FIX: Bug with lexing `"""` at end of text 4 | * Add `AlignSingleLineCaseStatements` preferences to align the arrows of consecutive single-line case statements 5 | * Add `IndentLocalDefs` preference to indent local methods an extra level 6 | * Track parser changes in Scala 2.9: 7 | * Type variables in a constructor pattern match 8 | * Generalised catch clause 9 | * Mirror code reorganisation of scala.tools.nsc.ast.parser.Parsers 10 | 11 | [1]: https://github.com/jadamcrain 12 | -------------------------------------------------------------------------------- /scalariform/notes/0.1.0.markdown: -------------------------------------------------------------------------------- 1 | * Add forgiving mode to lexer 2 | * Add `SpaceInsideParentheses` and `SpaceInsideBrackets` preferences (issue #14) 3 | * Ability to import/export preferences as properties; `--preferencesFile=` command-line option 4 | * FIX: incorrect indent behaviour for finally block after a catch block 5 | * Add `SpacesWithinPatternBinders` preference (issue #15) 6 | * Add `MultilineScaladocCommentsStartOnFirstLine` preference (issue #16) 7 | * FIX: infinite loop in lexer on malformed XML processing instructions / unparsed 8 | * Add IndentWithTabs preference (issue #17) 9 | * FIX: Fix incorrectly parsed command line argument (issue #20) 10 | * Update for 2.9.0 11 | -------------------------------------------------------------------------------- /scalariform/src/test/scala/com/danieltrinh/scalariform/formatter/RewriteArrowsTest.scala: -------------------------------------------------------------------------------- 1 | package scalariform.formatter 2 | 3 | import scalariform.parser._ 4 | import scalariform.formatter._ 5 | import scalariform.formatter.preferences._ 6 | 7 | // format: OFF 8 | class RewriteArrowsTest extends AbstractExpressionFormatterTest { 9 | 10 | { 11 | implicit val formattingPreferences = FormattingPreferences.setPreference(RewriteArrowSymbols, true) 12 | 13 | "(a: Int) => 3" ==> "(a: Int) ⇒ 3" 14 | "for (i <- 1 to 10) yield i" ==> "for (i ← 1 to 10) yield i" 15 | "cache += k -> f(k)" ==> "cache += k → f(k)" 16 | } 17 | 18 | override val debug = false 19 | 20 | } 21 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/utils/CaseClassReflector.scala: -------------------------------------------------------------------------------- 1 | package scalariform.utils 2 | 3 | trait CaseClassReflector extends Product { 4 | 5 | def getFields: List[(String, Any)] = { 6 | val names = getClass.getDeclaredFields map { _.getName } 7 | names.toList zip productIterator.toList 8 | } 9 | 10 | private def getFieldsOld: List[(String, Any)] = { 11 | var fieldValueToName: Map[Any, String] = Map() 12 | for (field ← getClass.getDeclaredFields) { 13 | field.setAccessible(true) 14 | fieldValueToName += (field.get(this) -> field.getName) 15 | } 16 | productIterator.toList map { value ⇒ fieldValueToName(value) -> value } 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /scalariform/src/test/scala/com/danieltrinh/scalariform/formatter/ScriptFormatterTest.scala: -------------------------------------------------------------------------------- 1 | package scalariform.formatter 2 | 3 | import scalariform.parser._ 4 | import scalariform.formatter._ 5 | 6 | // format: OFF 7 | class ScriptFormatterTest extends AbstractFormatterTest { 8 | 9 | """println("Hello world")""" ==> """println("Hello world")""" 10 | 11 | """def sayHi() { println("Hello") 12 | |}""" ==> 13 | """def sayHi() { 14 | | println("Hello") 15 | |}""" 16 | 17 | override val debug = false 18 | 19 | type Result = CompilationUnit 20 | 21 | def parse(parser: ScalaParser) = parser.scriptBody() 22 | 23 | def format(formatter: ScalaFormatter, result: Result) = formatter.format(result)(FormatterState()) 24 | 25 | } 26 | -------------------------------------------------------------------------------- /scalariform/src/test/scala/com/danieltrinh/scalariform/utils/TrimmedTest.scala: -------------------------------------------------------------------------------- 1 | package scalariform.utils 2 | 3 | import org.scalatest.FlatSpec 4 | import org.scalatest.matchers.ShouldMatchers 5 | 6 | // format: +preserveSpaceBeforeArguments 7 | class TrimmedTest extends FlatSpec with ShouldMatchers { 8 | 9 | it should "just work" in { 10 | 11 | Trimmed.unapply("").get should equal ("", "", "") 12 | Trimmed.unapply("foo").get should equal ("", "foo", "") 13 | Trimmed.unapply(" foo").get should equal (" ", "foo", "") 14 | Trimmed.unapply(" foo ").get should equal (" ", "foo", " ") 15 | Trimmed.unapply(" foo bar ").get should equal (" ", "foo bar", " ") 16 | Trimmed.unapply(" ").get should equal (" ", "", "") 17 | 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /scalariform.update/web/site.css: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /scalariform/notes/0.0.9.markdown: -------------------------------------------------------------------------------- 1 | * Add support for AST selection 2 | * Improve basic formatting of Scaladoc comments 3 | * Improve stairway indenting of argument lists, infix exprs 4 | * Add preference `PreserveDanglingCloseParenthesis` to keep newlines before a dangling right paren (issue #6) 5 | * Allow single-expression case clauses to be formatted adjacent to the case arrow (issue #7) 6 | * Improve formatting after a single-line comment (issue #8) 7 | * Allow missing param types for `-Yinfer-argument-types` (issue #9) 8 | * FIX: erroneous spaces after `type` (issue #10) 9 | * Add `--forceOutput` command-line argument to pass input through untouched if there is a parse error (#issue 11/12) 10 | * Pull out separate, non-`System.exit()`ing `process` method separate to `main()` (issue #13) 11 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/lexer/ScalaLexerReader.scala: -------------------------------------------------------------------------------- 1 | package scalariform.lexer 2 | 3 | import scalariform.lexer.Tokens._ 4 | import scala.util.parsing.input._ 5 | 6 | class ScalaLexerReader(val tokens: List[Token]) extends Reader[Token] { 7 | 8 | def first: Token = tokens.head 9 | 10 | def rest: Reader[Token] = new ScalaLexerReader(tokens.tail) 11 | 12 | def pos: Position = new ScalaLexerPosition(first) 13 | 14 | def atEnd: Boolean = tokens.isEmpty || tokens.head.tokenType == EOF 15 | 16 | private class ScalaLexerPosition(token: Token) extends Position { 17 | 18 | def line: Int = -1 19 | 20 | def column: Int = -1 21 | 22 | protected def lineContents: String = token.rawText 23 | 24 | override def longString = lineContents 25 | 26 | } 27 | 28 | } 29 | 30 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/lexer/HiddenToken.scala: -------------------------------------------------------------------------------- 1 | package scalariform.lexer 2 | 3 | abstract sealed class HiddenToken(val token: Token) { 4 | 5 | lazy val newlineful = token.text contains '\n' 6 | 7 | def text = token.text 8 | 9 | def rawText = token.rawText 10 | 11 | } 12 | 13 | case class Whitespace(override val token: Token) extends HiddenToken(token) 14 | 15 | sealed abstract class Comment(token: Token) extends HiddenToken(token) 16 | 17 | object Comment { 18 | 19 | def unapply(comment: Comment) = Some(comment.token) 20 | 21 | } 22 | 23 | case class SingleLineComment(override val token: Token) extends Comment(token) 24 | 25 | case class MultiLineComment(override val token: Token) extends Comment(token) 26 | 27 | case class ScalaDocComment(override val token: Token) extends Comment(token) 28 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/utils/Range.scala: -------------------------------------------------------------------------------- 1 | package scalariform.utils 2 | 3 | import scala.math.Ordering 4 | 5 | case class Range(offset: Int, length: Int) { 6 | 7 | def contains(other: Range) = other.offset >= offset && other.offset + other.length <= offset + length 8 | 9 | def strictlyContains(other: Range) = (this contains other) && this.length > other.length 10 | 11 | /** 12 | * @return the smallest range that contains both this and other 13 | */ 14 | def mergeWith(other: Range) = { 15 | val List(earliest, latest) = List(this, other) sortBy (_.offset) 16 | Range(earliest.offset, latest.offset - earliest.offset + latest.length) 17 | } 18 | 19 | def intersects(other: Range) = 20 | !(other.offset >= offset + length || other.offset + other.length - 1 < offset) 21 | 22 | def expandLeft(n: Int): Range = Range(offset - n, length + n) 23 | 24 | } 25 | -------------------------------------------------------------------------------- /scalariform/src/test/scala/com/danieltrinh/scalariform/parser/ScalaParserTest.scala: -------------------------------------------------------------------------------- 1 | package scalariform.parser 2 | 3 | import scalariform.lexer._ 4 | import scalariform.parser._ 5 | 6 | import org.scalatest.FlatSpec 7 | import org.scalatest.matchers.ShouldMatchers 8 | 9 | // format: +preserveSpaceBeforeArguments 10 | class ScalaParserTest extends FlatSpec with ShouldMatchers { 11 | 12 | "Parser" should "not throw exception" in { 13 | // parseExpression("class X") 14 | // parseExpression("class X { }") 15 | // parseExpression("class X { def method(n: Int) = 42 }") 16 | parseExpression { 17 | """ 18 | class C(@annotation(foo = {1 + 2}) n: Int) 19 | """ 20 | } 21 | 22 | } 23 | 24 | private def parseExpression(s: String) = { 25 | val tokens = ScalaLexer.tokenise(s) 26 | val scalaParser = new ScalaParser(tokens.toArray) 27 | scalaParser.compilationUnit() 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /cli/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/lexer/LexerMode.scala: -------------------------------------------------------------------------------- 1 | package scalariform.lexer 2 | 3 | sealed trait LexerMode 4 | 5 | class ScalaMode extends LexerMode { 6 | 7 | private var braceNestLevel: Int = 0 8 | 9 | def nestBrace() { braceNestLevel += 1 } 10 | 11 | def unnestBrace(): Int = { 12 | braceNestLevel -= 1 13 | braceNestLevel 14 | } 15 | 16 | } 17 | 18 | class XmlMode extends LexerMode { 19 | 20 | var isTagMode: Boolean = false 21 | 22 | var tagState: TagState = Normal 23 | 24 | private var tagNestLevel: Int = 0 25 | 26 | def nestTag() { tagNestLevel += 1 } 27 | 28 | def unnestTag(): Int = { 29 | tagNestLevel -= 1 30 | tagNestLevel 31 | } 32 | 33 | def nestingLevel = tagNestLevel 34 | 35 | } 36 | 37 | class StringInterpolationMode(val multiLine: Boolean) extends LexerMode { 38 | 39 | var initialSegment = true 40 | 41 | var interpolationVariable = false 42 | 43 | } 44 | -------------------------------------------------------------------------------- /cli/src/main/scala/com/danieltrinh/scalariform/commandline/ScalaFileWalker.scala: -------------------------------------------------------------------------------- 1 | package scalariform.commandline 2 | 3 | import java.io.File 4 | import java.util.{ ArrayList, Collection } 5 | import scala.collection.JavaConversions._ 6 | import scala.collection.mutable.Buffer 7 | 8 | import org.apache.commons.io._ 9 | import org.apache.commons.io.filefilter._ 10 | 11 | object ScalaFileWalker extends DirectoryWalker(TrueFileFilter.INSTANCE, FileFilterUtils.suffixFileFilter(".scala"), -1) { 12 | 13 | def findScalaFiles(path: String): List[File] = findScalaFiles(new File(path)) 14 | 15 | def findScalaFiles(path: File): List[File] = { 16 | val results = new ArrayList[File] 17 | walk(path, results) 18 | results.toList 19 | } 20 | 21 | override protected def handleFile(file: File, depth: Int, results: Collection[_]) { 22 | val castResults = results.asInstanceOf[Collection[File]] 23 | castResults.add(file) 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /scalariform/notes/0.1.1.markdown: -------------------------------------------------------------------------------- 1 | * FIX: Leave PCDATA whitespace untouched inside a single-line XML tag (issue #27) 2 | * FIX: indent for `indentLocalDefs` on first line of function block (issue #24) 3 | * `ParenExpr` now allows newline after opening paren (issue #18) 4 | * FIX: spurious indentation in staggered dot expressions (issue #25) 5 | * Preserve newline before annotations (issue #28) 6 | * Add option to support `CompactControlReadability` style (issue #22) (thanks to by Owein Reese (https://github.com/wheaties) and Rose Toomey (https://github.com/rktoomey) for the patch 7 | * Preserve newline before anonymous function argument (issue #21) 8 | * Allow one-line anonymous function blocks 9 | * Fix parser crash on argument-less constructor annotations 10 | * Add `PlaceScaladocAsterisksBeneathSecondAsterisk` preference to conform to recommended Scaladoc style (issue #30) 11 | * FIX: Removal of space causing token merge in varargs and unary ops (http://scala-ide-portfolio.assembla.com/spaces/scala-ide/tickets/1000601) 12 | * Switch to sbt 0.11 build 13 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/formatter/AnnotationFormatter.scala: -------------------------------------------------------------------------------- 1 | package scalariform.formatter 2 | 3 | import scalariform.parser._ 4 | import scalariform.utils._ 5 | import scalariform.lexer._ 6 | import scalariform.formatter.preferences._ 7 | 8 | trait AnnotationFormatter { self: HasFormattingPreferences with TypeFormatter with ExprFormatter ⇒ 9 | 10 | def format(annotation: Annotation)(implicit formatterState: FormatterState): FormatResult = { 11 | val Annotation(atToken: Token, annotationType: Type, argumentExprss: List[ArgumentExprs], newlineOption: Option[Token]) = annotation 12 | var formatResult: FormatResult = NoFormatResult 13 | 14 | formatResult = formatResult.before(annotationType.firstToken, Compact) 15 | formatResult ++= format(annotationType) 16 | for (argumentExprs ← argumentExprss) 17 | formatResult ++= format(argumentExprs)._1 18 | for (newline ← newlineOption) 19 | formatResult = formatResult.formatNewline(newline, Compact) // TODO: rethink 20 | formatResult 21 | } 22 | 23 | } 24 | 25 | -------------------------------------------------------------------------------- /scalariform/src/test/scala/com/danieltrinh/scalariform/formatter/MiscFormatterTest.scala: -------------------------------------------------------------------------------- 1 | package scalariform.formatter 2 | 3 | import scalariform.parser.{FullDefOrDcl, ScalaParser} 4 | 5 | class MiscFormatterTest extends AbstractFormatterTest { 6 | 7 | """class Foo( 8 | | bar: String, 9 | | baz: String 10 | |)""" ==> 11 | """class Foo( 12 | | bar: String, 13 | | baz: String 14 | |)""" 15 | 16 | """class Foo( 17 | | bar: String, 18 | | baz: String)""" ==> 19 | """class Foo( 20 | | bar: String, 21 | | baz: String 22 | |)""" 23 | 24 | """class Foo( 25 | |)""" ==> 26 | """class Foo()""" 27 | 28 | """class a()""" ==> 29 | """class a()""" 30 | 31 | """def a()""" ==> 32 | """def a()""" 33 | 34 | """class a(b: Int)""" ==> 35 | """class a(b: Int)""" 36 | 37 | """def a(b: Int)""" ==> 38 | """def a(b: Int)""" 39 | 40 | def parse(parser: ScalaParser) = parser.nonLocalDefOrDcl() 41 | 42 | type Result = FullDefOrDcl 43 | 44 | def format(formatter: ScalaFormatter, result: Result) = formatter.format(result)(FormatterState(indentLevel = 0)) 45 | 46 | } 47 | -------------------------------------------------------------------------------- /scalariform/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | org.scalariform 6 | scalariform 7 | 0.1.8-SNAPSHOT 8 | eclipse-plugin 9 | 10 | scalariform.parent 11 | org.scalariform 12 | 0.1.8-SNAPSHOT 13 | 14 | 15 | 16 | 17 | 18 | 19 | org.eclipse.tycho 20 | tycho-compiler-plugin 21 | ${tycho.version} 22 | 23 | 24 | **/*.scala 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2010 Matthew D. Russell 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and 10 | to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 18 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 20 | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/lexer/Chars.scala: -------------------------------------------------------------------------------- 1 | package scalariform.lexer 2 | 3 | import scala.annotation.switch 4 | 5 | object Chars { 6 | 7 | /** 8 | * @see scala.reflect.internal.Chars.isOperatorPart 9 | */ 10 | def isOperatorPart(c: Char): Boolean = 11 | (c: @switch) match { 12 | case '~' | '!' | '@' | '#' | '%' | 13 | '^' | '*' | '+' | '-' | '<' | 14 | '>' | '?' | ':' | '=' | '&' | 15 | '|' | '/' | '\\' ⇒ true 16 | case c ⇒ isSpecial(c) 17 | } 18 | 19 | /** 20 | * @see scala.reflect.internal.Chars.isSpecial 21 | */ 22 | def isSpecial(c: Char) = { 23 | val chtp = Character.getType(c) 24 | chtp == Character.MATH_SYMBOL.toInt || chtp == Character.OTHER_SYMBOL.toInt 25 | } 26 | 27 | /** 28 | * @see scala.reflect.internal.Chars.isIdentifierStart 29 | */ 30 | def isIdentifierStart(c: Char) = 31 | (c == '_') || (c == '$') || Character.isUnicodeIdentifierStart(c) 32 | 33 | /** 34 | * @see scala.reflect.internal.Chars.isIdentifierPart 35 | */ 36 | def isIdentifierPart(c: Char) = 37 | (c == '$') || Character.isUnicodeIdentifierPart(c) && c != CharConstants.SU 38 | 39 | } 40 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/formatter/FormatterDirectiveParser.scala: -------------------------------------------------------------------------------- 1 | package scalariform.formatter 2 | import scala.util.parsing.input._ 3 | import scala.util.parsing.combinator._ 4 | 5 | class FormatterDirectiveParser extends JavaTokenParsers { 6 | 7 | val directives: Parser[List[FormatterDirective]] = "format:" ~> 8 | ("ON" ^^^ List(ToggleFormatting(true)) | "OFF" ^^^ List(ToggleFormatting(false)) | repsep(toggle, ",")) 9 | 10 | val plusOrMinus = "+" ^^^ true | "-" ^^^ false 11 | 12 | val toggle = plusOrMinus ~ ident ^^ { case onOrOff ~ optionName ⇒ ToggleOption(onOrOff, optionName) } 13 | 14 | def getDirectives(s: String) = parse(directives, s) getOrElse Nil 15 | } 16 | 17 | object FormatterDirectiveParser { 18 | def getDirectives(s: String): List[FormatterDirective] = { 19 | val index = s indexOf "format:" 20 | if (index == -1) 21 | Nil 22 | else 23 | new FormatterDirectiveParser().getDirectives(s.substring(index)) 24 | } 25 | } 26 | 27 | sealed trait FormatterDirective 28 | 29 | case class ToggleOption(onOrOff: Boolean, optionName: String) extends FormatterDirective 30 | case class ToggleFormatting(onOrOff: Boolean) extends FormatterDirective 31 | -------------------------------------------------------------------------------- /misc/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/formatter/FormatterState.scala: -------------------------------------------------------------------------------- 1 | package scalariform.formatter 2 | 3 | import scalariform.lexer.Token 4 | 5 | case class FormatterState( 6 | indentLevel: Int = 0, 7 | indentRelativeToTokenOption: Option[Token] = None, 8 | val inSingleLineBlock: Boolean = false, 9 | val expressionBreakHappened: Boolean = false 10 | ) { 11 | 12 | private val nextIndentLevel = indentLevel + 1 13 | 14 | def indent: FormatterState = indent(1) 15 | 16 | def indent(n: Int): FormatterState = copy(indentLevel = indentLevel + n) 17 | 18 | def alignWithToken(token: Token): FormatterState = copy(indentLevel = 0, indentRelativeToTokenOption = Some(token)) 19 | 20 | def nextIndentLevelInstruction = EnsureNewlineAndIndent(nextIndentLevel, relativeTo = indentRelativeToTokenOption) 21 | 22 | def currentIndentLevelInstruction = EnsureNewlineAndIndent(indentLevel, relativeTo = indentRelativeToTokenOption) 23 | 24 | def indentForExpressionBreak = indent.copy(expressionBreakHappened = true) 25 | 26 | def indentForExpressionBreakIfNeeded = if (expressionBreakHappened) this else indent.copy(expressionBreakHappened = true) 27 | 28 | def clearExpressionBreakHappened = copy(expressionBreakHappened = false) 29 | 30 | } 31 | -------------------------------------------------------------------------------- /scalariform/src/test/scala/com/danieltrinh/scalariform/formatter/FunctionFormatterTest.scala: -------------------------------------------------------------------------------- 1 | package scalariform.formatter 2 | 3 | import scalariform.parser.{CompilationUnit, ScalaParser} 4 | 5 | // format: OFF 6 | class FunctionFormatterTest extends AbstractFormatterTest { 7 | 8 | "val f = x => x" ==> "val f = x => x" 9 | "val f: Int => Int => Unit = a => b => ()" ==> "val f: Int => Int => Unit = a => b => ()" 10 | 11 | "{ ctx => j => () }" ==> "{ ctx => j => () }" 12 | "fun { ctx => j => () }" ==> "fun { ctx => j => () }" 13 | "Thing() { ctx => j => () }" ==> "Thing() { ctx => j => () }" 14 | 15 | """{ ctx => 16 | | ??? 17 | |}""".stripMargin ==> 18 | """{ ctx => 19 | | ??? 20 | |}""".stripMargin 21 | 22 | """{ ctx => j => 23 | | ??? 24 | |}""".stripMargin ==> 25 | """{ ctx => j => 26 | | ??? 27 | |}""".stripMargin 28 | 29 | """val x = { ctx => j => 30 | | one() 31 | | two() 32 | |}""".stripMargin ==> 33 | """val x = { ctx => j => 34 | | one() 35 | | two() 36 | |}""".stripMargin 37 | 38 | override val debug = false 39 | 40 | type Result = CompilationUnit 41 | 42 | def parse(parser: ScalaParser) = parser.compilationUnitOrScript() 43 | 44 | def format(formatter: ScalaFormatter, result: Result) = formatter.format(result)(FormatterState()) 45 | 46 | } 47 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/lexer/Token.scala: -------------------------------------------------------------------------------- 1 | package scalariform.lexer 2 | 3 | import scalariform.lexer.Tokens._ 4 | import scalariform.utils.Range 5 | 6 | /** 7 | * A token of Scala source. 8 | * 9 | * @param text -- the text associated with the token after unicode escaping 10 | * @param rawText -- the text associated with the token before unicode escaping 11 | */ 12 | case class Token(tokenType: TokenType, text: String, offset: Int, rawText: String) { 13 | 14 | private[lexer] var associatedWhitespaceAndComments_ : HiddenTokens = null 15 | 16 | private[lexer] var containsUnicodeEscape = false 17 | 18 | def associatedWhitespaceAndComments: HiddenTokens = associatedWhitespaceAndComments_ 19 | 20 | def length = rawText.length 21 | 22 | def range = Range(offset, length) 23 | 24 | def lastCharacterOffset = offset + length - 1 25 | 26 | def isScalaDocComment = tokenType == MULTILINE_COMMENT && text.startsWith("/**") && text != "/**/" 27 | 28 | def isNewline = tokenType.isNewline 29 | 30 | @deprecated(message = "Use text instead" /*, since = "0.1.2"*/ ) 31 | def getText = text 32 | 33 | @deprecated(message = "Use offset instead" /*, since = "0.1.2"*/ ) 34 | def startIndex = offset 35 | 36 | @deprecated(message = "Use lastCharacterOffset instead" /*, since = "0.1.2"*/ ) 37 | def stopIndex = lastCharacterOffset 38 | 39 | } 40 | -------------------------------------------------------------------------------- /scalariform/src/test/scala/com/danieltrinh/scalariform/formatter/StringInterpolationFormatterTest.scala: -------------------------------------------------------------------------------- 1 | package scalariform.formatter 2 | 3 | import scalariform.parser._ 4 | import scalariform.formatter._ 5 | import scalariform.formatter.preferences._ 6 | 7 | // format: OFF 8 | class StringInterpolationFormatterTest extends AbstractExpressionFormatterTest { 9 | 10 | implicit val scalaVersion: String = "2.10.0" 11 | 12 | s"foo".text ==> s"foo".text 13 | s"".text ==> s"".text 14 | s"my name is $name".text ==> s"my name is $name".text 15 | s"my name is $this".text ==> s"my name is $this".text 16 | """s"my name is ${bob}"""" ==> """s"my name is ${bob}"""" 17 | """s"my name is ${ person.name }"""" ==> """s"my name is ${person.name}"""" 18 | 19 | """s"my name is ${ 20 | |bob}"""" ==> 21 | """s"my name is ${ 22 | | bob 23 | |}"""" 24 | 25 | """s"my name is ${ 26 | |val person = getPerson() 27 | |person.getName}"""" ==> 28 | """s"my name is ${ 29 | | val person = getPerson() 30 | | person.getName 31 | |}"""" 32 | 33 | s"""foo""".text ==> s"""foo""".text 34 | s"""""".text ==> s"""""".text 35 | s"""my name is $name""".text ==> s"""my name is $name""".text 36 | "s\"\"\"my name is ${bob}\"\"\"" ==> "s\"\"\"my name is ${bob}\"\"\"" 37 | "s\"\"\"my name is ${ person.name }\"\"\"" ==> "s\"\"\"my name is ${person.name}\"\"\"" 38 | 39 | } 40 | -------------------------------------------------------------------------------- /scalariform/src/test/scala/com/danieltrinh/scalariform/formatter/PackageFormatterTest.scala: -------------------------------------------------------------------------------- 1 | package scalariform.formatter 2 | 3 | import scalariform.parser._ 4 | import scalariform.formatter._ 5 | import scalariform.formatter.preferences._ 6 | 7 | // format: OFF 8 | class PackageFormatterTest extends AbstractFormatterTest { 9 | 10 | override val debug = false 11 | 12 | type Result = CompilationUnit 13 | 14 | def parse(parser: ScalaParser) = parser.compilationUnit() 15 | 16 | def format(formatter: ScalaFormatter, result: Result) = formatter.format(result)(FormatterState()) 17 | 18 | "" ==> "" 19 | 20 | "package foo . bar . baz" ==> "package foo.bar.baz" 21 | 22 | """package foo { 23 | |package bar { 24 | |class Baz 25 | |} 26 | |}""" ==> 27 | """package foo { 28 | | package bar { 29 | | class Baz 30 | | } 31 | |}""" 32 | 33 | "package foo" ==> "package foo" 34 | 35 | """/* foo */ 36 | |package wibble""" ==> 37 | """/* foo */ 38 | |package wibble""" 39 | 40 | """package a 41 | |{}""" ==> 42 | """package a {}""" 43 | 44 | """package a {} 45 | |""" ==> 46 | """package a {} 47 | |""" 48 | 49 | { 50 | 51 | implicit val formattingPreferences = FormattingPreferences.setPreference(IndentPackageBlocks, false) 52 | 53 | """package foo { 54 | |package bar { 55 | |class Baz 56 | |} 57 | |}""" ==> 58 | """package foo { 59 | |package bar { 60 | |class Baz 61 | |} 62 | |}""" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /scalariform/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/ScalaVersions.scala: -------------------------------------------------------------------------------- 1 | package scalariform 2 | 3 | import scala.util.Properties 4 | import scalariform.utils.Utils._ 5 | import scala.math.Ordering 6 | 7 | object ScalaVersion { 8 | 9 | private val VersionPattern = """(\d+)\.(\d+)\.(.*)""".r 10 | 11 | def parseOrDefault(s: String): ScalaVersion = parse(s).getOrElse(ScalaVersions.DEFAULT) 12 | 13 | def parse(s: String): Option[ScalaVersion] = 14 | s match { 15 | case VersionPattern(majorStr, minorStr, extra) ⇒ 16 | for { 17 | major ← majorStr.toIntOpt 18 | minor ← minorStr.toIntOpt 19 | } yield ScalaVersion(major, minor, extra) 20 | case _ ⇒ 21 | None 22 | } 23 | 24 | } 25 | 26 | case class ScalaVersion(major: Int, minor: Int, extra: String = "") extends Ordered[ScalaVersion] { 27 | 28 | private def majorMinor = (major, minor) 29 | 30 | def compare(that: ScalaVersion) = Ordering[(Int, Int)].compare(this.majorMinor, that.majorMinor) 31 | 32 | override def toString = major + "." + minor + "." + extra 33 | 34 | } 35 | 36 | object ScalaVersions { 37 | 38 | val Scala_2_11 = ScalaVersion.parse("2.11.0").get 39 | val Scala_2_10 = ScalaVersion.parse("2.10.0").get 40 | val Scala_2_9 = ScalaVersion.parse("2.9.2").get 41 | val Scala_2_8 = ScalaVersion.parse("2.8.1").get 42 | 43 | lazy val DEFAULT_VERSION = Properties.scalaPropOrElse("version.number", "2.9.2") 44 | 45 | lazy val DEFAULT = ScalaVersion.parse(DEFAULT_VERSION).get 46 | 47 | } 48 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/lexer/RedundantSemicolonDetector.scala: -------------------------------------------------------------------------------- 1 | package scalariform.lexer 2 | 3 | import scalariform.utils.Range 4 | import scalariform.utils.Utils._ 5 | import scalariform.utils.TextEdit 6 | import scalariform.utils.TextEditProcessor 7 | import scalariform.ScalaVersions 8 | 9 | object RedundantSemicolonDetector { 10 | 11 | /** 12 | * @return all semicolons in the source that could safely be removed without changing the meaning 13 | * of the program. 14 | */ 15 | def findRedundantSemis(source: String, scalaVersion: String = ScalaVersions.DEFAULT_VERSION): List[Token] = { 16 | 17 | def isRedundant(semi: Token, index: Int): Boolean = { 18 | val sourceWithoutSemi = deleteRange(source, semi.range) 19 | val tokensWithoutSemi = ScalaLexer.tokenise(sourceWithoutSemi, forgiveErrors = true, scalaVersion = scalaVersion) 20 | val replacementToken = tokensWithoutSemi(index) 21 | replacementToken.isNewline || replacementToken.tokenType == Tokens.EOF || replacementToken.tokenType == Tokens.RBRACE 22 | } 23 | 24 | ScalaLexer.tokenise(source, forgiveErrors = true, scalaVersion = scalaVersion).zipWithIndex.collect { 25 | case (token, index) if token.tokenType == Tokens.SEMI && isRedundant(token, index) ⇒ token 26 | } 27 | 28 | } 29 | 30 | def removeRedundantSemis(s: String): String = 31 | TextEditProcessor.runEdits(s, getEditsToRemoveRedundantSemis(s)) 32 | 33 | def getEditsToRemoveRedundantSemis(s: String): List[TextEdit] = 34 | findRedundantSemis(s).map(_.range).map(TextEdit.delete) 35 | 36 | } 37 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/lexer/ModeStack.scala: -------------------------------------------------------------------------------- 1 | package scalariform.lexer 2 | 3 | import scala.collection.mutable.Stack 4 | 5 | /** 6 | * Keeping track of nesting level of XML within Scala. 7 | */ 8 | trait ModeStack { self: ScalaLexer ⇒ 9 | 10 | private val modeStack = new Stack[LexerMode] 11 | 12 | private var currentRegionStart: Int = 0 13 | 14 | modeStack.push(new ScalaMode) 15 | 16 | protected def popMode() { 17 | val mode = modeStack.pop() 18 | } 19 | 20 | protected def isRootMode = modeStack.size == 1 21 | 22 | protected def switchToScalaModeAndFetchToken() { 23 | switchToScalaMode() 24 | fetchScalaToken() 25 | } 26 | 27 | protected def switchToXmlModeAndFetchToken() { 28 | modeStack.push(new XmlMode) 29 | fetchXmlToken() 30 | } 31 | 32 | protected def switchToStringInterpolationMode(multiLine: Boolean) { 33 | modeStack.push(new StringInterpolationMode(multiLine)) 34 | } 35 | 36 | protected def switchToScalaMode() { 37 | modeStack.push(new ScalaMode) 38 | } 39 | 40 | protected def isStringInterpolationMode = modeStack.head.isInstanceOf[StringInterpolationMode] 41 | 42 | protected def isXmlMode = modeStack.head.isInstanceOf[XmlMode] 43 | 44 | protected def isScalaMode = modeStack.head.isInstanceOf[ScalaMode] 45 | 46 | protected def xmlMode: XmlMode = modeStack.head.asInstanceOf[XmlMode] 47 | 48 | protected def scalaMode: ScalaMode = modeStack.head.asInstanceOf[ScalaMode] 49 | 50 | protected def stringInterpolationMode: StringInterpolationMode = modeStack.head.asInstanceOf[StringInterpolationMode] 51 | 52 | } 53 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/lexer/HiddenTokens.scala: -------------------------------------------------------------------------------- 1 | package scalariform.lexer 2 | 3 | import scalariform.lexer.Tokens._ 4 | import scalariform.utils.Utils 5 | 6 | object NoHiddenTokens extends HiddenTokens(Nil) 7 | 8 | case class HiddenTokens(tokens: List[HiddenToken]) extends Iterable[HiddenToken] { 9 | 10 | def removeInitialWhitespace = new HiddenTokens(tokens.dropWhile(_.isInstanceOf[Whitespace])) 11 | 12 | def iterator: Iterator[HiddenToken] = tokens.iterator 13 | 14 | val comments: List[Comment] = tokens collect { case comment: Comment ⇒ comment } 15 | 16 | val scalaDocComments: List[ScalaDocComment] = tokens collect { case comment @ ScalaDocComment(_) ⇒ comment } 17 | 18 | val whitespaces: List[Whitespace] = tokens collect { case whitespace @ Whitespace(_) ⇒ whitespace } 19 | 20 | def firstTokenOption = tokens.headOption 21 | 22 | def lastTokenOption = tokens.lastOption 23 | 24 | def containsNewline = text contains '\n' 25 | 26 | def containsComment = comments.nonEmpty 27 | 28 | def containsUnicodeEscape: Boolean = { 29 | for (token ← tokens if token.token.containsUnicodeEscape) 30 | return true 31 | false 32 | } 33 | 34 | lazy val text: String = { 35 | val sb = new StringBuilder 36 | for (token ← tokens) sb.append(token.text) 37 | // tokens.flatMap(_.token.text)(scala.collection.breakOut) 38 | sb.toString 39 | } 40 | lazy val rawText = tokens.map(_.token.rawText).mkString 41 | 42 | def rawTokens = tokens.map(_.token) 43 | 44 | def offset = tokens.head.token.offset 45 | 46 | def lastCharacterOffset = tokens.last.token.lastCharacterOffset 47 | 48 | } 49 | -------------------------------------------------------------------------------- /scalariform/src/test/scala/com/danieltrinh/scalariform/lexer/RedundantSemicolonDetectorTest.scala: -------------------------------------------------------------------------------- 1 | package scalariform.lexer 2 | 3 | import scalariform._ 4 | import scalariform.lexer.Tokens._ 5 | import org.scalatest.FlatSpec 6 | import org.scalatest.matchers.ShouldMatchers 7 | import org.scalatest.TestFailedException 8 | import org.scalatest.TestPendingException 9 | import scalariform.utils.Utils._ 10 | 11 | class RedundantSemicolonDetectorTest extends FlatSpec with ShouldMatchers { 12 | 13 | implicit def stringToCheckable(s: String)(implicit scalaVersion: String = ScalaVersions.DEFAULT_VERSION) = 14 | new { def check() = checkSemis(s, scalaVersion) }; // Expected redundant semicolons are indicated with <;> 15 | 16 | """ 17 | class A { 18 | def foo = 42<;> 19 | def bar = 123; def baz = 1234 20 | }<;> 21 | """.check(); 22 | 23 | """ 24 | { 25 | println("Foo")<;> 26 | } 27 | """.check(); 28 | 29 | """ 30 | class A { 31 | for ( 32 | x <- 1 to 10; 33 | y <- 1 to 10 34 | ) yield x + y<;> 35 | } 36 | """.check() 37 | 38 | { 39 | implicit val scalaVersion = "2.10.0"; 40 | """ 41 | s"my name is ${person.name<;>}" 42 | """.check 43 | } 44 | 45 | private def checkSemis(encodedSource: String, scalaVersion: String) { 46 | val ordinarySource = encodedSource.replace("<;>", ";") 47 | val semis = RedundantSemicolonDetector.findRedundantSemis(ordinarySource, scalaVersion) 48 | val encodedSourceAgain = semis.reverse.foldLeft(ordinarySource) { (s, semi) ⇒ replaceRange(s, semi.range, "<;>") } 49 | encodedSourceAgain should equal(encodedSource) 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /scalariform/src/test/scala/com/danieltrinh/scalariform/formatter/FormatterDirectiveParserTest.scala: -------------------------------------------------------------------------------- 1 | package scalariform.formatter 2 | 3 | import org.scalatest.FlatSpec 4 | import org.scalatest.matchers.ShouldMatchers 5 | import FormatterDirectiveParser.getDirectives 6 | 7 | // format: +preserveSpaceBeforeArguments 8 | class FormatterDirectiveParserTest extends FlatSpec with ShouldMatchers { 9 | 10 | it should "parse formatter ON/OFF instructions" in { 11 | "format: ON " ==> ToggleFormatting(true) 12 | "format: OFF" ==> ToggleFormatting(false) 13 | } 14 | 15 | it should "parse option toggle instructions" in { 16 | "format: +rewriteArrowSymbols" ==> ToggleOption(true, "rewriteArrowSymbols") 17 | "format: -rewriteArrowSymbols" ==> ToggleOption(false, "rewriteArrowSymbols") 18 | "format: -rewriteArrowSymbols, +spaceBeforeColon" ==> (ToggleOption(false, "rewriteArrowSymbols"), ToggleOption(true, "spaceBeforeColon")) 19 | } 20 | 21 | it should "parse despite surrounding junk" in { 22 | "// wibble wobble\nformat: OFF" ==> ToggleFormatting(false) 23 | "// wibble wobble\nformat: OFF\nwobblewobble" ==> ToggleFormatting(false) 24 | "// wibble wobble\nformat: OFFwobblewobble" ==> ToggleFormatting(false) 25 | "blahformat: -rewriteArrowSymbols, +spaceBeforeColon\nblah" ==> (ToggleOption(false, "rewriteArrowSymbols"), ToggleOption(true, "spaceBeforeColon")) 26 | } 27 | 28 | implicit def string2FormatTest(s: String): FormatTest = FormatTest(s.stripMargin) 29 | 30 | case class FormatTest(source: String) { 31 | def ==>(expectedDirectives: FormatterDirective*) { 32 | getDirectives(source) should be (expectedDirectives) 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/lexer/WhitespaceAndCommentsGrouper.scala: -------------------------------------------------------------------------------- 1 | package scalariform.lexer 2 | 3 | import scalariform.lexer.Tokens._ 4 | import scala.collection.mutable.ListBuffer 5 | 6 | class WhitespaceAndCommentsGrouper(lexer: ScalaLexer) extends Iterator[Token] { 7 | 8 | private var nextToken = lexer.next() 9 | 10 | private var ended = false 11 | 12 | private var hiddenTokens: HiddenTokens = _ 13 | 14 | def getHiddenTokens = hiddenTokens 15 | 16 | def hasNext = !ended 17 | 18 | private[lexer] def text = lexer.text 19 | 20 | def next() = { 21 | require(hasNext) 22 | hiddenTokens = readHiddenTokens() 23 | val resultToken = nextToken 24 | resultToken.associatedWhitespaceAndComments_ = hiddenTokens 25 | if (nextToken.tokenType == EOF) 26 | ended = true 27 | nextToken = lexer.next() 28 | resultToken 29 | } 30 | 31 | private def readHiddenTokens(): HiddenTokens = { 32 | val hiddenTokens = new ListBuffer[HiddenToken] 33 | while (isCommentOrWhitespace(nextToken)) { 34 | hiddenTokens += makeHiddenToken(nextToken) 35 | nextToken = lexer.next() 36 | } 37 | new HiddenTokens(hiddenTokens.toList) 38 | } 39 | 40 | private def isCommentOrWhitespace(token: Token) = token.tokenType match { 41 | case WS | LINE_COMMENT | MULTILINE_COMMENT ⇒ true 42 | case _ ⇒ false 43 | } 44 | 45 | private def makeHiddenToken(token: Token) = token.tokenType match { 46 | case LINE_COMMENT ⇒ SingleLineComment(token) 47 | case MULTILINE_COMMENT if token.isScalaDocComment ⇒ ScalaDocComment(token) 48 | case MULTILINE_COMMENT ⇒ MultiLineComment(token) 49 | case WS ⇒ Whitespace(token) 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/lexer/Keywords.scala: -------------------------------------------------------------------------------- 1 | package scalariform.lexer 2 | 3 | import scalariform.lexer.Tokens._ 4 | 5 | object Keywords { 6 | 7 | def apply(s: String): Option[TokenType] = keywords get s 8 | 9 | private val keywords = Map( 10 | "abstract" -> ABSTRACT, 11 | "case" -> CASE, 12 | "catch" -> CATCH, 13 | "class" -> CLASS, 14 | "def" -> DEF, 15 | "do" -> DO, 16 | "else" -> ELSE, 17 | "extends" -> EXTENDS, 18 | "false" -> FALSE, 19 | "final" -> FINAL, 20 | "finally" -> FINALLY, 21 | "for" -> FOR, 22 | "forSome" -> FORSOME, 23 | "if" -> IF, 24 | "implicit" -> IMPLICIT, 25 | "import" -> IMPORT, 26 | "lazy" -> LAZY, 27 | "match" -> MATCH, 28 | "new" -> NEW, 29 | "null" -> NULL, 30 | "object" -> OBJECT, 31 | "override" -> OVERRIDE, 32 | "package" -> PACKAGE, 33 | "private" -> PRIVATE, 34 | "protected" -> PROTECTED, 35 | "return" -> RETURN, 36 | "sealed" -> SEALED, 37 | "super" -> SUPER, 38 | "this" -> THIS, 39 | "throw" -> THROW, 40 | "trait" -> TRAIT, 41 | "try" -> TRY, 42 | "true" -> TRUE, 43 | "type" -> TYPE, 44 | "val" -> VAL, 45 | "var" -> VAR, 46 | "while" -> WHILE, 47 | "with" -> WITH, 48 | "yield" -> YIELD, 49 | "_" -> USCORE, 50 | ":" -> COLON, 51 | "=" -> EQUALS, 52 | "=>" -> ARROW, 53 | "<-" -> LARROW, 54 | "->" -> RARROW, 55 | "<:" -> SUBTYPE, 56 | "<%" -> VIEWBOUND, 57 | ">:" -> SUPERTYPE, 58 | "#" -> HASH, 59 | "@" -> AT, 60 | "." -> DOT, 61 | "+" -> PLUS, 62 | "-" -> MINUS, 63 | "*" -> STAR, 64 | "|" -> PIPE, 65 | "~" -> TILDE, 66 | "!" -> EXCLAMATION 67 | ) 68 | 69 | } 70 | -------------------------------------------------------------------------------- /scalariform/src/test/scala/com/danieltrinh/scalariform/formatter/ImportFormatterTest.scala: -------------------------------------------------------------------------------- 1 | package scalariform.formatter 2 | 3 | import scalariform.parser._ 4 | import scalariform.formatter._ 5 | import scalariform.formatter.preferences.{SpacesAroundMultiImports, FormattingPreferences} 6 | 7 | // format: OFF 8 | class ImportFormatterTest extends AbstractFormatterTest { 9 | 10 | { 11 | implicit val formattingPreferences = FormattingPreferences.setPreference( 12 | SpacesAroundMultiImports, true) 13 | 14 | "import foo . _" ==> "import foo._" 15 | "import foo . bar" ==> "import foo.bar" 16 | "import foo.{bar=>baz}" ==> "import foo.{ bar => baz }" 17 | "import foo.{bar=>baz},baz.biz" ==> "import foo.{ bar => baz }, baz.biz" 18 | """import foo.{bar => baz, 19 | |wibble => wobble}""" ==> 20 | """import foo.{ 21 | | bar => baz, 22 | | wibble => wobble 23 | |}""" 24 | } 25 | 26 | { 27 | implicit val formattingPreferences = FormattingPreferences.setPreference( 28 | SpacesAroundMultiImports, false) 29 | 30 | "import foo . _" ==> "import foo._" 31 | "import foo . bar" ==> "import foo.bar" 32 | "import foo.{bar=>baz}" ==> "import foo.{bar => baz}" 33 | "import foo.{bar=>baz},baz.biz" ==> "import foo.{bar => baz}, baz.biz" 34 | """import foo.{bar => baz, 35 | |wibble => wobble}""" ==> 36 | """import foo.{ 37 | | bar => baz, 38 | | wibble => wobble 39 | |}""" 40 | } 41 | 42 | override val debug = false 43 | 44 | type Result = CompilationUnit 45 | 46 | def parse(parser: ScalaParser) = parser.compilationUnit() 47 | 48 | def format(formatter: ScalaFormatter, result: Result) = formatter.format(result)(FormatterState()) 49 | 50 | } 51 | -------------------------------------------------------------------------------- /misc/src/main/scala/com/danieltrinh/scalariform/gui/TokenTable.scala: -------------------------------------------------------------------------------- 1 | package scalariform.gui 2 | 3 | import scalariform.formatter.FormatResult 4 | import javax.swing.JTable 5 | import scalariform.lexer.Token 6 | import javax.swing.table.AbstractTableModel 7 | import scalariform.utils.Range 8 | 9 | class TokenTable extends JTable(new TokenTableModel(Nil, FormatResult.EMPTY)) { 10 | 11 | setCellSelectionEnabled(false) 12 | setRowSelectionAllowed(true) 13 | setColumnSelectionAllowed(false) 14 | 15 | def setTokens(tokens: List[Token], formatResult: FormatResult = FormatResult.EMPTY) { 16 | val tableModel = new TokenTableModel(tokens, formatResult) 17 | setModel(tableModel) 18 | } 19 | 20 | override def getModel = super.getModel.asInstanceOf[TokenTableModel] 21 | 22 | def getSelectedToken: Option[Token] = 23 | getSelectedRow() match { 24 | case -1 ⇒ None 25 | case n ⇒ Some(getModel.tokens(n)) 26 | } 27 | 28 | } 29 | 30 | class TokenTableModel(val tokens: List[Token], formatResult: FormatResult) extends AbstractTableModel { 31 | 32 | def getColumnCount = 5 33 | 34 | def getRowCount = tokens.size 35 | 36 | def getValueAt(row: Int, col: Int): AnyRef = { 37 | val token = tokens(row) 38 | col match { 39 | case 0 ⇒ token.tokenType 40 | case 1 ⇒ token.text 41 | case 2 ⇒ token.offset.asInstanceOf[java.lang.Integer] 42 | case 3 ⇒ token.lastCharacterOffset.asInstanceOf[java.lang.Integer] 43 | case 4 ⇒ formatResult.predecessorFormatting.get(token) orElse formatResult.inferredNewlineFormatting.get(token) getOrElse "" 44 | } 45 | } 46 | 47 | override def getColumnName(col: Int) = col match { 48 | case 0 ⇒ "Type" 49 | case 1 ⇒ "Token text" 50 | case 2 ⇒ "Start" 51 | case 3 ⇒ "Finish" 52 | case 4 ⇒ "Instruction" 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/formatter/preferences/IFormattingPreferences.scala: -------------------------------------------------------------------------------- 1 | package scalariform.formatter.preferences 2 | 3 | trait IFormattingPreferences { 4 | 5 | def apply[T](preference: PreferenceDescriptor[T]): T 6 | 7 | def setPreference[T](preference: PreferenceDescriptor[T], value: T): IFormattingPreferences 8 | 9 | def preferencesMap: Map[PreferenceDescriptor[_], Any] 10 | 11 | def indentStyle: IndentStyle 12 | 13 | } 14 | 15 | abstract sealed class IndentStyle { 16 | def indent(n: Int): String 17 | protected def repeat(s: String, n: Int) = 1 to n map { _ ⇒ s } mkString 18 | } 19 | 20 | case object Tabs extends IndentStyle { 21 | def indent(indentLevel: Int) = repeat("\t", indentLevel) 22 | } 23 | 24 | case class Spaces(n: Int) extends IndentStyle { 25 | 26 | def indent(indentLevel: Int) = repeat(repeat(" ", n), indentLevel) 27 | 28 | def length(indentLevel: Int) = indent(indentLevel).length 29 | 30 | } 31 | 32 | class FormattingPreferences(val preferencesMap: Map[PreferenceDescriptor[_], Any]) extends IFormattingPreferences { 33 | 34 | def apply[T](preference: PreferenceDescriptor[T]): T = preferencesMap.get(preference) map { _.asInstanceOf[T] } getOrElse preference.defaultValue 35 | 36 | def setPreference[T](preference: PreferenceDescriptor[T], value: T) = new FormattingPreferences(preferencesMap + (preference -> value)) 37 | 38 | override def toString = getClass.getSimpleName + "(" + preferencesMap + ")" 39 | 40 | val indentStyle = if (this(IndentWithTabs)) Tabs else Spaces(this(IndentSpaces)) 41 | } 42 | 43 | case object FormattingPreferences extends FormattingPreferences(Map()) { 44 | 45 | def apply() = new FormattingPreferences(Map()) 46 | 47 | } 48 | 49 | trait HasFormattingPreferences { 50 | 51 | val formattingPreferences: IFormattingPreferences 52 | 53 | } 54 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/formatter/preferences/PreferencesImporterExporter.scala: -------------------------------------------------------------------------------- 1 | package scalariform.formatter.preferences 2 | 3 | import java.util.Properties 4 | import scala.collection.JavaConversions._ 5 | import scalariform.utils.Utils._ 6 | import java.io.IOException 7 | 8 | object PreferencesImporterExporter { 9 | 10 | /** 11 | * Pull preferences from a Properties store. 12 | * Errors are silently ignored. (TODO) 13 | */ 14 | def getPreferences(properties: Properties): IFormattingPreferences = { 15 | 16 | var preferences = FormattingPreferences() 17 | 18 | def setPreference[T](preferenceDescriptor: PreferenceDescriptor[T], valueString: String) = 19 | preferenceDescriptor.preferenceType.parseValue(valueString) match { 20 | case Left(error) ⇒ 21 | case Right(value) ⇒ preferences = preferences.setPreference(preferenceDescriptor, value) 22 | } 23 | 24 | for { 25 | key @ (dummy: String) ← properties.propertyNames 26 | descriptor ← AllPreferences.preferencesByKey.get(key) 27 | valueString = properties.getProperty(key) 28 | } setPreference(descriptor, valueString) 29 | preferences 30 | 31 | } 32 | 33 | def asProperties(preferences: IFormattingPreferences): Properties = { 34 | val properties = new Properties 35 | for (preference ← AllPreferences.preferences) 36 | properties.setProperty(preference.key, preferences(preference).toString) 37 | properties 38 | } 39 | 40 | @throws(classOf[IOException]) 41 | def loadPreferences(path: String): IFormattingPreferences = { 42 | val properties = new Properties 43 | withFileInputStream(path) { stream ⇒ 44 | properties.load(stream) 45 | } 46 | getPreferences(properties) 47 | } 48 | 49 | @throws(classOf[IOException]) 50 | def savePreferences(path: String, preferences: IFormattingPreferences) { 51 | val properties = asProperties(preferences) 52 | withFileOutputStream(path) { stream ⇒ 53 | properties.store(stream, "Scalariform formatter preferences") 54 | } 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /scalariform/src/test/scala/com/danieltrinh/scalariform/formatter/BlockExprFormatterTest.scala: -------------------------------------------------------------------------------- 1 | package scalariform.formatter 2 | 3 | import scalariform.parser._ 4 | import scalariform.formatter._ 5 | 6 | // format: OFF 7 | class BlockExprFormatterTest extends AbstractExpressionFormatterTest { 8 | 9 | override val debug = false 10 | 11 | """{ 12 | | a(); 13 | | b() 14 | |}""" ==> 15 | """{ 16 | | a(); 17 | | b() 18 | |}""" 19 | 20 | """{ 21 | |a;b;c; 22 | |d;e;f//Foo 23 | |g/*h*/;i 24 | |/* j */ 25 | |k 26 | |}""" ==> 27 | """{ 28 | | a; b; c; 29 | | d; e; f //Foo 30 | | g /*h*/ ; i 31 | | /* j */ 32 | | k 33 | |}""" 34 | 35 | """{ 36 | |val x = { 37 | |a() 38 | |b() 39 | |} 40 | |}""" ==> 41 | """{ 42 | | val x = { 43 | | a() 44 | | b() 45 | | } 46 | |}""" 47 | 48 | """{ 49 | |1 + 50 | |2 51 | |}""" ==> 52 | """{ 53 | | 1 + 54 | | 2 55 | |}""" 56 | 57 | "{ object A }" ==> "{ object A }" 58 | 59 | "{ class A }" ==> "{ class A }" 60 | 61 | """{ 62 | |class A 63 | |class B 64 | |}""" ==> 65 | """{ 66 | | class A 67 | | class B 68 | |}""" 69 | 70 | "{ case 42 => }" ==> "{ case 42 => }" 71 | "{ case -42 => }" ==> "{ case -42 => }" 72 | 73 | """{ 74 | | println("foo") 75 | | (x: Int) => 42 76 | |}""" ==> 77 | """{ 78 | | println("foo") 79 | | (x: Int) => 42 80 | |}""" 81 | 82 | """{ 83 | |c ! 84 | |val b 85 | |}""" ==> 86 | """{ 87 | | c ! 88 | | val b 89 | |}""" 90 | 91 | """{ 92 | | if (b) { /**/} 93 | | false 94 | |}""" ==> 95 | """{ 96 | | if (b) { /**/ } 97 | | false 98 | |}""" 99 | 100 | """{ 101 | |}""" ==> 102 | """{ 103 | |}""" 104 | 105 | """{ 106 | | 107 | |}""" ==> 108 | """{ 109 | | 110 | |}""" 111 | 112 | "{ ; a }" ==> "{ ; a }" 113 | 114 | } 115 | -------------------------------------------------------------------------------- /scalariform/src/test/scala/com/danieltrinh/scalariform/parser/ParserTest.scala: -------------------------------------------------------------------------------- 1 | package scalariform.parser 2 | 3 | import scalariform.lexer._ 4 | import scalariform.parser._ 5 | 6 | import org.scalatest.FlatSpec 7 | import org.scalatest.matchers.ShouldMatchers 8 | 9 | // format: +preserveSpaceBeforeArguments 10 | class ParserTest extends FlatSpec with ShouldMatchers { 11 | 12 | "Parser" should "throw a parse exception" in { 13 | evaluating { parseExpression("for {x <- b if }") } should produce[ScalaParserException] 14 | } 15 | 16 | "Parser" should "throw a parse exception for empty match " in { 17 | evaluating { parseExpression("a match { }") } should produce[ScalaParserException] 18 | } 19 | 20 | "Parser" should "produce a parse exception on a trailing close brace" in { 21 | evaluating { parseCompilationUnit("class A{}}") } should produce[ScalaParserException] 22 | } 23 | 24 | "Parser" should "not throw an exception" in { 25 | parseExpression("{ case List[String]() => 12 }") 26 | } 27 | 28 | // See issue #60 29 | "Parser" should "not throw an exception on case block ending with decl" in { 30 | parseExpression(""" 31 | args(0) match { 32 | case "blah" => 33 | val x = args(0) 34 | case _ => 35 | println("not blah") 36 | } 37 | """) 38 | } 39 | 40 | "Parser" should "throw a parse exception in bad package blocks" in { 41 | evaluating { parseCompilationUnit("package a {} package b {}") } should produce[ScalaParserException] 42 | } 43 | 44 | // issue #44 45 | "Parser" should "allow qualified type parameter in pattern matching" in { 46 | parseExpression(""" 47 | { 48 | case List[scala.Int]() => 1 49 | case _: List[scala.Int] => 2 50 | } 51 | """) 52 | } 53 | 54 | private def parser(s: String) = new ScalaParser(ScalaLexer.tokenise(s).toArray) 55 | private def parseExpression(s: String) = parser(s).expr 56 | private def parseCompilationUnit(s: String) = parser(s).compilationUnit 57 | 58 | } 59 | -------------------------------------------------------------------------------- /scalariform/src/test/scala/com/danieltrinh/scalariform/lexer/NewlineInferencerTest.scala: -------------------------------------------------------------------------------- 1 | package scalariform.lexer 2 | 3 | import scalariform._ 4 | import scalariform.lexer.Tokens._ 5 | import org.scalatest.FlatSpec 6 | import org.scalatest.matchers.ShouldMatchers 7 | import org.scalatest.TestFailedException 8 | import org.scalatest.TestPendingException 9 | import java.io._ 10 | 11 | /** 12 | * Test full tokeniser, including newline inferencing. 13 | */ 14 | class NewlineInferencerTest extends FlatSpec with ShouldMatchers { 15 | 16 | implicit def string2TestString(s: String)(implicit forgiveErrors: Boolean = false, scalaVersion: ScalaVersion = ScalaVersions.DEFAULT) = 17 | new TestString(s, forgiveErrors, scalaVersion); 18 | 19 | // See issue #60 20 | """ 21 | a match { 22 | case b => 23 | val c = d 24 | case e => 25 | }""" shouldProduceTokens ( 26 | VARID, MATCH, LBRACE, 27 | CASE, VARID, ARROW, 28 | VAL, VARID, EQUALS, VARID, NEWLINE, 29 | CASE, VARID, ARROW, 30 | RBRACE) 31 | 32 | class TestString(s: String, forgiveErrors: Boolean = false, scalaVersion: ScalaVersion = ScalaVersions.DEFAULT) { 33 | 34 | def shouldProduceTokens(toks: TokenType*)() { 35 | check(s.stripMargin, toks.toList) 36 | } 37 | 38 | private def check(s: String, expectedTokens: List[TokenType]) { 39 | it should ("tokenise >>>" + s + "<<< as >>>" + expectedTokens + "<<< forgiveErrors = " + forgiveErrors + ", scalaVersion = " + scalaVersion) in { 40 | val actualTokens: List[Token] = ScalaLexer.tokenise(s, forgiveErrors, scalaVersion.toString) 41 | val actualTokenTypes = actualTokens.map(_.tokenType) 42 | require(actualTokenTypes.last == EOF, "Last token must be EOF, but was " + actualTokens.last.tokenType) 43 | require(actualTokenTypes.count(_ == EOF) == 1, "There must only be one EOF token") 44 | val reconstitutedSource = actualTokens.init.map(_.rawText).mkString 45 | require(actualTokenTypes.init == expectedTokens, "Tokens do not match. Expected " + expectedTokens + ", but was " + actualTokenTypes.init) 46 | } 47 | } 48 | 49 | } 50 | 51 | } 52 | 53 | -------------------------------------------------------------------------------- /scalariform/src/test/scala/com/danieltrinh/scalariform/formatter/CompactControlReadabilityTest.scala: -------------------------------------------------------------------------------- 1 | package scalariform.formatter 2 | 3 | import preferences.FormattingPreferences._ 4 | import scalariform.formatter.preferences._ 5 | import scalariform.parser._ 6 | import scalariform.formatter._ 7 | 8 | // format: OFF 9 | class CompactControlReadabilityTest extends AbstractExpressionFormatterTest { 10 | 11 | implicit val formattingPreferences = FormattingPreferences.setPreference(CompactControlReadability, true) 12 | 13 | """if(a){ 14 | |foo 15 | |} else { 16 | |bar }""" ==> 17 | """if (a) { 18 | | foo 19 | |} 20 | |else { 21 | | bar 22 | |}""" 23 | 24 | """if(a){ 25 | |foo 26 | |} 27 | |else { 28 | |bar }""" ==> 29 | """if (a) { 30 | | foo 31 | |} 32 | |else { 33 | | bar 34 | |}""" 35 | 36 | """if(a){ 37 | |foo 38 | |} else { 39 | | 40 | |bar }""" ==> 41 | """if (a) { 42 | | foo 43 | |} 44 | |else { 45 | | 46 | | bar 47 | |}""" 48 | 49 | """try{ 50 | | foo 51 | |} catch { 52 | | bar 53 | |}""" ==> 54 | """try { 55 | | foo 56 | |} 57 | |catch { 58 | | bar 59 | |}""" 60 | 61 | """try{ 62 | | foo 63 | |} finally { 64 | | bar 65 | |}""" ==> 66 | """try { 67 | | foo 68 | |} 69 | |finally { 70 | | bar 71 | |}""" 72 | 73 | """try{ 74 | | foo 75 | |} 76 | |finally { 77 | | bar 78 | |}""" ==> 79 | """try { 80 | | foo 81 | |} 82 | |finally { 83 | | bar 84 | |}""" 85 | 86 | """try{ 87 | | foo 88 | |} finally { 89 | | 90 | | bar 91 | |}""" ==> 92 | """try { 93 | | foo 94 | |} 95 | |finally { 96 | | 97 | | bar 98 | |}""" 99 | 100 | "if (y > 0) positive else if (y < 0) negative else zero" ==> "if (y > 0) positive else if (y < 0) negative else zero" 101 | 102 | "try x catch y finally z" ==> "try x catch y finally z" 103 | 104 | 105 | 106 | } 107 | -------------------------------------------------------------------------------- /scalariform.feature/feature.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | Code formatter for Scala 2.8 9 | 10 | 11 | 12 | The MIT License 13 | 14 | Copyright (c) 2010 Matthew D. Russell 15 | 16 | Permission is hereby granted, free of charge, to any person obtaining 17 | a copy of this software and associated documentation files (the 18 | "Software"), to deal in the Software without restriction, including 19 | without limitation the rights to use, copy, modify, merge, publish, 20 | distribute, sublicense, and/or sell copies of the Software, and 21 | to permit persons to whom the Software is furnished to do so, 22 | subject to the following conditions: 23 | 24 | The above copyright notice and this permission notice shall be 25 | included in all copies or substantial portions of the Software. 26 | 27 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 28 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 29 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 30 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 31 | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 32 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 33 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /scalariform/src/test/scala/com/danieltrinh/scalariform/formatter/WhileExprFormatterTest.scala: -------------------------------------------------------------------------------- 1 | package scalariform.formatter 2 | 3 | import scalariform.parser._ 4 | import scalariform.formatter._ 5 | 6 | // format: OFF 7 | class WhileExprFormatterTest extends AbstractExpressionFormatterTest { 8 | 9 | "while( true ) run()" ==> "while (true) run()" 10 | 11 | """while( true ) 12 | |run()""" ==> 13 | """while (true) 14 | | run()""" 15 | 16 | "while (true) { run() }" ==> "while (true) { run() }" 17 | 18 | """while (true) { 19 | |run() }""" ==> 20 | """while (true) { 21 | | run() 22 | |}""" 23 | 24 | "do run()while( true )" ==> "do run() while (true)" 25 | 26 | """do 27 | |run() while (true)""" ==> 28 | """do 29 | | run() 30 | |while (true)""" 31 | 32 | """do { 33 | |run() 34 | |} 35 | |while (true)""" ==> 36 | """do { 37 | | run() 38 | |} while (true)""" 39 | 40 | """do { 41 | |run() 42 | |}; 43 | |while (true)""" ==> 44 | """do { 45 | | run() 46 | |}; while (true)""" 47 | 48 | """do run(); 49 | |while (true)""" ==> 50 | """do run(); 51 | |while (true)""" 52 | 53 | """do run() 54 | |while (true)""" ==> 55 | """do run() 56 | |while (true)""" 57 | 58 | """do 59 | |run() 60 | |while (true)""" ==> 61 | """do 62 | | run() 63 | |while (true)""" 64 | 65 | """do 66 | |run() 67 | |; 68 | |while (true)""" ==> 69 | """do 70 | | run(); 71 | |while (true)""" 72 | 73 | """do { run() }; 74 | |while (true)""" ==> 75 | """do { run() }; 76 | |while (true)""" 77 | 78 | """do { run() } 79 | |while (true)""" ==> 80 | """do { run() } 81 | |while (true)""" 82 | 83 | """while(true){do { run(); }; 84 | |while (if (true) { 85 | |a} else { 86 | |b}) 87 | |}""" ==> 88 | """while (true) { 89 | | do { run(); }; 90 | | while (if (true) { 91 | | a 92 | | } else { 93 | | b 94 | | }) 95 | |}""" 96 | 97 | """a(while (b) 98 | |c())""" ==> 99 | """a(while (b) 100 | | c())""" 101 | 102 | } 103 | 104 | 105 | -------------------------------------------------------------------------------- /scalariform.update/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | scalariform.update 4 | 5 | 6 | 55 | 56 | 57 | 58 |
59 | 60 | 61 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/utils/TextEdits.scala: -------------------------------------------------------------------------------- 1 | package scalariform.utils 2 | 3 | object TextEdit { 4 | 5 | def delete(range: Range): TextEdit = delete(range.offset, range.length) 6 | 7 | def delete(position: Int, length: Int): TextEdit = TextEdit(position = position, length = length, replacement = "") 8 | 9 | } 10 | 11 | case class TextEdit(position: Int, length: Int, replacement: String) { 12 | 13 | require(position >= 0, "position must be positive: " + position) 14 | 15 | require(length >= 0) 16 | 17 | override lazy val toString = { 18 | val replacementDisplay = replacement.replace("\n", """\n""").replace("\r", """\r""") 19 | getClass.getSimpleName + "(position = " + position + ", length = " + length + ", replacement = '" + replacementDisplay + "')" 20 | } 21 | 22 | def shift(n: Int) = copy(position = position + n) 23 | 24 | } 25 | 26 | object TextEditProcessor { 27 | 28 | /** 29 | * @param edits must be ordered and non-overlapping 30 | */ 31 | def runEdits(s: String, edits: TextEdit*): String = runEdits(s, edits.toList) 32 | 33 | /** 34 | * @param edits must be ordered and non-overlapping 35 | */ 36 | def runEdits(s: String, edits: List[TextEdit]): String = { 37 | val sb = new StringBuilder 38 | var pos = 0 39 | var editsRemaining = edits 40 | while (pos < s.length) { 41 | if (editsRemaining.isEmpty) { 42 | sb.append(s(pos)) 43 | pos += 1 44 | } else { 45 | val edit = editsRemaining.head 46 | if (pos == edit.position) { 47 | editsRemaining = editsRemaining.tail 48 | pos += edit.length 49 | sb.append(edit.replacement) 50 | } else { 51 | sb.append(s(pos)) 52 | pos += 1 53 | } 54 | } 55 | } 56 | var processEditsAtEnd = true 57 | while (processEditsAtEnd) { 58 | if (editsRemaining.isEmpty) 59 | processEditsAtEnd = false 60 | else { 61 | val edit = editsRemaining.head 62 | if (pos == edit.position) { 63 | editsRemaining = editsRemaining.tail 64 | pos += edit.length 65 | sb.append(edit.replacement) 66 | } else 67 | processEditsAtEnd = false 68 | } 69 | } 70 | require(editsRemaining.isEmpty) 71 | sb.toString 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /scalariform/src/test/scala/com/danieltrinh/scalariform/formatter/TypeFormatterTest.scala: -------------------------------------------------------------------------------- 1 | package scalariform.formatter 2 | 3 | import scalariform.parser._ 4 | 5 | // format: OFF 6 | class TypeFormatterTest extends AbstractFormatterTest { 7 | 8 | "Int" ==> "Int" 9 | "List [ String ] " ==> "List[String]" 10 | "Map[String,List[ Int]]" ==> "Map[String, List[Int]]" 11 | 12 | "A => B" ==> "A => B" 13 | 14 | "List/*b*/[/*c*/String/*d*/]" ==> "List /*b*/ [ /*c*/ String /*d*/ ]" 15 | 16 | "Int /*foo*/ Either String" ==> "Int /*foo*/ Either String" 17 | 18 | "List[_>:A<:B]" ==> "List[_ >: A <: B]" 19 | 20 | "List[_>:A_ <:B_]" ==> "List[_ >: A_ <: B_]" 21 | "A_ @Deprecated" ==> "A_ @Deprecated" 22 | "A Either B @Deprecated" ==> "A Either B @Deprecated" 23 | 24 | "A#B" ==> "A#B" 25 | "A_ #B" ==> "A_ #B" 26 | "A_# #B" ==> "A_# #B" 27 | 28 | "this . type" ==> "this.type" 29 | "List[a.type]" ==> "List[a.type]" 30 | "(a.type, b.type)" ==> "(a.type, b.type)" 31 | 32 | "(A)#X" ==> "(A)#X" 33 | 34 | "A @cps[A, C]" ==> "A @cps[A, C]" 35 | 36 | "Int @cps[Int,Int]" ==> "Int @cps[Int, Int]" 37 | 38 | "{def bar :Unit}" ==> "{ def bar: Unit }" 39 | 40 | "(A, B) => C" ==> "(A, B) => C" 41 | 42 | "(A*) => B" ==> "(A*) => B" 43 | 44 | "(=> A) => B" ==> "(=> A) => B" 45 | 46 | "(C, => A) => B" ==> "(C, => A) => B" 47 | 48 | "(C, A*) => B" ==> "(C, A*) => B" 49 | 50 | "(A*, B) => C" ==> "(A*, B) => C" 51 | 52 | "(=>A)" ==> "(=> A)" 53 | "(=> A#Inner[Int])" ==> "(=> A#Inner[Int])" 54 | 55 | "Int Either String" ==> "Int Either String" 56 | 57 | // TODO: forSome clause not valid 58 | "(=> A#Inner[Int] @Deprecated with B with (C) @Deprecated Either (B, A#Inner[_ <: B]) with C) => B forSome {}" ==> 59 | "(=> A#Inner[Int] @Deprecated with B with (C) @Deprecated Either (B, A#Inner[_ <: B]) with C) => B forSome {}" 60 | 61 | "(=> A with B Either (B, A)) => B" ==> 62 | "(=> A with B Either (B, A)) => B" 63 | 64 | "b[c# ::[d]]" ==> "b[c# ::[d]]" 65 | 66 | """C :: 67 | | D""" ==> 68 | """C :: D""" // To check that this doesn't blow up -- we should maintain the newline 69 | 70 | override val debug = false 71 | 72 | type Result = Type 73 | 74 | def parse(parser: ScalaParser) = parser.typ() // TODO: ensure EOF 75 | 76 | def format(formatter: ScalaFormatter, result: Result) = formatter.format(result)(FormatterState(indentLevel = 0)) 77 | 78 | } 79 | -------------------------------------------------------------------------------- /misc/src/main/scala/com/danieltrinh/scalariform/perf/LexerPerformanceTest.scala: -------------------------------------------------------------------------------- 1 | package scalariform.perf 2 | 3 | import java.io.File 4 | import scala.io.Source 5 | import scalariform.parser._ 6 | import scalariform.lexer.{ Token ⇒ _, _ } 7 | import scalariform.utils.Utils.time 8 | import scalariform.formatter._ 9 | 10 | object LexerPerformanceTest { 11 | 12 | val ITERATIONS = 1000 13 | 14 | val WARMUP = 200 15 | 16 | def main(args: Array[String]) { 17 | 18 | val file = new File("/home/matt/coding/scala/src/compiler/scala/tools/nsc/typechecker/Typers.scala") 19 | // val file = new File("/home/matt/Downloads/scala/scala/src/compiler/scala/tools/nsc/typechecker/Typers.scala") 20 | val source = Source.fromFile(file).mkString 21 | println("Source: " + source.length + " chars") 22 | val tokens = ScalaLexer.rawTokenise(source) 23 | println("Tokens: " + tokens.size) 24 | 1 to WARMUP foreach { _ ⇒ doIt(source) } 25 | 26 | val start = System.currentTimeMillis 27 | val durations = 1 to ITERATIONS map { _ ⇒ 28 | val start1 = System.nanoTime 29 | doIt(source) 30 | val duration = System.nanoTime - start1 31 | duration.toDouble / 1000000.0 32 | } 33 | val duration = System.currentTimeMillis - start 34 | val meanT = duration.toDouble / ITERATIONS 35 | println("Raw average: " + meanT) 36 | def compute(iterable: Iterable[Double]): (Double, Double) = { 37 | def square(x: Double) = x * x 38 | val size = iterable.size 39 | val mean = durations.sum / size 40 | (mean, math.sqrt(durations.map(n ⇒ square(n - mean)).sum / size)) 41 | } 42 | val (mean, stdDev) = compute(durations) 43 | def isNormal(d: Double) = math.abs(d - mean) < 2 * stdDev 44 | val durations2 = durations.filter(isNormal) 45 | println("Original trials: " + ITERATIONS) 46 | println("Outliers removed: " + (ITERATIONS - durations2.size)) 47 | val (mean2, stdDev2) = compute(durations2) 48 | println("Minimum: " + durations.min + " ms") 49 | println("Average: " + mean2 + " ms") 50 | println("Standard deviation: " + stdDev2 + " ms") 51 | println() 52 | durations.foreach(println) 53 | } 54 | 55 | private def doIt(s: String) = { 56 | // new WhitespaceAndCommentsGrouper(ScalaLexer.createRawLexer(s)).toList 57 | // ScalaLexer.tokenise(s) 58 | // UnicodeEscapeDecoder.decode(s) 59 | ScalaLexer.rawTokenise(s) // 8.13 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/formatter/Alignment.scala: -------------------------------------------------------------------------------- 1 | package scalariform.formatter 2 | 3 | import scalariform.parser._ 4 | import Math._ 5 | import scalariform.formatter.preferences._ 6 | 7 | // For now, this is just a place to store alignment related functionality. 8 | // TOOD: refactor duplicate behavior in here 9 | object Alignment { 10 | type EitherAlignableParam = Either[ConsecutiveSingleLineParams, Param] 11 | type EitherAlignableEqualsExpr = Either[ConsecutiveSingleLineEqualsExprs, CallExpr] 12 | type EitherAlignableCaseClause = Either[ConsecutiveSingleLineCaseClauses, CaseClause] 13 | 14 | case class ConsecutiveSingleLineParams(params: List[Param], maxSectionLengths: ParamSectionLengths, thisSectionLengths: ParamSectionLengths) { 15 | def prepend(param: Param, newLengths: ParamSectionLengths): ConsecutiveSingleLineParams = { 16 | ConsecutiveSingleLineParams(param :: params, maxSectionLengths.max(newLengths), thisSectionLengths) 17 | } 18 | 19 | // Splits the head param off, returning it and the remaining params. 20 | // This doesn't recalculate section lengths. 21 | def pop: (Option[Param], ConsecutiveSingleLineParams) = params match { 22 | case param :: remainingParams ⇒ 23 | (Some(param), copy(params = remainingParams)) 24 | case Nil ⇒ 25 | (None, copy(params = Nil)) 26 | } 27 | } 28 | 29 | case class ConsecutiveSingleLineEqualsExprs( 30 | equalsExprs: List[EqualsExpr], 31 | largestIdLength: Int 32 | ) { 33 | 34 | def prepend(equalsExpr: EqualsExpr, length: Int) = { 35 | ConsecutiveSingleLineEqualsExprs(equalsExpr :: equalsExprs, max(length, largestIdLength)) 36 | } 37 | } 38 | 39 | case class ConsecutiveSingleLineCaseClauses( 40 | clauses: List[CaseClause], 41 | largestCasePatternLength: Int, 42 | smallestCasePatternLength: Int 43 | ) { 44 | def prepend(clause: CaseClause, length: Int) = 45 | ConsecutiveSingleLineCaseClauses(clause :: clauses, max(length, largestCasePatternLength), min(length, smallestCasePatternLength)) 46 | 47 | def patternLengthRange = largestCasePatternLength - smallestCasePatternLength 48 | 49 | } 50 | 51 | case class ParamSectionLengths(prefixLength: Int, idLength: Int, prefixAndIdLength: Int, typeLength: Int) { 52 | def max(newParamSectionLength: ParamSectionLengths): ParamSectionLengths = { 53 | val ParamSectionLengths(newPrefixLength, newIdLength, newPrefixAndIdLength, newTypeLength) = newParamSectionLength 54 | ParamSectionLengths( 55 | math.max(prefixLength, newPrefixLength), 56 | math.max(idLength, newIdLength), 57 | math.max(prefixAndIdLength, newPrefixAndIdLength), 58 | math.max(typeLength, newTypeLength) 59 | ) 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /cli/src/main/scala/com/danieltrinh/scalariform/commandline/CommandLineOptionParser.scala: -------------------------------------------------------------------------------- 1 | package scalariform.commandline 2 | 3 | import scala.util.parsing.input._ 4 | import scala.util.parsing.combinator._ 5 | 6 | class CommandLineOptionParser extends RegexParsers { 7 | 8 | lazy val option: Parser[CommandLineArgument] = 9 | phrase(help) | phrase(version) | phrase(scalaVersion) | phrase(stdin) | phrase(stdout) | phrase(recurse) | 10 | phrase(test) | phrase(forceOutput) | phrase(quiet) | phrase(fileList) | phrase(encoding) | phrase(toggle) | 11 | phrase(preferenceFile) | phrase(preferenceOption) | phrase(badOption) 12 | 13 | lazy val test = ("--test" | "-t") ^^^ Test 14 | 15 | lazy val forceOutput = ("--forceOutput" | "-f") ^^^ ForceOutput 16 | 17 | lazy val stdout = "--stdout" ^^^ Stdout 18 | 19 | lazy val stdin = "--stdin" ^^^ Stdin 20 | 21 | lazy val quiet = ("--quiet" | "-q") ^^^ Quiet 22 | 23 | lazy val recurse = ("--recurse" | "-r") ^^^ Recurse 24 | 25 | lazy val help = ("--help" | "-help" | "-h") ^^^ Help 26 | 27 | lazy val version = ("--version" | "-version") ^^^ Version 28 | 29 | lazy val scalaVersion = ("--scalaVersion=" | "-s=") ~> """(\d|\.)+""".r ^^ ScalaVersion 30 | 31 | lazy val fileList = ("--fileList=" | "-l=") ~> ".+".r ^^ FileList 32 | 33 | lazy val encoding = "--encoding=" ~> ".+".r ^^ Encoding 34 | 35 | lazy val toggle = plusOrMinus ~ preferenceKey ^^ { case onOrOff ~ key ⇒ PreferenceOption(key, onOrOff.toString) } 36 | 37 | lazy val plusOrMinus = "+" ^^^ true | "-" ^^^ false 38 | 39 | lazy val preferenceFile = ("--preferenceFile=" | "-p=") ~> ".+".r ^^ PreferenceFile 40 | 41 | lazy val preferenceOption = ("-" ~> preferenceKey <~ "=") ~ """(\w|\.)+""".r ^^ { 42 | case (key ~ value) ⇒ PreferenceOption(key, value) 43 | } 44 | 45 | lazy val preferenceKey: Parser[String] = """[a-zA-Z.]+""".r 46 | 47 | lazy val badOption = guard(plusOrMinus) ~> ".*".r ^^ BadOption 48 | 49 | def getArgument(s: String) = parse(option, s) getOrElse FileName(s) 50 | } 51 | 52 | sealed trait CommandLineArgument 53 | 54 | case class PreferenceOption(preferenceKey: String, value: String) extends CommandLineArgument 55 | case class PreferenceFile(name: String) extends CommandLineArgument 56 | case class FileName(name: String) extends CommandLineArgument 57 | case class FileList(name: String) extends CommandLineArgument 58 | case class Encoding(encoding: String) extends CommandLineArgument 59 | case object Test extends CommandLineArgument 60 | case object Stdout extends CommandLineArgument 61 | case object Stdin extends CommandLineArgument 62 | case object ForceOutput extends CommandLineArgument 63 | case object Quiet extends CommandLineArgument 64 | case object Help extends CommandLineArgument 65 | case object Version extends CommandLineArgument 66 | case object Recurse extends CommandLineArgument 67 | case class ScalaVersion(scalaVersion: String) extends CommandLineArgument 68 | case class BadOption(name: String) extends CommandLineArgument 69 | -------------------------------------------------------------------------------- /scalariform/src/test/scala/com/danieltrinh/scalariform/utils/TextEditTest.scala: -------------------------------------------------------------------------------- 1 | package scalariform.utils 2 | 3 | import org.scalatest.FlatSpec 4 | import org.scalatest.matchers.ShouldMatchers 5 | 6 | // format: +preserveSpaceBeforeArguments 7 | class TextEditTest extends FlatSpec with ShouldMatchers { 8 | 9 | import TextEditProcessor.runEdits 10 | 11 | it should "do replace edits" in { 12 | 13 | runEdits("012345", TextEdit(position = 0, length = 2, replacement = "wibble")) should equal ("wibble2345") 14 | 15 | runEdits("012345", TextEdit(position = 2, length = 2, replacement = "23")) should equal ("012345") 16 | 17 | runEdits("012345", 18 | TextEdit(position = 0, length = 2, replacement = "foo"), 19 | TextEdit(position = 3, length = 2, replacement = "bar")) should equal ("foo2bar5") 20 | } 21 | 22 | it should "do delete edits" in { 23 | 24 | runEdits("012345", TextEdit(position = 0, length = 1, replacement = "")) should equal ("12345") 25 | runEdits("012345", TextEdit(position = 0, length = 6, replacement = "")) should equal ("") 26 | runEdits("012345", TextEdit(position = 5, length = 1, replacement = "")) should equal ("01234") 27 | runEdits("012345", TextEdit(position = 1, length = 4, replacement = "")) should equal ("05") 28 | runEdits("0", TextEdit(position = 0, length = 1, replacement = "")) should equal ("") 29 | 30 | runEdits("012345", 31 | TextEdit(position = 0, length = 2, replacement = ""), 32 | TextEdit(position = 4, length = 2, replacement = "")) should equal ("23") 33 | 34 | } 35 | 36 | it should "do insert edits" in { 37 | 38 | runEdits("012345", TextEdit(position = 0, length = 0, replacement = "")) should equal ("012345") 39 | runEdits("012345", TextEdit(position = 6, length = 0, replacement = "")) should equal ("012345") 40 | runEdits("012345", TextEdit(position = 0, length = 0, replacement = "X")) should equal ("X012345") 41 | runEdits("012345", TextEdit(position = 1, length = 0, replacement = "X")) should equal ("0X12345") 42 | runEdits("012345", TextEdit(position = 5, length = 0, replacement = "X")) should equal ("01234X5") 43 | runEdits("012345", TextEdit(position = 6, length = 0, replacement = "X")) should equal ("012345X") 44 | runEdits("", TextEdit(position = 0, length = 0, replacement = "X")) should equal ("X") 45 | 46 | runEdits("012345", 47 | TextEdit(position = 0, length = 0, replacement = "X"), 48 | TextEdit(position = 3, length = 0, replacement = "X")) should equal ("X012X345") 49 | 50 | } 51 | 52 | it should "do multiple insert edits at same position" in { 53 | 54 | runEdits("012345", 55 | TextEdit(position = 0, length = 0, replacement = "X"), 56 | TextEdit(position = 0, length = 0, replacement = "Y")) should equal ("XY012345") 57 | 58 | runEdits("012345", 59 | TextEdit(position = 0, length = 0, replacement = "X"), 60 | TextEdit(position = 6, length = 0, replacement = "X"), 61 | TextEdit(position = 6, length = 0, replacement = "Y")) should equal ("X012345XY") 62 | 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/formatter/CommentFormatter.scala: -------------------------------------------------------------------------------- 1 | package scalariform.formatter 2 | 3 | import scalariform.parser._ 4 | import scalariform.utils._ 5 | import scalariform.lexer._ 6 | import scalariform.formatter.preferences._ 7 | import scala.annotation.tailrec 8 | 9 | trait CommentFormatter { self: HasFormattingPreferences with ScalaFormatter ⇒ 10 | 11 | private def getLines(comment: String): (String, List[String]) = { 12 | val prefix = List("/** ", "/**", "/* ", "/*").find(comment.startsWith).get 13 | val (start, rest) = comment.splitAt(prefix.length) 14 | val (contents, _) = rest.splitAt(rest.length - "*/".length) 15 | val firstLine :: otherLines = contents.split("""\r?\n([ \t]*(\*(?!/))?)?""", Integer.MAX_VALUE).toList 16 | val afterStarSpaces = if (formattingPreferences(MultilineScaladocCommentsStartOnFirstLine)) 2 else 1 17 | val initialSpaces = firstLine takeWhile (_.isWhitespace) 18 | val adjustedLines = dropInitialSpaces(firstLine, initialSpaces.size) :: (otherLines map { dropInitialSpaces(_, afterStarSpaces) }) 19 | // val adjustedLines map { line ⇒ if (line startsWith "*/") "*" + line else line } 20 | (start, adjustedLines) 21 | } 22 | 23 | @tailrec 24 | private def dropInitialSpaces(s: String, maxSpacesToDrop: Int): String = 25 | if (maxSpacesToDrop > 0 && s.startsWith(" ")) 26 | dropInitialSpaces(s drop 1, maxSpacesToDrop - 1) 27 | else 28 | s 29 | 30 | private def removeTrailingWhitespace(s: String) = s.reverse.dropWhile(_.isWhitespace).reverse 31 | 32 | private def pruneEmptyInitial(lines: List[String]) = lines match { 33 | case first :: rest if first.trim == "" ⇒ rest 34 | case _ ⇒ lines 35 | } 36 | 37 | private def pruneEmptyFinal(lines: List[String]) = pruneEmptyInitial(lines.reverse).reverse 38 | 39 | def formatComment(comment: HiddenToken, indentLevel: Int): String = 40 | if (comment.rawText contains '\n') { 41 | val sb = new StringBuilder 42 | val (start, rawLines) = getLines(comment.rawText) 43 | 44 | val lines = pruneEmptyFinal(pruneEmptyInitial(rawLines)) 45 | 46 | val alignBeneathSecondAsterisk = formattingPreferences(PlaceScaladocAsterisksBeneathSecondAsterisk) 47 | val startOnFirstLine = formattingPreferences(MultilineScaladocCommentsStartOnFirstLine) 48 | val beforeStarSpaces = if (alignBeneathSecondAsterisk) " " else " " 49 | val afterStarSpaces = if (startOnFirstLine && !alignBeneathSecondAsterisk) " " else " " 50 | sb.append(start.trim) 51 | var firstLine = true 52 | for (line ← lines) { 53 | val trimmedLine = removeTrailingWhitespace(line) 54 | if (firstLine && startOnFirstLine) { 55 | if (trimmedLine.nonEmpty) 56 | sb.append(" ").append(trimmedLine) 57 | } else { 58 | sb.append(newlineSequence).indent(indentLevel).append(beforeStarSpaces).append("*") 59 | if (trimmedLine.nonEmpty) 60 | sb.append(afterStarSpaces).append(trimmedLine) 61 | } 62 | firstLine = false 63 | } 64 | sb.append(newlineSequence).indent(indentLevel).append(beforeStarSpaces).append("*/") 65 | sb.toString 66 | } else 67 | comment.rawText 68 | 69 | } 70 | -------------------------------------------------------------------------------- /project/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/formatter/TypeFormatter.scala: -------------------------------------------------------------------------------- 1 | package scalariform.formatter 2 | 3 | import scalariform.lexer.Tokens._ 4 | import scalariform.lexer.Token 5 | import scalariform.parser._ 6 | import scalariform.utils._ 7 | import scalariform.lexer.Tokens 8 | import scalariform.formatter.preferences._ 9 | import scalariform.lexer.Chars 10 | 11 | trait TypeFormatter { self: HasFormattingPreferences with AnnotationFormatter with ExprFormatter with ScalaFormatter ⇒ 12 | 13 | def format(type_ :Type)(implicit formatterState: FormatterState): FormatResult = format(type_.contents) 14 | 15 | def format(typeElements: List[TypeElement])(implicit formatterState: FormatterState): FormatResult = { 16 | var formatResult = format(typeElements.head) 17 | for ((previousElement, element) ← Utils.stagger(typeElements)) { 18 | if (previousElement.isInstanceOf[Annotation] || 19 | previousElement.isInstanceOf[Refinement] || 20 | previousElement.isInstanceOf[InfixTypeConstructor] || 21 | element.isInstanceOf[Refinement] || 22 | element.isInstanceOf[InfixTypeConstructor]) 23 | formatResult = formatResult.formatNewlineOrOrdinary(element.firstToken, CompactEnsuringGap) 24 | else if (element.isInstanceOf[Annotation]) { 25 | val instruction = 26 | previousElement match { 27 | case GeneralTokens(tokens) if tokens.last.tokenType == Tokens.LBRACKET ⇒ Compact 28 | case _ ⇒ CompactEnsuringGap 29 | } 30 | formatResult = formatResult.before(element.firstToken, instruction) 31 | } else if (previousElement.isInstanceOf[VarianceTypeElement]) 32 | formatResult = formatResult.before(element.firstToken, Compact) 33 | else if (element.isInstanceOf[VarargsTypeElement]) { 34 | val instruction = if (Chars.isOperatorPart(previousElement.lastToken.text.last)) CompactEnsuringGap else Compact 35 | formatResult = formatResult.before(element.firstToken, instruction) 36 | } 37 | // else if (previousElement.isInstanceOf[CallByNameTypeElement]) 38 | // formatResult = formatResult.before(element.firstToken, Compact) 39 | formatResult ++= format(element) 40 | } 41 | formatResult 42 | } 43 | 44 | private def format(typeElement: TypeElement)(implicit formatterState: FormatterState): FormatResult = { 45 | typeElement match { 46 | case type_ @ Type(_) ⇒ format(type_) 47 | case refinement @ Refinement(_, _, _) ⇒ format(refinement) 48 | case annotation @ Annotation(_, _, _, _) ⇒ format(annotation) 49 | case TypeParamClause(contents) ⇒ format(contents) 50 | case TypeParam(contents) ⇒ format(contents) 51 | case VarianceTypeElement(id) ⇒ NoFormatResult 52 | case VarargsTypeElement(star) ⇒ NoFormatResult 53 | case _ ⇒ NoFormatResult 54 | } 55 | } 56 | 57 | private def format(refinement: Refinement)(implicit formatterState: FormatterState): FormatResult = { 58 | val Refinement(lbrace: Token, statSeq: StatSeq, rbrace: Token) = refinement 59 | val dummyBlock = BlockExpr(lbrace, Right(statSeq), rbrace) 60 | format(dummyBlock, indent = true) 61 | } 62 | 63 | } 64 | 65 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | 9 | # Internal variables. 10 | PAPEROPT_a4 = -D latex_paper_size=a4 11 | PAPEROPT_letter = -D latex_paper_size=letter 12 | ALLSPHINXOPTS = -d build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 13 | 14 | .PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest 15 | 16 | help: 17 | @echo "Please use \`make ' where is one of" 18 | @echo " html to make standalone HTML files" 19 | @echo " dirhtml to make HTML files named index.html in directories" 20 | @echo " pickle to make pickle files" 21 | @echo " json to make JSON files" 22 | @echo " htmlhelp to make HTML files and a HTML help project" 23 | @echo " qthelp to make HTML files and a qthelp project" 24 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 25 | @echo " changes to make an overview of all changed/added/deprecated items" 26 | @echo " linkcheck to check all external links for integrity" 27 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 28 | 29 | clean: 30 | -rm -rf build/* 31 | 32 | html: 33 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) build/html 34 | @echo 35 | @echo "Build finished. The HTML pages are in build/html." 36 | 37 | dirhtml: 38 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) build/dirhtml 39 | @echo 40 | @echo "Build finished. The HTML pages are in build/dirhtml." 41 | 42 | pickle: 43 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) build/pickle 44 | @echo 45 | @echo "Build finished; now you can process the pickle files." 46 | 47 | json: 48 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) build/json 49 | @echo 50 | @echo "Build finished; now you can process the JSON files." 51 | 52 | htmlhelp: 53 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) build/htmlhelp 54 | @echo 55 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 56 | ".hhp project file in build/htmlhelp." 57 | 58 | qthelp: 59 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) build/qthelp 60 | @echo 61 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 62 | ".qhcp project file in build/qthelp, like this:" 63 | @echo "# qcollectiongenerator build/qthelp/Scalariform.qhcp" 64 | @echo "To view the help file:" 65 | @echo "# assistant -collectionFile build/qthelp/Scalariform.qhc" 66 | 67 | latex: 68 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) build/latex 69 | @echo 70 | @echo "Build finished; the LaTeX files are in build/latex." 71 | @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ 72 | "run these through (pdf)latex." 73 | 74 | changes: 75 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) build/changes 76 | @echo 77 | @echo "The overview file is in build/changes." 78 | 79 | linkcheck: 80 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) build/linkcheck 81 | @echo 82 | @echo "Link check complete; look for any errors in the above output " \ 83 | "or in build/linkcheck/output.txt." 84 | 85 | doctest: 86 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) build/doctest 87 | @echo "Testing of doctests in the sources finished, look at the " \ 88 | "results in build/doctest/output.txt." 89 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/formatter/FormatResult.scala: -------------------------------------------------------------------------------- 1 | package scalariform.formatter 2 | 3 | import scalariform.lexer.Tokens._ 4 | import scalariform.lexer._ 5 | import scalariform.parser._ 6 | import scalariform.utils._ 7 | 8 | object FormatResult { 9 | 10 | val EMPTY = FormatResult(Map(), Map(), Map()) 11 | 12 | } 13 | 14 | case class FormatResult( 15 | predecessorFormatting: Map[Token, IntertokenFormatInstruction], 16 | inferredNewlineFormatting: Map[Token, IntertokenFormatInstruction], 17 | xmlRewrites: Map[Token, String] 18 | ) { 19 | 20 | def replaceXml(token: Token, replacement: String) = { 21 | require(token.tokenType.isXml) 22 | copy(xmlRewrites = xmlRewrites + (token -> replacement)) 23 | } 24 | 25 | def before(token: Token, formatInstruction: IntertokenFormatInstruction) = { 26 | require(!token.isNewline, " cannot do 'before' formatting for NEWLINE* tokens: " + token + ", " + formatInstruction) 27 | copy(predecessorFormatting = predecessorFormatting + (token -> formatInstruction)) 28 | } 29 | 30 | def formatNewline(token: Token, formatInstruction: IntertokenFormatInstruction) = { 31 | require(token.isNewline, " cannot do 'newline' formatting for non-NEWLINE tokens: " + token + ", " + formatInstruction) 32 | copy(inferredNewlineFormatting = inferredNewlineFormatting + (token -> formatInstruction)) 33 | } 34 | 35 | def formatNewlineOrOrdinary(token: Token, formatInstruction: IntertokenFormatInstruction) = 36 | if (token.isNewline) formatNewline(token, formatInstruction) 37 | else before(token, formatInstruction) 38 | 39 | def tokenWillHaveNewline(token: Token): Boolean = { 40 | val hasNewlineInstruction = predecessorFormatting.get(token) map { 41 | PartialFunction.cond(_) { case newlineInstruction: EnsureNewlineAndIndent ⇒ true } 42 | } 43 | hasNewlineInstruction.getOrElse(false) 44 | } 45 | 46 | def mergeWith(other: FormatResult): FormatResult = 47 | FormatResult( 48 | this.predecessorFormatting ++ other.predecessorFormatting, 49 | this.inferredNewlineFormatting ++ other.inferredNewlineFormatting, 50 | this.xmlRewrites ++ other.xmlRewrites 51 | ) 52 | 53 | def ++(other: FormatResult) = mergeWith(other) 54 | } 55 | 56 | object NoFormatResult extends FormatResult(Map(), Map(), Map()) 57 | 58 | abstract sealed class IntertokenFormatInstruction 59 | 60 | /** 61 | * Packs the comments together as compactly as possible, eliminating 62 | * as much non-comment whitespace as possible while ensuring that the 63 | * lexer produces the same tokens. 64 | */ 65 | case object Compact extends IntertokenFormatInstruction 66 | 67 | /** Like "Compact", but ensures there is either some comment or a single space. */ 68 | case object CompactEnsuringGap extends IntertokenFormatInstruction 69 | 70 | /** Like "Compact", but will keep at least a single space if there was whitespace before */ 71 | case object CompactPreservingGap extends IntertokenFormatInstruction 72 | 73 | /** Ensures that the interttoken region ends with NEWLINE INDENT. */ 74 | case class EnsureNewlineAndIndent(indentLevel: Int, relativeTo: Option[Token] = None) extends IntertokenFormatInstruction 75 | 76 | /** Places the token at spaces number of spaces after the indent level, padding with spaces if necessary */ 77 | case class PlaceAtColumn(indentLevel: Int, spaces: Int, relativeTo: Option[Token] = None) extends IntertokenFormatInstruction 78 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/formatter/SpecificFormatter.scala: -------------------------------------------------------------------------------- 1 | package scalariform.formatter 2 | 3 | import scalariform.lexer.Tokens._ 4 | import scalariform.lexer._ 5 | import scalariform.parser._ 6 | import scalariform.utils._ 7 | import scalariform.formatter.preferences._ 8 | import scalariform.ScalaVersions 9 | 10 | trait SpecificFormatter { 11 | 12 | def debug = false 13 | 14 | type Result <: AstNode 15 | 16 | def parse(parser: ScalaParser): Result 17 | 18 | def format(formatter: ScalaFormatter, result: Result): FormatResult 19 | 20 | @throws(classOf[ScalaParserException]) 21 | def format(source: String, lineDelimiter: Option[String] = None, scalaVersion: String = ScalaVersions.DEFAULT_VERSION)(baseFormattingPreferences: IFormattingPreferences): String = { 22 | val (edits, _) = fullFormat(source, lineDelimiter, scalaVersion)(baseFormattingPreferences) 23 | TextEditProcessor.runEdits(source, edits) 24 | } 25 | 26 | @throws(classOf[ScalaParserException]) 27 | def fullFormat(source: String, lineDelimiter: Option[String] = None, scalaVersion: String = ScalaVersions.DEFAULT_VERSION)(baseFormattingPreferences: IFormattingPreferences): (List[TextEdit], FormatResult) = { 28 | import scalariform.parser._ 29 | 30 | val startTime = System.currentTimeMillis 31 | val tokens = ScalaLexer.tokenise(source, scalaVersion = scalaVersion) 32 | if (debug) { 33 | println 34 | println(source) 35 | println("Tokens:") 36 | tokens foreach println 37 | } 38 | 39 | val parser = new ScalaParser(tokens.toArray) 40 | 41 | val parseResult = parse(parser) 42 | 43 | var actualFormattingPreferences = baseFormattingPreferences 44 | for { 45 | token ← tokens 46 | hiddenToken ← token.associatedWhitespaceAndComments 47 | ToggleOption(onOrOff, optionName) ← FormatterDirectiveParser.getDirectives(hiddenToken.text) 48 | rawPreference ← AllPreferences.preferencesByKey.get(optionName) 49 | if rawPreference.preferenceType == BooleanPreference 50 | preference = BooleanPreference.cast(rawPreference) 51 | } actualFormattingPreferences = actualFormattingPreferences.setPreference(preference, onOrOff) 52 | 53 | val parsedTokens = parseResult.tokens.filter(_.tokenType != EOF) 54 | require(parsedTokens == tokens.init /* <-- drop EOF */ , "Parse tokens differ from expected.\n Actual = \n" + 55 | parsedTokens.mkString("\n") + "\n expected = \n" + tokens.init.mkString("\n") + "\n parseResult = \n" + 56 | parseResult) 57 | 58 | if (debug) { println("Parse result: " + parseResult) } 59 | val elapsedTime = System.currentTimeMillis - startTime 60 | // if (debug) 61 | // println("Parse time = " + elapsedTime + "ms") 62 | val newlineSequence_ = lineDelimiter.getOrElse(if (source contains "\r\n") "\r\n" else "\n") 63 | 64 | val formatter = new ScalaFormatter() { 65 | 66 | def isInferredNewline(token: Token): Boolean = token.isNewline 67 | 68 | /** requires isInferredNewline(token) == true */ 69 | def inferredNewlines(token: Token): HiddenTokens = token.associatedWhitespaceAndComments 70 | 71 | def hiddenPredecessors(token: Token): HiddenTokens = token.associatedWhitespaceAndComments 72 | 73 | val formattingPreferences: IFormattingPreferences = actualFormattingPreferences 74 | 75 | val newlineSequence = newlineSequence_ 76 | 77 | } 78 | 79 | val formatResult = format(formatter, parseResult) 80 | if (debug) println("Format result: " + formatResult) 81 | val edits = formatter.writeTokens(source, tokens, formatResult) 82 | (edits, formatResult) 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /scalariform/src/test/scala/com/danieltrinh/scalariform/formatter/ForExprFormatterTest.scala: -------------------------------------------------------------------------------- 1 | package scalariform.formatter 2 | 3 | import scalariform.parser._ 4 | import scalariform.formatter._ 5 | 6 | // format: OFF 7 | class ForExprFormatterTest extends AbstractExpressionFormatterTest { 8 | 9 | // override val debug = true 10 | 11 | "for (x <- xs) yield x" ==> "for (x <- xs) yield x" 12 | 13 | "for (x <- xs) println(x)" ==> "for (x <- xs) println(x)" 14 | 15 | """for (x <- 1 to 10) 16 | | println("hello")""" ==> 17 | """for (x <- 1 to 10) 18 | | println("hello")""" 19 | 20 | """for (x <- 1 to 10) { 21 | | println("hello") 22 | |}""" ==> 23 | """for (x <- 1 to 10) { 24 | | println("hello") 25 | |}""" 26 | 27 | """for { x <- xs 28 | | y <- ys 29 | | if x > y } yield x + y""" ==> 30 | """for { 31 | | x <- xs 32 | | y <- ys 33 | | if x > y 34 | |} yield x + y""" 35 | 36 | "for (x <- xs) a()" ==> "for (x <- xs) a()" 37 | 38 | """for (x <- xs) 39 | |a()""" ==> 40 | """for (x <- xs) 41 | | a()""" 42 | 43 | """for(x <- xs) 44 | |{ 45 | |a() 46 | |}""" ==> 47 | """for (x <- xs) { 48 | | a() 49 | |}""" 50 | 51 | """for(x <- xs) { a() }""" ==> 52 | """for (x <- xs) { a() }""" 53 | 54 | """for(x <- xs) 55 | |{ a() }""" ==> 56 | """for (x <- xs) { a() }""" 57 | 58 | """for(x <- xs) yield 59 | |{ 60 | | a + b 61 | |}""" ==> 62 | """for (x <- xs) yield { 63 | | a + b 64 | |}""" 65 | 66 | "for(x <- xs) yield 3" ==> "for (x <- xs) yield 3" 67 | 68 | "for(x <- xs) yield { 3 }" ==> "for (x <- xs) yield { 3 }" 69 | 70 | """for(x <- xs) yield 71 | |{ a + b 72 | |}""" ==> 73 | """for (x <- xs) yield { 74 | | a + b 75 | |}""" 76 | 77 | """for(x <- xs) 78 | |yield 79 | |{ 80 | |z } """ ==> 81 | """for (x <- xs) yield { 82 | | z 83 | |}""" 84 | 85 | "for{x<-xs if(true)}yield( if (true) 1 else 2)" ==> "for { x <- xs if (true) } yield (if (true) 1 else 2)" 86 | 87 | """for { 88 | |val x <- xs 89 | |val y <- ys 90 | |x > y 91 | |val z = x + y 92 | |} yield 2""" ==> 93 | """for { 94 | | val x <- xs 95 | | val y <- ys 96 | | x > y 97 | | val z = x + y 98 | |} yield 2""" 99 | 100 | """for ( 101 | | val x <- xs; 102 | | val y <- ys; 103 | | x > y; 104 | | val z = x + y 105 | | ) yield z""" ==> 106 | """for ( 107 | | val x <- xs; 108 | | val y <- ys; 109 | | x > y; 110 | | val z = x + y 111 | |) yield z""" 112 | 113 | """for { 114 | |x <- xs 115 | |y <- ys 116 | |} 117 | |yield 118 | |2""" ==> 119 | """for { 120 | | x <- xs 121 | | y <- ys 122 | |} yield 2""" 123 | 124 | """for { 125 | |x <- xs 126 | |y <- ys 127 | |} 128 | |println(x + y)""" ==> 129 | """for { 130 | | x <- xs 131 | | y <- ys 132 | |} println(x + y)""" 133 | 134 | "for (n <- 1 to 10 if n > 4 if n < 10) yield n" ==> "for (n <- 1 to 10 if n > 4 if n < 10) yield n" 135 | 136 | "for {n <- 1 to 10 if n > (4)if n < 10} yield n" ==> "for { n <- 1 to 10 if n > (4) if n < 10 } yield n" 137 | 138 | """Some( 139 | |for (n <- 1 to 10) 140 | |yield n)""" ==> 141 | """Some( 142 | | for (n <- 1 to 10) 143 | | yield n 144 | |)""" 145 | 146 | """Some( 147 | |for (n <- 1 to 10) 148 | |proc())""" ==> 149 | """Some( 150 | | for (n <- 1 to 10) 151 | | proc() 152 | |)""" 153 | 154 | } 155 | 156 | 157 | -------------------------------------------------------------------------------- /scalariform/src/test/scala/com/danieltrinh/scalariform/formatter/TryExprFormatterTest.scala: -------------------------------------------------------------------------------- 1 | package scalariform.formatter 2 | 3 | import scalariform.parser._ 4 | import scalariform.formatter._ 5 | 6 | // format: OFF 7 | class TryExprFormatterTest extends AbstractExpressionFormatterTest { 8 | 9 | override val debug = false 10 | 11 | "try println()" ==> "try println()" 12 | 13 | """try 14 | |println()""" ==> 15 | """try 16 | | println()""" 17 | 18 | """try 19 | | { 20 | |println() 21 | | }""" ==> 22 | """try { 23 | | println() 24 | |}""" 25 | 26 | """try{println() 27 | | }""" ==> 28 | """try { 29 | | println() 30 | |}""" 31 | 32 | "try{println()}" ==> "try { println() }" 33 | 34 | """try { 35 | |println() 36 | |} 37 | |catch { case e => }""" ==> 38 | """try { 39 | | println() 40 | |} catch { case e => }""" 41 | 42 | "try { foo() }catch{ case e => }" ==> "try { foo() } catch { case e => }" 43 | 44 | """try { foo() } 45 | |catch{ case e => }""" ==> 46 | """try { foo() } 47 | |catch { case e => }""" 48 | 49 | """try { 50 | |foo() } 51 | |catch{ case e => }""" ==> 52 | """try { 53 | | foo() 54 | |} catch { case e => }""" 55 | 56 | """try { 57 | |} catch { case e => }finally 58 | |{ 59 | |println("foo") }""" ==> 60 | """try { 61 | |} catch { case e => } finally { 62 | | println("foo") 63 | |}""" 64 | 65 | "try {} catch { case e => } finally {}" ==> "try {} catch { case e => } finally {}" 66 | 67 | """try {} 68 | |catch { case e => } 69 | |finally {}""" ==> 70 | """try {} 71 | |catch { case e => } 72 | |finally {}""" 73 | 74 | """try {} catch { case e => } 75 | |finally{ 76 | |resource.close() 77 | |}""" ==> 78 | """try {} catch { case e => } 79 | |finally { 80 | | resource.close() 81 | |}""" 82 | 83 | """try {} catch { case e => } 84 | |finally resource.close()""" ==> 85 | """try {} catch { case e => } 86 | |finally resource.close()""" 87 | 88 | """try 89 | |resource.useIt() 90 | |catch { case e => } 91 | |finally 92 | |resource.close()""" ==> 93 | """try 94 | | resource.useIt() 95 | |catch { case e => } 96 | |finally 97 | | resource.close()""" 98 | 99 | """try { 100 | | foo() 101 | |} catch { 102 | | case _ => bar() 103 | |}""" ==> 104 | """try { 105 | | foo() 106 | |} catch { 107 | | case _ => bar() 108 | |}""" 109 | 110 | """try { 111 | |println("bar") 112 | |} finally { 113 | |println("foo") 114 | |}""" ==> 115 | """try { 116 | | println("bar") 117 | |} finally { 118 | | println("foo") 119 | |}""" 120 | 121 | """try { 122 | | 123 | | } catch { 124 | | case _ => 125 | | } 126 | | finally { 127 | | close() 128 | | }""" ==> 129 | """try { 130 | | 131 | |} catch { 132 | | case _ => 133 | |} finally { 134 | | close() 135 | |}""" 136 | 137 | // 2.9 generalised catch tests: 138 | 139 | "try body catch implicitly[Handler[T]]" ==> "try body catch implicitly[Handler[T]]" 140 | 141 | """try a catch { 142 | |b 143 | |}""" ==> 144 | """try a catch { 145 | | b 146 | |}""" 147 | 148 | """try a catch 149 | | { 150 | |b 151 | |}""" ==> 152 | """try a catch { 153 | | b 154 | |}""" 155 | 156 | """try a catch 157 | |b""" ==> 158 | """try a catch 159 | | b""" 160 | 161 | """try a 162 | |catch b""" ==> 163 | """try a 164 | |catch b""" 165 | 166 | } 167 | -------------------------------------------------------------------------------- /scalariform/src/test/scala/com/danieltrinh/scalariform/formatter/AbstractFormatterTest.scala: -------------------------------------------------------------------------------- 1 | package scalariform.formatter 2 | 3 | import scalariform.parser._ 4 | import scalariform.lexer._ 5 | import scalariform.formatter.preferences._ 6 | import scala.util.parsing.input._ 7 | import scala.util.parsing.combinator._ 8 | import org.scalatest.FlatSpec 9 | import org.scalatest.matchers.ShouldMatchers 10 | import org.scalatest.TestFailedException 11 | import org.scalatest.TestPendingException 12 | import scalariform.ScalaVersions 13 | 14 | abstract class AbstractFormatterTest extends FlatSpec with ShouldMatchers with SpecificFormatter { 15 | 16 | def prettyPrint(s: String): String = 17 | //s.replaceAll("\n", "↵\n").replaceAll("\t", "↦" ).replaceAll(" ", "▵") 18 | s.replaceAll("\n", "¶\n").replaceAll("\t", "↦") //.replaceAll(" ", "▲") 19 | 20 | implicit def string2FormatTest(s: String)(implicit formattingPreferences: IFormattingPreferences = FormattingPreferences(), scalaVersion: String = ScalaVersions.DEFAULT_VERSION): FormatTest = 21 | FormatTest(s.stripMargin, formattingPreferences, scalaVersion) 22 | 23 | def testFailedException(message: String) = new TestFailedException(message = Some(message), cause = None, failedCodeStackDepth = 2) 24 | 25 | case class FormatTest(source: String, formattingPreferences: IFormattingPreferences, scalaVersion: String) { 26 | 27 | require(formattingPreferences != null) 28 | 29 | def ==>(expectedRaw: String) { 30 | it should ("format >>>" + prettyPrint(source) + "<<< as >>>" + prettyPrint(expectedRaw) + "<<< with preferences " + formattingPreferences + " in version " + scalaVersion) in { 31 | val expected = expectedRaw.stripMargin 32 | val actual = format(source, scalaVersion = scalaVersion)(formattingPreferences) 33 | if (debug) println("Actual = " + actual) 34 | if (expected != actual) 35 | throw testFailedException("Format failure:\n ---- Expected ---- \n" + prettyPrint(expected) + "<<<\n ---- but was----- \n" + prettyPrint(actual) + "<<<\n ---- Original: ----- \n" + prettyPrint(source) + "<<<.") 36 | val beforeTokens = ScalaLexer.tokenise(source, scalaVersion = scalaVersion) 37 | val afterTokens = ScalaLexer.tokenise(actual, scalaVersion = scalaVersion) 38 | val newlineTokenTypes = Set(Tokens.NEWLINE, Tokens.NEWLINES) 39 | if (beforeTokens.map(_.tokenType).find(!newlineTokenTypes.contains(_)) != afterTokens.map(_.tokenType).find(!newlineTokenTypes.contains(_))) 40 | throw testFailedException("Text as expected, but actual and expected tokens differ:\n ---- Before ---- \n" + beforeTokens + "\n ---- After ---- \n" + afterTokens + "\n") 41 | 42 | val actual2 = format(actual, scalaVersion = scalaVersion)(formattingPreferences) 43 | if (actual2 != actual) { 44 | throw testFailedException("Idempotency failure:\n ---- Expected ---- \n" + prettyPrint(actual) + "<<<\n ---- but was----- \n" + prettyPrint(actual2) + "<<<.") 45 | } 46 | val afterTokens2 = ScalaLexer.tokenise(actual2, scalaVersion = scalaVersion) 47 | if (afterTokens2.map(_.tokenType) != afterTokens.map(_.tokenType)) { 48 | throw testFailedException("Idempotency token inconsistency:\n ---- One ---- \n" + afterTokens2 + "\n ---- Twice ---- \n" + afterTokens2 + "\n") 49 | } 50 | } 51 | } 52 | 53 | def =/=>(expected: String): Because = { 54 | //println("Warning -- skipped test:\n" + source) 55 | new Because(expected) 56 | } 57 | 58 | class Because(expected: String) { 59 | def because(reason: String) = { 60 | //println("because " + reason) 61 | it should ("format >>>" + prettyPrint(source) + "<<< as >>>" + prettyPrint(expected) + "<<<, but did not because " + reason) in { 62 | throw new TestPendingException 63 | } 64 | } 65 | } 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /misc/src/main/scala/com/danieltrinh/scalariform/corpusscan/CorpusScanner.scala: -------------------------------------------------------------------------------- 1 | package scalariform.corpusscan 2 | 3 | import java.io.File 4 | 5 | import scala.io.Source 6 | 7 | import org.apache.commons.io.FileUtils 8 | 9 | import scalariform.commandline.ScalaFileWalker 10 | import scalariform.formatter._ 11 | import scalariform.formatter.preferences.FormattingPreferences 12 | import scalariform.lexer._ 13 | import scalariform.parser._ 14 | import scalariform.utils.Utils.writeText 15 | 16 | sealed trait ParseFault 17 | case object TokensDoNotCoverSource extends ParseFault 18 | case object UnsuccessfulParse extends ParseFault 19 | case object BadAstTokens extends ParseFault 20 | case class ParseException(e: Throwable) extends ParseFault 21 | 22 | object CorpusScanner extends SpecificFormatter { 23 | 24 | def attemptToParse(file: File): Option[ParseFault] = { 25 | val source = getText(file) 26 | val sourceAgain = ScalaLexer.rawTokenise(source).map(_.rawText).mkString 27 | if (source != sourceAgain) { 28 | FileUtils.writeStringToFile(new File("source"), source) 29 | FileUtils.writeStringToFile(new File("sourceAgain"), sourceAgain) 30 | return Some(TokensDoNotCoverSource) 31 | } 32 | val tokens = ScalaLexer.tokenise(source) 33 | try { 34 | val result = new ScalaParser(tokens.toArray).compilationUnitOrScript() 35 | if (result.tokens != tokens) { 36 | Some(BadAstTokens) 37 | } else 38 | None 39 | } catch { 40 | case e: ScalaParserException ⇒ Some(UnsuccessfulParse) 41 | } 42 | } 43 | 44 | private def getText(file: File) = Source.fromFile(file).mkString 45 | 46 | val prefs = FormattingPreferences() //.setPreference(AlignSingleLineCaseStatements, true) 47 | 48 | def formatFile(file: File) { 49 | val source = getText(file) 50 | val formatted = format(source)(prefs) 51 | val formatted2 = format(formatted)(prefs) 52 | if (formatted != formatted2) { 53 | FileUtils.writeStringToFile(new File("formatted"), formatted) 54 | FileUtils.writeStringToFile(new File("formatted2"), formatted2) 55 | require(formatted == formatted2, "Idempotency failure") 56 | } 57 | writeText(file, formatted) 58 | // for (parseFault <- attemptToParse(file)) 59 | // throw new RuntimeException(parseFault.toString) 60 | } 61 | 62 | type Result = CompilationUnit 63 | 64 | def parse(parser: ScalaParser) = parser.compilationUnitOrScript() 65 | 66 | def format(formatter: ScalaFormatter, result: Result) = formatter.format(result)(FormatterState(indentLevel = 0)) 67 | 68 | } 69 | 70 | object Runner { 71 | 72 | val corpusDir = "/home/matthew/coding/scala-corpus/repos2" 73 | // val corpusDir = "/home/matt/scala-corpus" 74 | 75 | def main(args: Array[String]) { 76 | formatInPlace() 77 | } 78 | 79 | def checkParser() { 80 | val files = ScalaFileWalker.findScalaFiles(corpusDir) 81 | var count = 0 82 | var parsedCount = 0 83 | for (file ← files) { 84 | val parsed = CorpusScanner.attemptToParse(file).isEmpty 85 | if (parsed) 86 | parsedCount += 1 87 | if (!parsed) 88 | println((if (parsed) "OK " else "FAIL!") + " -- " + file) 89 | count += 1 90 | } 91 | println("Total scanned: " + count) 92 | println("Number parsed successfully: " + parsedCount) 93 | println("Parse failures: " + (count - parsedCount)) 94 | } 95 | 96 | def formatInPlace() { 97 | var count = 0 98 | for (file ← ScalaFileWalker.findScalaFiles(corpusDir)) { 99 | print("Formatting: " + file) 100 | CorpusScanner.formatFile(file) 101 | val parseFaultOpt = CorpusScanner.attemptToParse(file) 102 | require(parseFaultOpt == None, parseFaultOpt.toString) 103 | println() 104 | count += 1 105 | } 106 | println(count + " files formatted.") 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /misc/src/main/scala/com/danieltrinh/scalariform/gui/ParseTreeModel.scala: -------------------------------------------------------------------------------- 1 | package scalariform.gui 2 | 3 | import javax.swing.event.TreeModelListener 4 | import javax.swing.tree._ 5 | import scalariform.lexer.Token 6 | import scalariform.parser.AstNode 7 | import scalariform.utils.Range 8 | 9 | class ParseTreeModel(rootAstNode: AstNode) extends TreeModel { 10 | 11 | def getDocumentRange(obj: AnyRef): Option[Range] = obj.asInstanceOf[TreeNode].range 12 | 13 | abstract sealed class TreeNode(name: String) { 14 | 15 | def children: List[TreeNode] 16 | 17 | lazy val range: Option[Range] = { 18 | val childRanges = children.flatMap(_.range) 19 | if (childRanges.isEmpty) 20 | None 21 | else 22 | Some(childRanges.reduceLeft(_ mergeWith _)) 23 | } 24 | 25 | override def toString = name 26 | 27 | } 28 | 29 | case class AstNodeNode(name: String, astNode: AstNode) extends TreeNode(name) { 30 | 31 | val fields = astNode.getFields 32 | 33 | lazy val children = fields flatMap { 34 | case (_, None) | (_, Nil) ⇒ None 35 | case (fieldName, value) ⇒ Some(makeTreeNode(value.asInstanceOf[AnyRef], fieldName)) 36 | } 37 | 38 | override def toString = { 39 | val typeName = astNode.getClass.getSimpleName 40 | (if (name != "") name + ": " else "") + typeName 41 | } 42 | 43 | } 44 | 45 | case class TokenNode(name: String, token: Token) extends TreeNode(name) { 46 | 47 | val children = Nil 48 | 49 | override lazy val range = Some(token.range) 50 | 51 | override def toString = name + ": " + token 52 | 53 | } 54 | 55 | case class ListNode(name: String, list: List[Any]) extends TreeNode(name) { 56 | 57 | lazy val children = list.zipWithIndex map { case (x, i) ⇒ makeTreeNode(x, i.toString) } 58 | 59 | } 60 | 61 | case class OptionNode(name: String, opt: Option[Any]) extends TreeNode(name) { 62 | 63 | lazy val children = opt map { x ⇒ makeTreeNode(x, "Some") } toList 64 | 65 | } 66 | 67 | case class EitherNode(name: String, either: Either[Any, Any]) extends TreeNode(name) { 68 | 69 | lazy val children = either match { 70 | case Left(obj) ⇒ List(makeTreeNode(obj, "Left")) 71 | case Right(obj) ⇒ List(makeTreeNode(obj, "Right")) 72 | } 73 | 74 | } 75 | 76 | case class PairNode(name: String, pair: (Any, Any)) extends TreeNode(name) { 77 | 78 | lazy val children = List(makeTreeNode(pair._1, "_1"), makeTreeNode(pair._2, "_2")) 79 | 80 | } 81 | 82 | case class TodoNode(name: String, obj: Any) extends TreeNode(name) { 83 | 84 | val children = Nil 85 | 86 | override def toString = name + ": " + obj 87 | 88 | } 89 | 90 | def makeTreeNode(obj: Any, name: String = ""): TreeNode = obj match { 91 | case astNode: AstNode ⇒ 92 | AstNodeNode(name, astNode) 93 | case token: Token ⇒ 94 | TokenNode(name, token) 95 | case list: List[_] ⇒ 96 | ListNode(name, list) 97 | case Some(x) ⇒ 98 | makeTreeNode(x, name) 99 | case None ⇒ 100 | OptionNode(name, None) 101 | case either: Either[_, _] ⇒ 102 | EitherNode(name, either) 103 | case pair: (_, _) ⇒ 104 | PairNode(name, pair) 105 | case _ ⇒ 106 | TodoNode(name, obj) 107 | } 108 | 109 | lazy val getRoot = AstNodeNode("root", rootAstNode) 110 | 111 | def getChildCount(obj: AnyRef) = getChildren(obj).length 112 | 113 | def getChild(parent: AnyRef, index: Int): AnyRef = getChildren(parent)(index) 114 | 115 | def getIndexOfChild(parent: AnyRef, child: AnyRef) = getChildren(parent).indexOf(child) 116 | 117 | def isLeaf(obj: AnyRef): Boolean = getChildCount(obj) == 0 118 | 119 | def addTreeModelListener(l: TreeModelListener) {} 120 | 121 | def removeTreeModelListener(l: TreeModelListener) {} 122 | 123 | def valueForPathChanged(path: TreePath, newValue: Any) {} 124 | 125 | private def getChildren(obj: AnyRef) = obj.asInstanceOf[TreeNode].children 126 | 127 | } 128 | 129 | -------------------------------------------------------------------------------- /scalariform/src/test/scala/com/danieltrinh/scalariform/formatter/IndentWithTabsTest.scala: -------------------------------------------------------------------------------- 1 | package scalariform.formatter 2 | 3 | import scalariform.parser._ 4 | import scalariform.formatter._ 5 | import scalariform.formatter.preferences._ 6 | 7 | // format: OFF 8 | class IndentWithTabsTest extends AbstractFormatterTest { 9 | 10 | implicit val formattingPreferences = FormattingPreferences.setPreference(IndentWithTabs, true) 11 | 12 | """class A { 13 | | 14 | |def meth() { 15 | | 16 | |println("42") // wibble 17 | |println("wobble") 18 | | 19 | |} 20 | | 21 | |}""" ==> 22 | """class A { 23 | | 24 | | def meth() { 25 | | 26 | | println("42") // wibble 27 | | println("wobble") 28 | | 29 | | } 30 | | 31 | |}""" 32 | 33 | """val n = 42 + 34 | |3""" ==> 35 | """val n = 42 + 36 | | 3""" 37 | 38 | """val xml = 39 | |bar 40 | |""" ==> 41 | """val xml = 42 | | bar 43 | |""" 44 | 45 | """foo( 46 | |alpha = "foo", 47 | |beta = "bar", 48 | |gamma = false)""" ==> 49 | """foo( 50 | | alpha = "foo", 51 | | beta = "bar", 52 | | gamma = false 53 | |)""" 54 | 55 | """foo( 56 | |"foo", 57 | |"bar", 58 | |false)""" ==> 59 | """foo( 60 | | "foo", 61 | | "bar", 62 | | false 63 | |)""" 64 | 65 | { 66 | implicit val formattingPreferences = FormattingPreferences 67 | .setPreference(IndentWithTabs, true) 68 | .setPreference(DanglingCloseParenthesis, Force) 69 | 70 | """foo( 71 | |alpha = "foo", 72 | |beta = "bar", 73 | |gamma = false)""" ==> 74 | """foo( 75 | | alpha = "foo", 76 | | beta = "bar", 77 | | gamma = false 78 | |)""" 79 | 80 | """foo( 81 | |"foo", 82 | |"bar", 83 | |false)""" ==> 84 | """foo( 85 | | "foo", 86 | | "bar", 87 | | false 88 | |)""" 89 | 90 | """foo( 91 | | "foo", 92 | | "bar", 93 | | false)""" ==> 94 | """foo( 95 | | "foo", 96 | | "bar", 97 | | false 98 | |)""" 99 | } 100 | 101 | { 102 | implicit val formattingPreferences = FormattingPreferences 103 | .setPreference(IndentWithTabs, true) 104 | .setPreference(DanglingCloseParenthesis, Preserve) 105 | 106 | """foo( 107 | |alpha = "foo", 108 | |beta = "bar", 109 | |gamma = false)""" ==> 110 | """foo( 111 | | alpha = "foo", 112 | | beta = "bar", 113 | | gamma = false)""" 114 | 115 | """foo( 116 | |"foo", 117 | |"bar", 118 | |false)""" ==> 119 | """foo( 120 | | "foo", 121 | | "bar", 122 | | false)""" 123 | 124 | """foo( 125 | | "foo", 126 | | "bar", 127 | | false)""" ==> 128 | """foo( 129 | | "foo", 130 | | "bar", 131 | | false)""" 132 | } 133 | 134 | { 135 | implicit val formattingPreferences = FormattingPreferences 136 | .setPreference(IndentWithTabs, true) 137 | .setPreference(DanglingCloseParenthesis, Prevent) 138 | 139 | """foo( 140 | |alpha = "foo", 141 | |beta = "bar", 142 | |gamma = false 143 | |)""" ==> 144 | """foo( 145 | | alpha = "foo", 146 | | beta = "bar", 147 | | gamma = false)""" 148 | 149 | """foo( 150 | |"foo", 151 | |"bar", 152 | |false 153 | |)""" ==> 154 | """foo( 155 | | "foo", 156 | | "bar", 157 | | false)""" 158 | 159 | """foo( 160 | | "foo", 161 | | "bar", 162 | | false 163 | |)""" ==> 164 | """foo( 165 | | "foo", 166 | | "bar", 167 | | false)""" 168 | } 169 | 170 | 171 | override val debug = false 172 | 173 | type Result = CompilationUnit 174 | 175 | def parse(parser: ScalaParser) = parser.compilationUnitOrScript 176 | 177 | def format(formatter: ScalaFormatter, result: Result) = formatter.format(result)(FormatterState()) 178 | 179 | } 180 | -------------------------------------------------------------------------------- /scalariform/src/test/scala/com/danieltrinh/scalariform/formatter/CommentFormatterTest.scala: -------------------------------------------------------------------------------- 1 | package scalariform.formatter 2 | 3 | import scalariform.parser._ 4 | import scalariform.formatter._ 5 | import scalariform.formatter.preferences._ 6 | 7 | // format: OFF 8 | class CommentFormatterTest extends AbstractFormatterTest { 9 | 10 | type Result = CompilationUnit 11 | 12 | def parse(parser: ScalaParser) = parser.scriptBody() 13 | 14 | def format(formatter: ScalaFormatter, result: Result) = formatter.format(result)(FormatterState()) 15 | 16 | override val debug = false 17 | 18 | """/** 19 | |*a 20 | |b 21 | | */c""" ==> 22 | """/** 23 | | * a 24 | | * b 25 | | */ 26 | |c""" 27 | 28 | """/** 29 | |*a 30 | |b 31 | | */""" ==> 32 | """/** 33 | | * a 34 | | * b 35 | | */ 36 | |""" 37 | 38 | """/** 39 | | * 40 | | *Wibble*/ 41 | |class X""" ==> 42 | """/** 43 | | * 44 | | * Wibble 45 | | */ 46 | |class X""" 47 | 48 | """/***/ 49 | |class A""" ==> 50 | """/***/ 51 | |class A""" 52 | 53 | """/** */ 54 | |class A""" ==> 55 | """/** */ 56 | |class A""" 57 | 58 | """/** a */ 59 | |class A""" ==> 60 | """/** a */ 61 | |class A""" 62 | 63 | """/** 64 | | * {{ 65 | | * wibble 66 | | * }} 67 | | */ 68 | |class A""" ==> 69 | """/** 70 | | * {{ 71 | | * wibble 72 | | * }} 73 | | */ 74 | |class A""" 75 | 76 | """/** 77 | |* 78 | |*/""" ==> 79 | """/** 80 | | * 81 | | */ 82 | |""" 83 | 84 | """/** a 85 | | * b */""" ==> 86 | """/** 87 | | * a 88 | | * b 89 | | */ 90 | |""" 91 | 92 | // nested comments 93 | """/** 94 | |/* 95 | |*/ 96 | |*/""" ==> 97 | """/** 98 | | * /* 99 | | * */ 100 | | */ 101 | |""" 102 | 103 | { 104 | implicit val formattingPreferences = FormattingPreferences.setPreference(MultilineScaladocCommentsStartOnFirstLine, true) 105 | 106 | """/** This method applies f to each 107 | | * element of the given list. 108 | | */""" ==> 109 | """/** This method applies f to each 110 | | * element of the given list. 111 | | */ 112 | |""" 113 | 114 | """/** Foo 115 | |Bar 116 | |*Baz */""" ==> 117 | """/** Foo 118 | | * Bar 119 | | * Baz 120 | | */ 121 | |""" 122 | 123 | """/** Foo 124 | |*/""" ==> 125 | """/** Foo 126 | | */ 127 | |""" 128 | 129 | """/** 130 | |*/""" ==> 131 | """/** 132 | | */ 133 | |""" 134 | } 135 | 136 | { 137 | implicit val formattingPreferences = FormattingPreferences.setPreference(PlaceScaladocAsterisksBeneathSecondAsterisk, true) 138 | 139 | """/** This method applies f to each 140 | | * element of the given list. 141 | | */""" ==> 142 | """/** 143 | | * This method applies f to each 144 | | * element of the given list. 145 | | */ 146 | |""" 147 | 148 | """/** Foo 149 | |Bar 150 | |*Baz */""" ==> 151 | """/** 152 | | * Foo 153 | | * Bar 154 | | * Baz 155 | | */ 156 | |""" 157 | 158 | """/** Foo 159 | |*/""" ==> 160 | """/** 161 | | * Foo 162 | | */ 163 | |""" 164 | 165 | """/** 166 | |*/""" ==> 167 | """/** 168 | | */ 169 | |""" 170 | } 171 | 172 | { 173 | implicit val formattingPreferences = FormattingPreferences 174 | .setPreference(MultilineScaladocCommentsStartOnFirstLine, true) 175 | .setPreference(PlaceScaladocAsterisksBeneathSecondAsterisk, true) 176 | """/** This method applies f to each 177 | | * element of the given list. 178 | | */""" ==> 179 | """/** This method applies f to each 180 | | * element of the given list. 181 | | */ 182 | |""" 183 | 184 | """/** Foo 185 | |Bar 186 | |*Baz */""" ==> 187 | """/** Foo 188 | | * Bar 189 | | * Baz 190 | | */ 191 | |""" 192 | 193 | """/** Foo 194 | |*/""" ==> 195 | """/** Foo 196 | | */ 197 | |""" 198 | 199 | """/** 200 | |*/""" ==> 201 | """/** 202 | | */ 203 | |""" 204 | 205 | """/** This method applies f to each 206 | | * element of the given list. 207 | | */""" ==> 208 | """/** This method applies f to each 209 | | * element of the given list. 210 | | */ 211 | |""" 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /scalariform/src/test/scala/com/danieltrinh/scalariform/formatter/ParenAndBracketSpacingTest.scala: -------------------------------------------------------------------------------- 1 | package scalariform.formatter 2 | 3 | import scalariform.formatter.preferences._ 4 | 5 | class ParenAndBracketSpacingTest extends AbstractExpressionFormatterTest { 6 | 7 | { 8 | implicit val formattingPreferences = FormattingPreferences.setPreference(SpaceInsideParentheses, true) 9 | "()" ==> "()" 10 | "(a: Int) => 3" ==> "( a: Int ) => 3" 11 | "(3)" ==> "( 3 )" 12 | "(3, 4)" ==> "( 3, 4 )" 13 | "for (n <- 1 to 10) yield foo(n, n)" ==> "for ( n <- 1 to 10 ) yield foo( n, n )" 14 | } 15 | 16 | { 17 | implicit val formattingPreferences = FormattingPreferences.setPreference(SpaceInsideBrackets, true) 18 | "x: List[String]" ==> "x: List[ String ]" 19 | "foo[Bar](baz)" ==> "foo[ Bar ](baz)" 20 | "{ class A[B] { private[this] val bob } }" ==> "{ class A[ B ] { private[ this ] val bob } }" 21 | "super[X].y" ==> "super[ X ].y" 22 | "foo[Bar](baz)[Biz]" ==> "foo[ Bar ](baz)[ Biz ]" 23 | "foo[Bar][Baz][Buz]" ==> "foo[ Bar ][ Baz ][ Buz ]" 24 | 25 | """foo( 26 | |alpha = "foo", 27 | |beta = bar match { 28 | | case _ => "bar" 29 | |}, 30 | |gamma = false)""" ==> 31 | """foo( 32 | | alpha = "foo", 33 | | beta = bar match { 34 | | case _ => "bar" 35 | | }, 36 | | gamma = false 37 | |)""" 38 | 39 | """foo( 40 | |alpha = "foo", 41 | |beta = bar( 42 | |a = 1 43 | |), 44 | |gamma = false)""" ==> 45 | """foo( 46 | | alpha = "foo", 47 | | beta = bar( 48 | | a = 1 49 | | ), 50 | | gamma = false 51 | |)""" 52 | 53 | """foo( 54 | |arg = bar( 55 | |baz = "a" 56 | |).xyz 57 | |)""" ==> 58 | """foo( 59 | | arg = bar( 60 | | baz = "a" 61 | | ).xyz 62 | |)""" 63 | } 64 | 65 | { 66 | implicit val formattingPreferences = FormattingPreferences.setPreference(DanglingCloseParenthesis, Force) 67 | """foo( 68 | |alpha = "foo", 69 | |beta = "bar", 70 | |gamma = false)""" ==> 71 | """foo( 72 | | alpha = "foo", 73 | | beta = "bar", 74 | | gamma = false 75 | |)""" 76 | 77 | """foo( 78 | |"foo", 79 | |"bar", 80 | |false)""" ==> 81 | """foo( 82 | | "foo", 83 | | "bar", 84 | | false 85 | |)""" 86 | } 87 | 88 | { 89 | implicit val formattingPreferences = FormattingPreferences.setPreference(DanglingCloseParenthesis, Preserve) 90 | """foo( 91 | |alpha = "foo", 92 | |beta = "bar", 93 | |gamma = false)""" ==> 94 | """foo( 95 | | alpha = "foo", 96 | | beta = "bar", 97 | | gamma = false)""" 98 | 99 | """foo( 100 | |alpha = "foo", 101 | |beta = "bar", 102 | |gamma = false 103 | |)""" ==> 104 | """foo( 105 | | alpha = "foo", 106 | | beta = "bar", 107 | | gamma = false 108 | |)""" 109 | 110 | """foo( 111 | |"foo", 112 | |"bar", 113 | |false)""" ==> 114 | """foo( 115 | | "foo", 116 | | "bar", 117 | | false)""" 118 | 119 | """foo( 120 | |"foo", 121 | |"bar", 122 | |false 123 | |)""" ==> 124 | """foo( 125 | | "foo", 126 | | "bar", 127 | | false 128 | |)""" 129 | } 130 | 131 | { 132 | implicit val formattingPreferences = FormattingPreferences.setPreference(DanglingCloseParenthesis, Prevent) 133 | """foo( 134 | |alpha = "foo", 135 | |beta = "bar", 136 | |gamma = false)""" ==> 137 | """foo( 138 | | alpha = "foo", 139 | | beta = "bar", 140 | | gamma = false)""" 141 | 142 | """foo( 143 | |alpha = "foo", 144 | |beta = "bar", 145 | |gamma = false 146 | |)""" ==> 147 | """foo( 148 | | alpha = "foo", 149 | | beta = "bar", 150 | | gamma = false)""" 151 | 152 | """foo( 153 | |"foo", 154 | |"bar", 155 | |false)""" ==> 156 | """foo( 157 | | "foo", 158 | | "bar", 159 | | false)""" 160 | 161 | """foo( 162 | |"foo", 163 | |"bar", 164 | |false 165 | |)""" ==> 166 | """foo( 167 | | "foo", 168 | | "bar", 169 | | false)""" 170 | 171 | """foo( 172 | |alpha = "foo", 173 | |beta = "bar", 174 | |gamma = false // comment 175 | |)""" ==> 176 | """foo( 177 | | alpha = "foo", 178 | | beta = "bar", 179 | | gamma = false // comment 180 | |)""" 181 | } 182 | 183 | } 184 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/lexer/Tokens.scala: -------------------------------------------------------------------------------- 1 | package scalariform.lexer 2 | 3 | object Tokens { 4 | 5 | val PACKAGE = TokenType("PACKAGE") 6 | val STAR = TokenType("STAR") 7 | val WHILE = TokenType("WHILE") 8 | val CASE = TokenType("CASE") 9 | val NEW = TokenType("NEW") 10 | val DO = TokenType("DO") 11 | val EQUALS = TokenType("EQUALS") 12 | val SUBTYPE = TokenType("SUBTYPE") 13 | val EOF = TokenType("EOF") 14 | val SEALED = TokenType("SEALED") 15 | val TYPE = TokenType("TYPE") 16 | val LBRACKET = TokenType("LBRACKET") 17 | val FINAL = TokenType("FINAL") 18 | val RPAREN = TokenType("RPAREN") 19 | val IMPORT = TokenType("IMPORT") 20 | val STRING_LITERAL = TokenType("STRING_LITERAL") 21 | val STRING_PART = TokenType("STRING_PART") 22 | val FLOATING_POINT_LITERAL = TokenType("FLOATING_POINT_LITERAL") 23 | val EXCLAMATION = TokenType("EXCLAMATION") 24 | val NEWLINES = TokenType("NEWLINES") 25 | val THIS = TokenType("THIS") 26 | val RETURN = TokenType("RETURN") 27 | val VAL = TokenType("VAL") 28 | val VAR = TokenType("VAR") 29 | val SUPER = TokenType("SUPER") 30 | val RBRACE = TokenType("RBRACE") 31 | val LINE_COMMENT = TokenType("LINE_COMMENT") 32 | val PRIVATE = TokenType("PRIVATE") 33 | val NULL = TokenType("NULL") 34 | val ELSE = TokenType("ELSE") 35 | val CHARACTER_LITERAL = TokenType("CHARACTER_LITERAL") 36 | val MATCH = TokenType("MATCH") 37 | val TRY = TokenType("TRY") 38 | val WS = TokenType("WS") 39 | val SUPERTYPE = TokenType("SUPERTYPE") 40 | val INTEGER_LITERAL = TokenType("INTEGER_LITERAL") 41 | val OP = TokenType("OP") 42 | val USCORE = TokenType("USCORE") 43 | val LOWER = TokenType("LOWER") 44 | val CATCH = TokenType("CATCH") 45 | val FALSE = TokenType("FALSE") 46 | val VARID = TokenType("VARID") 47 | val THROW = TokenType("THROW") 48 | val UPPER = TokenType("UPPER") 49 | val PROTECTED = TokenType("PROTECTED") 50 | val CLASS = TokenType("CLASS") 51 | val DEF = TokenType("DEF") 52 | val LBRACE = TokenType("LBRACE") 53 | val FOR = TokenType("FOR") 54 | val LARROW = TokenType("LARROW") 55 | val RARROW = TokenType("RARROW") 56 | val ABSTRACT = TokenType("ABSTRACT") 57 | val LPAREN = TokenType("LPAREN") 58 | val IF = TokenType("IF") 59 | val AT = TokenType("AT") 60 | val MULTILINE_COMMENT = TokenType("MULTILINE_COMMENT") 61 | val SYMBOL_LITERAL = TokenType("SYMBOL_LITERAL") 62 | val OBJECT = TokenType("OBJECT") 63 | val COMMA = TokenType("COMMA") 64 | val YIELD = TokenType("YIELD") 65 | val TILDE = TokenType("TILDE") 66 | val PLUS = TokenType("PLUS") 67 | val PIPE = TokenType("PIPE") 68 | val VIEWBOUND = TokenType("VIEWBOUND") 69 | val RBRACKET = TokenType("RBRACKET") 70 | val DOT = TokenType("DOT") 71 | val WITH = TokenType("WITH") 72 | val IMPLICIT = TokenType("IMPLICIT") 73 | val LAZY = TokenType("LAZY") 74 | val TRAIT = TokenType("TRAIT") 75 | val HASH = TokenType("HASH") 76 | val FORSOME = TokenType("FORSOME") 77 | val MINUS = TokenType("MINUS") 78 | val TRUE = TokenType("TRUE") 79 | val SEMI = TokenType("SEMI") 80 | val COLON = TokenType("COLON") 81 | val OTHERID = TokenType("OTHERID") 82 | val NEWLINE = TokenType("NEWLINE") 83 | val FINALLY = TokenType("FINALLY") 84 | val OVERRIDE = TokenType("OVERRIDE") 85 | val ARROW = TokenType("ARROW") 86 | val EXTENDS = TokenType("EXTENDS") 87 | val INTERPOLATION_ID = TokenType("INTERPOLATION_ID") 88 | val XML_START_OPEN = TokenType("XML_START_OPEN", isXml = true) 89 | val XML_EMPTY_CLOSE = TokenType("XML_EMPTY_CLOSE", isXml = true) 90 | val XML_TAG_CLOSE = TokenType("XML_TAG_CLOSE", isXml = true) 91 | val XML_END_OPEN = TokenType("XML_END_OPEN", isXml = true) 92 | val XML_WHITESPACE = TokenType("XML_WHITESPACE", isXml = true) 93 | val XML_ATTR_EQ = TokenType("XML_ATTR_EQ", isXml = true) 94 | val XML_ATTR_VALUE = TokenType("XML_ATTR_VALUE", isXml = true) 95 | val XML_NAME = TokenType("XML_NAME", isXml = true) 96 | val XML_PCDATA = TokenType("XML_PCDATA", isXml = true) 97 | val XML_COMMENT = TokenType("XML_COMMENT", isXml = true) 98 | val XML_CDATA = TokenType("XML_CDATA", isXml = true) 99 | val XML_UNPARSED = TokenType("XML_UNPARSED", isXml = true) 100 | val XML_PROCESSING_INSTRUCTION = TokenType("XML_PROCESSING_INSTRUCTION", isXml = true) 101 | 102 | val KEYWORDS = Set( 103 | ABSTRACT, CASE, CATCH, CLASS, DEF, 104 | DO, ELSE, EXTENDS, FINAL, 105 | FINALLY, FOR, FORSOME, IF, IMPLICIT, 106 | IMPORT, LAZY, MATCH, NEW, 107 | OBJECT, OVERRIDE, PACKAGE, PRIVATE, PROTECTED, 108 | RETURN, SEALED, SUPER, THIS, 109 | THROW, TRAIT, TRY, TYPE, 110 | VAL, VAR, WHILE, WITH, YIELD 111 | ) 112 | 113 | val COMMENTS = Set(LINE_COMMENT, MULTILINE_COMMENT, XML_COMMENT) 114 | 115 | val IDS = Set(VARID, PLUS, MINUS, STAR, PIPE, TILDE, EXCLAMATION) 116 | 117 | val LITERALS = Set(CHARACTER_LITERAL, INTEGER_LITERAL, FLOATING_POINT_LITERAL, STRING_LITERAL, STRING_PART, SYMBOL_LITERAL, TRUE, FALSE, NULL) 118 | 119 | } 120 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/utils/Utils.scala: -------------------------------------------------------------------------------- 1 | package scalariform.utils 2 | 3 | import java.io.FileOutputStream 4 | import java.io.FileInputStream 5 | import java.io.IOException 6 | 7 | object Utils { 8 | 9 | def when[T](b: Boolean)(x: ⇒ T): Option[T] = if (b) Some(x) else None 10 | 11 | def asInstanceOf[T](o: Any) = if (o.isInstanceOf[T]) Some(o.asInstanceOf[T]) else None 12 | 13 | def checkNotNull[T](item: T): T = { require(item != null); item } 14 | 15 | implicit def boolean2ImpliesWrapper(b: Boolean): ImpliesWrapper = new ImpliesWrapper(b) 16 | 17 | class ImpliesWrapper(b: Boolean) { 18 | def implies(b2: ⇒ Boolean) = if (!b) true else b2 19 | } 20 | 21 | implicit def string2PimpedString(s: String) = new PimpedString(s) 22 | 23 | class PimpedString(s: String) { 24 | def toIntOpt: Option[Int] = try Some(s.toInt) catch { case _: NumberFormatException ⇒ None } 25 | } 26 | 27 | def stagger[T](iterable: Iterable[T]) = iterable zip iterable.tail 28 | 29 | def pairWithPrevious[T](iterable: Iterable[T]): List[(Option[T], T)] = { 30 | if (iterable.isEmpty) 31 | Nil 32 | else { 33 | val previous = None :: (iterable.init map Some[T]).toList 34 | previous zip iterable 35 | } 36 | } 37 | 38 | def withPreviousAndNext[T](iterable: Iterable[T]): List[(Option[T], T, Option[T])] = { 39 | if (iterable.isEmpty) 40 | Nil 41 | else { 42 | val previous = None :: (iterable.init map Some[T]).toList 43 | val next = (iterable.tail map Some[T]).toList ::: List(None) 44 | previous zip iterable zip next map { case ((a, b), c) ⇒ (a, b, c) } 45 | } 46 | } 47 | 48 | import scala.reflect.Manifest 49 | implicit def any2optionable(x: AnyRef) = new { 50 | def matchInstance[B](implicit m: Manifest[B]): Option[B] = 51 | if (Manifest.singleType(x) <:< m) 52 | Some(x.asInstanceOf[B]) 53 | else 54 | None 55 | } 56 | 57 | def groupBy[A](eq: (A, A) ⇒ Boolean, lst: List[A]): List[List[A]] = 58 | lst match { 59 | case Nil ⇒ Nil 60 | case (x :: xs) ⇒ { 61 | val (ys, zs) = xs span { eq(x, _) } 62 | (x :: ys) :: groupBy(eq, zs) 63 | } 64 | } 65 | 66 | // Swing --------------------- 67 | 68 | def onSwingThread(proc: ⇒ Unit) = javax.swing.SwingUtilities.invokeLater(new Runnable() { def run() = proc }) 69 | 70 | import javax.swing.JTree 71 | import javax.swing.tree._ 72 | 73 | def expandAll(tree: JTree) { 74 | val root = tree.getModel().getRoot() 75 | expandAll(tree, new TreePath(root)) 76 | } 77 | 78 | private def expandAll(tree: JTree, parent: TreePath) { 79 | val node = parent.getLastPathComponent() 80 | val model = tree.getModel 81 | val children = 0 until model.getChildCount(node) map { model.getChild(node, _) } 82 | for (child ← children) { 83 | val path = parent.pathByAddingChild(child) 84 | expandAll(tree, path) 85 | } 86 | tree.expandPath(parent) 87 | } 88 | 89 | // File ------------------ 90 | 91 | def writeText(file: java.io.File, text: String, encodingOpt: Option[String] = None) { 92 | import java.io.{ OutputStreamWriter, FileOutputStream } 93 | val encoding = encodingOpt getOrElse (System getProperty "file.encoding") 94 | val writer = new OutputStreamWriter(new FileOutputStream(file), encoding) 95 | try 96 | writer.write(text) 97 | finally 98 | writer.close() 99 | } 100 | 101 | @throws(classOf[IOException]) 102 | def withFileInputStream[T](fileName: String)(p: FileInputStream ⇒ T): T = { 103 | var fis: FileInputStream = null 104 | try { 105 | fis = new FileInputStream(fileName) 106 | p(fis) 107 | } finally 108 | if (fis != null) 109 | fis.close() 110 | } 111 | 112 | @throws(classOf[IOException]) 113 | def withFileOutputStream[T](fileName: String)(p: FileOutputStream ⇒ T): T = { 114 | var fis: FileOutputStream = null 115 | try { 116 | fis = new FileOutputStream(fileName) 117 | p(fis) 118 | } finally 119 | if (fis != null) 120 | fis.close() 121 | } 122 | 123 | def time[T](s: String)(f: ⇒ T): T = { 124 | val start = System.currentTimeMillis 125 | val result = f 126 | val duration = System.currentTimeMillis - start 127 | println(s + ": " + duration + "ms") 128 | result 129 | } 130 | 131 | def digit2int(ch: Char, base: Int): Int = 132 | if ('0' <= ch && ch <= '9' && ch < '0' + base) 133 | ch - '0' 134 | else if ('A' <= ch && ch < 'A' + base - 10) 135 | ch - 'A' + 10 136 | else if ('a' <= ch && ch < 'a' + base - 10) 137 | ch - 'a' + 10 138 | else 139 | -1 140 | 141 | def deleteRange(text: String, range: Range): String = 142 | replaceRange(text, range, replacement = "") 143 | 144 | def replaceRange(text: String, range: Range, replacement: String): String = 145 | text.take(range.offset) + replacement + text.drop(range.offset + range.length) 146 | 147 | } 148 | 149 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/lexer/UnicodeEscapeReader.scala: -------------------------------------------------------------------------------- 1 | package scalariform.lexer 2 | 3 | import scalariform.lexer.CharConstants.SU 4 | import scalariform.utils.Utils._ 5 | 6 | object UnicodeEscapeDecoder { 7 | 8 | /** 9 | * Decode unicode escapes of the form "\u0061" in the given text. 10 | * If forgiveErrors is true, then no exception will be thrown on malformed escapes. 11 | */ 12 | @throws(classOf[ScalaLexerException]) 13 | def decode(text: String, forgiveErrors: Boolean = false): String = 14 | new UnicodeEscapeReader(text, forgiveErrors).mkString 15 | 16 | } 17 | 18 | trait IUnicodeEscapeReader extends Iterator[Char] { 19 | 20 | val text: String 21 | 22 | /** 23 | * @return true if all the available characters have been read. 24 | */ 25 | def isEof: Boolean 26 | 27 | /** 28 | * @return the next character from the post-decoded text 29 | */ 30 | @throws(classOf[ScalaLexerException]) 31 | def read(): Char 32 | 33 | /** 34 | * @return the corresponding unicode escape sequence if the last character read was decoded, otherwise None. 35 | */ 36 | def unicodeEscapeOpt: Option[String] 37 | 38 | def next() = read() 39 | 40 | def hasNext = !isEof 41 | 42 | /** 43 | * Return a clone of this reader initialised to the current state 44 | */ 45 | def copy: IUnicodeEscapeReader 46 | 47 | } 48 | 49 | class UnicodeEscapeReader(val text: String, forgiveErrors: Boolean = false) extends IUnicodeEscapeReader { 50 | 51 | private var pos: Int = 0 52 | 53 | private var unicodeEscapeSequence: String = null 54 | 55 | /** 56 | * To distinguish cases like "\\u" from unicode escape sequences. 57 | */ 58 | private var consecutiveBackslashCount = 0 59 | 60 | def copy: UnicodeEscapeReader = { 61 | val reader = new UnicodeEscapeReader(text, forgiveErrors) 62 | reader.pos = pos 63 | reader.unicodeEscapeSequence = unicodeEscapeSequence 64 | reader.consecutiveBackslashCount = consecutiveBackslashCount 65 | reader 66 | } 67 | 68 | def isEof = pos >= text.length 69 | 70 | @throws(classOf[ScalaLexerException]) 71 | def read(): Char = { 72 | val ch = consumeNextCharacter() 73 | unicodeEscapeSequence = null 74 | if (ch == '\\') 75 | if (nextChar == 'u' && consecutiveBackslashCount % 2 == 0) { 76 | consecutiveBackslashCount = 0 77 | readUnicodeChar(pos - 1) 78 | } else { 79 | consecutiveBackslashCount += 1 80 | ch 81 | } 82 | else { 83 | consecutiveBackslashCount = 0 84 | ch 85 | } 86 | } 87 | 88 | def unicodeEscapeOpt: Option[String] = Option(unicodeEscapeSequence) 89 | 90 | private def consumeNextCharacter(): Char = { 91 | val result = safeGet(pos) 92 | pos += 1 93 | result 94 | } 95 | 96 | private def nextChar = safeGet(pos) 97 | 98 | private def safeGet(pos: Int): Char = if (pos >= text.length) SU else text(pos) 99 | 100 | private def readUnicodeChar(startPos: Int): Char = { 101 | this.unicodeEscapeSequence = consumeUnicodeEscape() 102 | val decodedChar = decodeUnicodeChar(unicodeEscapeSequence takeRight 4 toList, unicodeEscapeSequence, startPos) 103 | decodedChar 104 | } 105 | 106 | private def consumeUnicodeEscape(): String = { 107 | val sb = new StringBuilder 108 | sb.append('\\') 109 | 110 | // Repeating u's are allowed in Unicode escapes (bizarrely enough): 111 | do sb.append(consumeNextCharacter()) 112 | while (nextChar == 'u') 113 | 114 | for (n ← 1 to 4) 115 | sb.append(consumeNextCharacter()) 116 | 117 | sb.toString 118 | } 119 | 120 | private def decodeUnicodeChar(digits: List[Char], unicodeEscapeSequence: String, startPos: Int): Char = { 121 | val List(digit1, digit2, digit3, digit4) = digits.map(digit2int(_, base = 16)) 122 | if (digit1 < 0 || digit2 < 0 || digit3 < 0 || digit4 < 0) 123 | if (forgiveErrors) 124 | ' ' 125 | else { 126 | val (line, column) = lineAndColumn(startPos) 127 | throw new ScalaLexerException("[" + line + ":" + column + "] error in unicode escape: '" + unicodeEscapeSequence + "'") 128 | } 129 | else 130 | (digit1 << 12 | digit2 << 8 | digit3 << 4 | digit4).toChar 131 | } 132 | 133 | private def lineAndColumn(offset: Int): (Int, Int) = { 134 | var line = 1 135 | var column = 1 136 | for (i ← 0 until offset) { 137 | if (text(i) == '\n') { 138 | line += 1 139 | column = 1 140 | } else 141 | column += 1 142 | } 143 | (line, column) 144 | } 145 | 146 | } 147 | 148 | class NoUnicodeEscapeReader(val text: String) extends IUnicodeEscapeReader { 149 | 150 | private var pos = 0 151 | 152 | def copy = { 153 | val reader = new NoUnicodeEscapeReader(text) 154 | reader.pos = pos 155 | reader 156 | } 157 | 158 | def isEof: Boolean = pos >= text.length 159 | 160 | def read(): Char = { 161 | val result = if (isEof) SU else text(pos) 162 | pos += 1 163 | result 164 | } 165 | 166 | def unicodeEscapeOpt: Option[String] = None 167 | 168 | } 169 | -------------------------------------------------------------------------------- /scalariform/src/test/scala/com/danieltrinh/scalariform/formatter/XmlExpressionFormatterTest.scala: -------------------------------------------------------------------------------- 1 | package scalariform.formatter 2 | 3 | import scalariform.parser._ 4 | import scalariform.formatter._ 5 | import scalariform.formatter.preferences._ 6 | 7 | // format: OFF 8 | class XmlExpressionFormatterTest extends AbstractExpressionFormatterTest { 9 | 10 | "" ==> "" 11 | 12 | "" ==> "" 13 | "" ==> "" 14 | 15 | "{foo}" ==> "{ foo }" 16 | "{foo}{bar}" ==> "{ foo }{ bar }" 17 | 18 | "1" ==> "1" 19 | " 1 " ==> " 1 " 20 | " b {c} d {e} f " ==> " b { c } d { e } f " 21 | "ABORT: { msg }" ==> "ABORT: { msg }" // See issue #27 22 | " {b} " ==> " { b } " 23 | 24 | """ 25 | |{foo} 26 | |""" ==> 27 | """ 28 | | { foo } 29 | |""" 30 | 31 | """ 32 | |foo 33 | |{bar} 34 | |baz 35 | |""" ==> 36 | """ 37 | | foo 38 | | { bar } 39 | | baz 40 | |""" 41 | 42 | """42 43 | |""" ==> 44 | """ 45 | | 42 46 | |""" 47 | 48 | """b()""" ==> 50 | """b()""" 54 | 55 | """b()""" ==> 57 | """b()""" 61 | 62 | """ 63 | |1""" ==> 64 | """ 65 | | 1 66 | |""" 67 | 68 | """1 69 | |2 70 | |3 71 | |""" ==> 72 | """ 73 | | 1 74 | | 2 75 | | 3 76 | |""" 77 | 78 | """{ 79 | |{ 80 | |println("Foo") 81 | |} 82 | |}""" ==> 83 | """{ 84 | | { 85 | | println("Foo") 86 | | } 87 | |}""" 88 | 89 | """{ 90 | | 91 | | { name.get } 92 | | { version.get } 93 | |}""" ==> 94 | """{ 95 | | 96 | | { name.get } 97 | | { version.get } 98 | | 99 | |}""" 100 | 101 | """{ 102 | |val b = 103 | | 104 | |}""" ==> 105 | """{ 106 | | val b = 107 | | 108 | | 109 | |}""" 110 | 111 | """{ 112 | | 113 | |

{ 1 } { 1 }

; 114 | | 115 | |

{ 1 }{ 1 }

116 | | 117 | |}""" ==> 118 | """{ 119 | | 120 | |

{ 1 } { 1 }

; 121 | | 122 | |

{ 1 }{ 1 }

123 | | 124 | |}""" 125 | 126 | 127 | """{ 128 | |{ } 129 | | { 130 | | 131 | | } 132 | |}""" ==> 133 | """{ 134 | | { } 135 | | { 136 | | 137 | | 138 | | 139 | | } 140 | |}""" 141 | 142 | """ 143 | |{ 144 | |42 145 | |} 146 | |""" ==> 147 | """ 148 | | { 149 | | 42 150 | | } 151 | |""" 152 | 153 | """ 154 | |42 155 | |{ 156 | |234 157 | |} 158 | |""" ==> 159 | """ 160 | | 42 161 | | { 162 | | 234 163 | | } 164 | |""" 165 | 166 | """ 167 | |{ 168 | |println("wibble") 169 | |} 170 | |""" ==> 171 | """ 172 | | { 173 | | println("wibble") 174 | | } 175 | |""" 176 | 177 | """42{ 178 | |println("foo")}""" ==> 179 | """42{ 180 | | println("foo") 181 | |}""" 182 | 183 | """{ 184 | | 185 | |val x = 186 | | 187 | | 188 | |42 189 | | 190 | | 191 | |}""" ==> 192 | """{ 193 | | 194 | | val x = 195 | | 196 | | 197 | | 42 198 | | 199 | | 200 | |}""" 201 | 202 | """{ 203 | | 204 | |val x = 205 | | 206 | | 207 | | 208 | |42 209 | | 210 | | 211 | |}""" ==> 212 | """{ 213 | | 214 | | val x = 215 | | 216 | | 217 | | 218 | | 42 219 | | 220 | | 221 | |}""" 222 | 223 | """{ 224 | |a 225 | | 226 | |}""" ==> 227 | """{ 228 | | a 229 | | 230 | |}""" 231 | 232 | """a match { 233 | | 234 | | case c 235 | | => 236 | | 237 | |}""" ==> 238 | """a match { 239 | | 240 | | case 241 | | c 242 | | => 243 | | 244 | |}""" 245 | 246 | { 247 | implicit val formattingPreferences = FormattingPreferences.setPreference(FormatXml, false) 248 | 249 | """ 250 | |b""" ==> 251 | """ 252 | |b""" 253 | 254 | """a match { 255 | | 256 | | case c 257 | | => 258 | | 259 | |}""" ==> 260 | """a match { 261 | | 262 | | case c 263 | | => 264 | | 265 | |}""" 266 | 267 | } 268 | 269 | "{
}" ==> "{
}" 270 | 271 | "a match { case {_*} => }" ==> "a match { case { _* } => }" 272 | 273 | override val debug = false 274 | 275 | } 276 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/lexer/NewlineInferencer.scala: -------------------------------------------------------------------------------- 1 | package scalariform.lexer 2 | 3 | import scalariform.lexer.Tokens._ 4 | 5 | /** 6 | * Logic for promoting intertoken whitespace/comments to a NEWLINE or NEWLINES token as required. 7 | */ 8 | class NewlineInferencer(delegate: WhitespaceAndCommentsGrouper) extends Iterator[Token] { 9 | 10 | import NewlineInferencer._ 11 | 12 | /** 13 | * Contains the next unprocessed token from the delegate, or null if we've processed them all. 14 | */ 15 | private var nextToken: Token = if (delegate.hasNext) delegate.next() else null 16 | 17 | private var previousToken: Token = _ 18 | 19 | /** 20 | * If not null, we emit this token rather than looking at nextToken (used for inserting NEWLINE or NEWLINES 21 | * into the stream). 22 | */ 23 | private var tokenToEmitNextTime: Token = _ 24 | 25 | /** 26 | * Keeps track of the nested regions which allow multiple statements or not. An item in the stack indicates the 27 | * expected closing token type for that region. 28 | */ 29 | private var regionMarkerStack: List[TokenType] = Nil 30 | 31 | def hasNext = nextToken != null || tokenToEmitNextTime != null 32 | 33 | def next(): Token = { 34 | val token = 35 | if (tokenToEmitNextTime == null) 36 | fetchNextToken() 37 | else { 38 | val result = tokenToEmitNextTime 39 | tokenToEmitNextTime = null 40 | result 41 | } 42 | updateRegionStack(token.tokenType, nextToken) 43 | previousToken = token 44 | token 45 | } 46 | 47 | /** 48 | * Multiple statements are allowed immediately inside "{..}" regions, but disallowed immediately inside "[..]", "(..)" 49 | * and "case ... =>" regions. 50 | */ 51 | private def updateRegionStack(tokenType: TokenType, nextToken: Token) = 52 | tokenType match { 53 | case LBRACE ⇒ 54 | regionMarkerStack ::= RBRACE 55 | case LPAREN ⇒ 56 | regionMarkerStack ::= RPAREN 57 | case LBRACKET ⇒ 58 | regionMarkerStack ::= RBRACKET 59 | case CASE if nextToken != null ⇒ 60 | val followingTokenType = nextToken.tokenType 61 | // "case class" and "case object" are excluded from the usual "case .. =>" region. 62 | if (followingTokenType != CLASS && followingTokenType != OBJECT) 63 | regionMarkerStack ::= ARROW 64 | case tokenType if regionMarkerStack.headOption == Some(tokenType) ⇒ 65 | regionMarkerStack = regionMarkerStack.tail 66 | case _ ⇒ 67 | } 68 | 69 | private def fetchNextToken(): Token = { 70 | val token = nextToken 71 | if (delegate.hasNext) 72 | nextToken = delegate.next() 73 | else 74 | nextToken = null 75 | if (shouldTranslateToNewline(previousToken, token, nextToken)) 76 | translateToNewline(token) 77 | else 78 | token 79 | } 80 | 81 | private def translateToNewline(currentToken: Token): Token = { 82 | val currentHiddenTokens = currentToken.associatedWhitespaceAndComments 83 | val rawText = delegate.text.substring(currentHiddenTokens.offset, currentHiddenTokens.lastCharacterOffset + 1) 84 | val text = if (currentHiddenTokens.containsUnicodeEscape) currentHiddenTokens.text else rawText 85 | val tokenType = if (containsBlankLine(text)) NEWLINES else NEWLINE 86 | val newlineToken = Token(tokenType, text, currentHiddenTokens.offset, rawText) 87 | currentToken.associatedWhitespaceAndComments_ = NoHiddenTokens 88 | newlineToken.associatedWhitespaceAndComments_ = currentHiddenTokens 89 | tokenToEmitNextTime = currentToken 90 | newlineToken 91 | } 92 | 93 | private def containsNewline(hiddenTokens: HiddenTokens) = 94 | hiddenTokens.exists(_.text contains '\n') 95 | 96 | private def shouldTranslateToNewline(previousToken: Token, currentToken: Token, nextToken: Token): Boolean = { 97 | val hiddenTokens = currentToken.associatedWhitespaceAndComments 98 | val currentTokenType = currentToken.tokenType 99 | if (!containsNewline(hiddenTokens)) 100 | false 101 | else if (TOKENS_WHICH_CANNOT_BEGIN_A_STATEMENT contains currentTokenType) 102 | false 103 | // else if (currentTokenType == CASE && !followingTokenIsClassOrObject(nextToken)) 104 | // false 105 | else if (regionMarkerStack.nonEmpty && regionMarkerStack.head != RBRACE) 106 | false 107 | else 108 | return previousToken != null && (TOKENS_WHICH_CAN_END_A_STATEMENT contains previousToken.tokenType) 109 | } 110 | 111 | private def followingTokenIsClassOrObject(nextToken: Token): Boolean = 112 | nextToken != null && (nextToken.tokenType == CLASS || nextToken.tokenType == OBJECT) 113 | 114 | private def containsBlankLine(s: String): Boolean = { 115 | var i = 0 116 | var inBlankLine = false 117 | while (i < s.length) { 118 | val c = s(i) 119 | if (inBlankLine) { 120 | if (c == '\n' || c == '\f') 121 | return true 122 | else if (c > ' ') 123 | inBlankLine = false 124 | } else if (c == '\n' || c == '\f') 125 | inBlankLine = true 126 | i += 1 127 | } 128 | false 129 | } 130 | 131 | } 132 | 133 | object NewlineInferencer { 134 | 135 | private val TOKENS_WHICH_CAN_END_A_STATEMENT: Set[TokenType] = Set( 136 | INTEGER_LITERAL, FLOATING_POINT_LITERAL, CHARACTER_LITERAL, STRING_LITERAL, SYMBOL_LITERAL, VARID, OTHERID, PLUS, 137 | MINUS, STAR, PIPE, TILDE, EXCLAMATION, THIS, NULL, TRUE, FALSE, RETURN, TYPE, XML_EMPTY_CLOSE, XML_TAG_CLOSE, 138 | XML_COMMENT, XML_CDATA, XML_UNPARSED, XML_PROCESSING_INSTRUCTION, USCORE, RPAREN, RBRACKET, RBRACE 139 | ) 140 | 141 | private val TOKENS_WHICH_CANNOT_BEGIN_A_STATEMENT: Set[TokenType] = Set( 142 | CATCH, ELSE, EXTENDS, FINALLY, FORSOME, MATCH, WITH, YIELD, COMMA, DOT, SEMI, COLON, /* USCORE, */ EQUALS, 143 | ARROW, LARROW, RARROW, SUBTYPE, VIEWBOUND, SUPERTYPE, HASH, LBRACKET, RPAREN, RBRACKET, RBRACE 144 | ) 145 | 146 | private val BLANK_LINE_PATTERN = """(?s).*\n\s*\n.*""" 147 | 148 | } 149 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | org.scalariform 6 | scalariform.parent 7 | 0.1.8-SNAPSHOT 8 | pom 9 | 10 | 11 | 12 | scm:git://github.com/daniel-trinh/scalariform.git 13 | https://github.com/daniel-trinh/scalariform 14 | 15 | 16 | 17 | 0.20.0 18 | 2.10.4 19 | UTF-8 20 | 3.0.4 21 | 2.11 22 | 1.0.2 23 | 2_10 24 | 25 | 26 | 27 | ${maven.version} 28 | 29 | 30 | 31 | 32 | scalariform 33 | scalariform.feature 34 | scalariform.update 35 | 36 | 37 | 38 | org.scala-lang 39 | scala-library 40 | ${scala.version} 41 | 42 | 43 | 44 | 45 | 46 | org.eclipse.tycho 47 | tycho-maven-plugin 48 | ${tycho.version} 49 | true 50 | 51 | 52 | org.eclipse.tycho 53 | target-platform-configuration 54 | ${tycho.version} 55 | 56 | p2 57 | consider 58 | 59 | 60 | 61 | 62 | 63 | org.codehaus.mojo 64 | buildnumber-maven-plugin 65 | 1.1 66 | 67 | 68 | validate 69 | 70 | create 71 | 72 | 73 | 74 | 75 | false 76 | false 77 | 7 78 | 79 | 80 | 81 | 82 | org.eclipse.tycho 83 | tycho-packaging-plugin 84 | ${tycho.version} 85 | 86 | '${version.suffix}-'yyyyMMddHHmm'-${buildNumber}' 87 | true 88 | 89 | 90 | 91 | net.alchim31.maven 92 | scala-maven-plugin 93 | 3.1.5 94 | 95 | 96 | 97 | add-source 98 | compile 99 | 100 | 101 | 102 | 103 | ${scala.version} 104 | 105 | -Xmx1024m 106 | -Xss4m 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | kepler 115 | p2 116 | http://download.eclipse.org/releases/kepler 117 | 118 | 119 | sonatype.release 120 | Sonatype maven release repository 121 | https://oss.sonatype.org/content/repositories/releases/ 122 | false 123 | 124 | 125 | sonatype.snapshot 126 | Sonatype maven snapshot repository 127 | https://oss.sonatype.org/content/repositories/snapshots 128 | 129 | daily 130 | 131 | 132 | 133 | 134 | 135 | scala-2.10.x 136 | 137 | 2_10 138 | 139 | 140 | 141 | scala-2.11.x 142 | 143 | 2.11.1 144 | 2_11 145 | 146 | 147 | 148 | org.scala-lang.modules 149 | scala-xml_${scala.binary.version} 150 | ${scala.xml.version} 151 | 152 | 153 | 154 | 155 | scala-2.12.x 156 | 157 | 2.12.0-SNAPSHOT 158 | 2_12 159 | 160 | 161 | 162 | org.scala-lang.modules 163 | scala-xml_${scala.binary.version} 164 | ${scala.xml.version} 165 | 166 | 167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /scalariform/src/main/scala/com/danieltrinh/scalariform/formatter/TemplateFormatter.scala: -------------------------------------------------------------------------------- 1 | package scalariform.formatter 2 | 3 | import scalariform.lexer.Tokens._ 4 | import scalariform.lexer.Token 5 | 6 | import scalariform.parser._ 7 | import scalariform.utils._ 8 | import scalariform.formatter.preferences._ 9 | 10 | trait TemplateFormatter { self: HasFormattingPreferences with AnnotationFormatter with HasHiddenTokenInfo with TypeFormatter with ExprFormatter with ScalaFormatter ⇒ 11 | 12 | def format(tmplDef: TmplDef)(implicit formatterState: FormatterState): FormatResult = { 13 | val TmplDef(markerTokens, name, typeParamClauseOpt, annotations, accessModifierOpt, paramClausesOpt, templateInheritanceSectionOpt, templateBodyOption) = tmplDef 14 | var formatResult: FormatResult = NoFormatResult 15 | for (typeParamClause ← typeParamClauseOpt) 16 | formatResult ++= format(typeParamClause.contents) 17 | 18 | for (annotation ← annotations) { 19 | formatResult = formatResult.before(annotation.firstToken, CompactEnsuringGap) 20 | formatResult ++= format(annotation) 21 | } 22 | 23 | for { 24 | accessModifier ← accessModifierOpt 25 | astNode ← (paramClausesOpt orElse templateInheritanceSectionOpt orElse templateBodyOption) 26 | firstToken ← astNode.firstTokenOption 27 | } formatResult = formatResult.formatNewlineOrOrdinary(firstToken, CompactEnsuringGap) 28 | 29 | for { 30 | paramClauses ← paramClausesOpt 31 | firstToken ← paramClauses.firstTokenOption 32 | } { 33 | if (annotations.size > 0) 34 | formatResult = formatResult.formatNewlineOrOrdinary(firstToken, CompactEnsuringGap) 35 | val doubleIndentParams = formattingPreferences(DoubleIndentClassDeclaration) && 36 | !templateInheritanceSectionOpt.exists { section ⇒ containsNewline(section) || hiddenPredecessors(section.firstToken).containsNewline } && 37 | templateBodyOption.exists(containsNewline(_)) 38 | formatResult ++= formatParamClauses(paramClauses, doubleIndentParams) 39 | } 40 | for (TemplateInheritanceSection(extendsOrSubtype, earlyDefsOpt, templateParentsOpt) ← templateInheritanceSectionOpt) { 41 | val doubleIndentTemplateInheritance = formattingPreferences(DoubleIndentClassDeclaration) && 42 | (templateBodyOption.exists(containsNewline(_)) || paramClausesOpt.exists(containsNewline(_))) 43 | val inheritanceIndent = if (doubleIndentTemplateInheritance) 2 else 1 44 | var currentFormatterState = formatterState 45 | if (hiddenPredecessors(extendsOrSubtype).containsNewline) { 46 | currentFormatterState = formatterState.indent(inheritanceIndent) 47 | formatResult = formatResult.before(extendsOrSubtype, currentFormatterState.currentIndentLevelInstruction) 48 | } 49 | for (EarlyDefs(earlyBody: TemplateBody, withOpt) ← earlyDefsOpt) 50 | formatResult ++= format(earlyBody)(currentFormatterState) 51 | 52 | for (templateParents ← templateParentsOpt) { 53 | val TemplateParents((type1: Type, argumentExprss: List[ArgumentExprs]), withTypes: List[(Token, Type, List[ArgumentExprs])]) = templateParents 54 | 55 | formatResult ++= format(type1)(currentFormatterState) 56 | for (argumentExprs ← argumentExprss) 57 | formatResult ++= format(argumentExprs)(currentFormatterState)._1 58 | 59 | for ((withToken, type_, argumentExprss2) ← withTypes) { 60 | if (hiddenPredecessors(withToken).containsNewline) { 61 | currentFormatterState = formatterState.indent(inheritanceIndent) 62 | formatResult = formatResult.before(withToken, currentFormatterState.currentIndentLevelInstruction) 63 | } 64 | formatResult ++= format(type_)(currentFormatterState) 65 | for (argumentExprs2 ← argumentExprss2) 66 | formatResult ++= format(argumentExprs2)(currentFormatterState)._1 67 | } 68 | } 69 | } 70 | 71 | for (templateBody ← templateBodyOption) 72 | formatResult ++= format(templateBody) 73 | 74 | formatResult 75 | } 76 | 77 | // TODO: Copy and pasted below 78 | private def format(templateBody: TemplateBody)(implicit formatterState: FormatterState): FormatResult = { 79 | val TemplateBody(newlineOpt, lbrace, statSeq, rbrace) = templateBody 80 | var formatResult: FormatResult = NoFormatResult 81 | newlineOpt match { 82 | case Some(newline) ⇒ 83 | formatResult = formatResult.formatNewline(newline, CompactEnsuringGap) 84 | case None ⇒ 85 | } 86 | 87 | val dummyBlock = BlockExpr(lbrace, Right(statSeq), rbrace) 88 | formatResult ++= format(dummyBlock, indent = true) 89 | formatResult 90 | } 91 | 92 | def format(template: Template)(implicit formatterState: FormatterState): FormatResult = { 93 | val Template(earlyDefsOpt: Option[EarlyDefs], templateParentsOpt: Option[TemplateParents], templateBodyOpt: Option[TemplateBody]) = template 94 | var formatResult: FormatResult = NoFormatResult 95 | 96 | for (EarlyDefs(earlyBody: TemplateBody, withOpt) ← earlyDefsOpt) 97 | formatResult ++= format(earlyBody) 98 | 99 | for (templateParents ← templateParentsOpt) 100 | formatResult ++= format(templateParents) 101 | 102 | // TODO: Copy and paste from above 103 | for (templateBody @ TemplateBody(newlineOpt, lbrace, statSeq, rbrace) ← templateBodyOpt) { 104 | newlineOpt match { 105 | case Some(newline) ⇒ 106 | formatResult = formatResult.formatNewline(newline, CompactEnsuringGap) 107 | case None ⇒ 108 | formatResult = formatResult.before(lbrace, CompactEnsuringGap) 109 | } 110 | 111 | val dummyBlock = BlockExpr(lbrace, Right(statSeq), rbrace) 112 | formatResult ++= format(dummyBlock, indent = true) 113 | } 114 | 115 | formatResult 116 | } 117 | 118 | private def format(templateParents: TemplateParents)(implicit formatterState: FormatterState): FormatResult = { 119 | var formatResult: FormatResult = NoFormatResult 120 | val TemplateParents((type1: Type, argumentExprss: List[ArgumentExprs]), withTypes: List[(Token, Type, List[ArgumentExprs])]) = templateParents 121 | formatResult ++= format(type1) 122 | for (argumentExprs ← argumentExprss) 123 | formatResult ++= format(argumentExprs)._1 124 | 125 | // TODO: Unify with TmplDef code 126 | 127 | val currentFormatterState = formatterState 128 | for ((withToken, type_, argumentExprss2) ← withTypes) { 129 | formatResult ++= format(type_)(currentFormatterState) 130 | for (argumentExprs2 ← argumentExprss2) 131 | formatResult ++= format(argumentExprs2)(currentFormatterState)._1 132 | } 133 | 134 | formatResult 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /project/Build.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | import sbt.Keys._ 3 | import com.typesafe.sbt.SbtScalariform 4 | import com.typesafe.sbt.SbtScalariform.ScalariformKeys 5 | import scalariform.formatter.preferences._ 6 | import xerial.sbt.Sonatype._ 7 | import xerial.sbt.Sonatype.SonatypeKeys._ 8 | import sbtassembly.AssemblyPlugin.autoImport._ 9 | 10 | object ScalariformBuild extends Build { 11 | 12 | // This is to make sure nobody tries to compile with 1.6 as the target JDK. 13 | // Not clear if this will actually work on 1.8, needs to be tested when that is out. 14 | val validateJavaVersion = taskKey[Unit]("Check if we are running using required Java version") 15 | val mismatchedSpecificationMessage = 16 | """|Java 1.7 is required for building the `misc` subproject of Scalariform. 17 | | 18 | |This is due to a dependency on the javax.swing library, which 19 | |had an API change from 1.6 to 1.7. 20 | | 21 | |Using 1.7 to build requires setting SBT to use JDK 1.7 or higher -- if SBT is 22 | |booting on JDK 1.6, you will get a javax.swing related compilation error.""".stripMargin 23 | 24 | lazy val commonSettings = Defaults.defaultSettings ++ SbtScalariform.defaultScalariformSettings ++ sonatypeSettings ++ Seq( 25 | organization := "org.scalariform", 26 | profileName := "org.scalariform", 27 | version := "0.1.8", 28 | scalaVersion := "2.10.6", 29 | crossScalaVersions := Seq( 30 | "2.11.7", 31 | "2.10.6", 32 | "2.9.3", "2.9.2" //"2.9.1-1", "2.9.1", "2.9.0-1", "2.9.0" 33 | ), 34 | exportJars := true, // Needed for cli oneJar 35 | retrieveManaged := true, 36 | scalacOptions += "-deprecation" 37 | ) 38 | 39 | lazy val subprojectSettings = commonSettings ++ Seq( 40 | ScalariformKeys.preferences <<= baseDirectory.apply(getScalariformPreferences)) 41 | 42 | def getScalariformPreferences(dir: File) = 43 | PreferencesImporterExporter.loadPreferences((dir / ".." / "formatterPreferences.properties").getPath) 44 | 45 | lazy val root: Project = Project("root", file("."), settings = commonSettings ++ Seq( 46 | publish := (), 47 | publishLocal := ()) 48 | ) aggregate (scalariform, cli, misc) 49 | 50 | 51 | implicit class Regex(sc: StringContext) { 52 | def r = new util.matching.Regex(sc.parts.mkString, sc.parts.tail.map(_ => "x"): _*) 53 | } 54 | 55 | def getScalaTestDependency(scalaVersion: String) = scalaVersion match { 56 | case r"2.11.\d+[-\w]*" | r"2.10.\d+[-\w]*" => "org.scalatest" %% "scalatest" % "2.2.4" % "test" 57 | case "2.9.3" => "org.scalatest" %% "scalatest" % "1.9.1" % "test" 58 | case _ => "org.scalatest" %% "scalatest" % "1.7.2" % "test" 59 | } 60 | 61 | def get2_11Dependencies(scalaVersion: String): List[ModuleID] = scalaVersion match { 62 | case r"2.11.\d+[-\w]*" => List( 63 | "org.scala-lang.modules" %% "scala-xml" % "1.0.1", 64 | "org.scala-lang.modules" %% "scala-parser-combinators" % "1.0.1" 65 | ) 66 | case _ => Nil 67 | } 68 | 69 | def publishSettings(projectName: String) = Seq( 70 | pomExtra := pomExtraXml, 71 | publishMavenStyle := true, 72 | publishArtifact in Test := false, 73 | publishArtifact in (Compile, packageDoc) := true, 74 | publishArtifact in (Compile, packageSrc) := true, 75 | pomIncludeRepository := { _ ⇒ false }, 76 | sbtbuildinfo.Plugin.buildInfoKeys := Seq[sbtbuildinfo.Plugin.BuildInfoKey](version), 77 | sourceGenerators in Compile <+= sbtbuildinfo.Plugin.buildInfo, 78 | sbtbuildinfo.Plugin.buildInfoPackage := projectName 79 | ) 80 | 81 | lazy val scalariform: Project = Project("scalariform", file("scalariform"), settings = 82 | subprojectSettings ++ sbtbuildinfo.Plugin.buildInfoSettings ++ publishSettings("scalariform") ++ 83 | Seq( 84 | libraryDependencies <<= (scalaVersion, libraryDependencies) { (sv, deps) ⇒ 85 | deps ++ get2_11Dependencies(sv) :+ getScalaTestDependency(sv) 86 | }, 87 | testOptions in Test += Tests.Argument("-oI"), 88 | publishTo <<= isSnapshot(getPublishToRepo))) 89 | 90 | def getPublishToRepo(isSnapshot: Boolean) = 91 | if (isSnapshot) 92 | Some("snapshots" at "https://oss.sonatype.org/content/repositories/snapshots") 93 | else 94 | Some("releases" at "https://oss.sonatype.org/service/local/staging/deploy/maven2") 95 | 96 | lazy val cli = Project("cli", file("cli"), settings = subprojectSettings ++ publishSettings("cli") ++ 97 | sbtbuildinfo.Plugin.buildInfoSettings ++ 98 | Seq( 99 | libraryDependencies += "commons-io" % "commons-io" % "1.4", 100 | mainClass in (Compile, packageBin) := Some("scalariform.commandline.Main"), 101 | mainClass in assembly := Some("scalariform.commandline.Main"), 102 | publishTo <<= isSnapshot(getPublishToRepo), 103 | artifact in (Compile, assembly) := { 104 | val art = (artifact in (Compile, assembly)).value 105 | art.copy(`classifier` = Some("assembly")) 106 | } 107 | ) ++ addArtifact(artifact in (Compile, assembly), assembly) 108 | ) dependsOn (scalariform) 109 | 110 | lazy val misc: Project = Project("misc", file("misc"), settings = subprojectSettings ++ 111 | Seq( 112 | libraryDependencies ++= Seq( 113 | "commons-io" % "commons-io" % "1.4", 114 | "com.miglayout" % "miglayout" % "3.7.4"), 115 | publish := (), 116 | publishLocal := (), 117 | validateJavaVersion := { 118 | val specJavaVersion = sys.props("java.specification.version") 119 | val compatibleJavaVersion = specJavaVersion == "1.7" || specJavaVersion == "1.8" 120 | if (!compatibleJavaVersion) 121 | sys.error(mismatchedSpecificationMessage) 122 | }, 123 | // this means we'll validate required Java version only _right before_ running the compile 124 | // command in misc subproject. In particular, build won't fail if user is not interested 125 | // in building `misc` subproject. 126 | compile in Compile := ((compile in Compile) dependsOn validateJavaVersion).value, 127 | mainClass in (Compile, run) := Some("scalariform.gui.Main"))) dependsOn (scalariform, cli) 128 | 129 | def pomExtraXml = 130 | 2010 131 | http://github.com/mdr/scalariform 132 | 133 | 134 | MIT License 135 | http://www.opensource.org/licenses/mit-license.php 136 | repo 137 | 138 | 139 | 140 | git@github.com:daniel-trinh/scalariform.git 141 | scm:git:git@github.com:daniel-trinh/scalariform 142 | 143 | 144 | 145 | mdr 146 | Matt Russell 147 | https://github.com/mdr/ 148 | 149 | 150 | daniel-trinh 151 | Daniel Trinh 152 | https://github.com/daniel-trinh/ 153 | 154 | 155 | 156 | } 157 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Scalariform documentation build configuration file, created by 4 | # sphinx-quickstart on Sat Apr 10 15:15:49 2010. 5 | # 6 | # This file is execfile()d with the current directory set to its containing dir. 7 | # 8 | # Note that not all possible configuration values are present in this 9 | # autogenerated file. 10 | # 11 | # All configuration values have a default; values that are commented out 12 | # serve to show the default. 13 | 14 | import sys, os 15 | 16 | # If extensions (or modules to document with autodoc) are in another directory, 17 | # add these directories to sys.path here. If the directory is relative to the 18 | # documentation root, use os.path.abspath to make it absolute, like shown here. 19 | #sys.path.append(os.path.abspath('.')) 20 | 21 | # -- General configuration ----------------------------------------------------- 22 | 23 | # Add any Sphinx extension module names here, as strings. They can be extensions 24 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 25 | extensions = ['github.tools.sphinx'] 26 | 27 | # Add any paths that contain templates here, relative to this directory. 28 | templates_path = ['_templates'] 29 | 30 | # The suffix of source filenames. 31 | source_suffix = '.rst' 32 | 33 | # The encoding of source files. 34 | #source_encoding = 'utf-8' 35 | 36 | # The master toctree document. 37 | master_doc = 'README' 38 | 39 | # General information about the project. 40 | project = u'Scalariform' 41 | copyright = u'2010, Matt Russell' 42 | 43 | # The version info for the project you're documenting, acts as replacement for 44 | # |version| and |release|, also used in various other places throughout the 45 | # built documents. 46 | # 47 | # The short X.Y version. 48 | version = '0.1.5-SNAPSHOT' 49 | # The full version, including alpha/beta/rc tags. 50 | release = '0.1.5-SNAPSHOT' 51 | 52 | # The language for content autogenerated by Sphinx. Refer to documentation 53 | # for a list of supported languages. 54 | #language = None 55 | 56 | # There are two options for replacing |today|: either, you set today to some 57 | # non-false value, then it is used: 58 | #today = '' 59 | # Else, today_fmt is used as the format for a strftime call. 60 | #today_fmt = '%B %d, %Y' 61 | 62 | # List of documents that shouldn't be included in the build. 63 | #unused_docs = [] 64 | 65 | # List of directories, relative to source directory, that shouldn't be searched 66 | # for source files. 67 | exclude_trees = [] 68 | 69 | # The reST default role (used for this markup: `text`) to use for all documents. 70 | #default_role = None 71 | 72 | # If true, '()' will be appended to :func: etc. cross-reference text. 73 | #add_function_parentheses = True 74 | 75 | # If true, the current module name will be prepended to all description 76 | # unit titles (such as .. function::). 77 | #add_module_names = True 78 | 79 | # If true, sectionauthor and moduleauthor directives will be shown in the 80 | # output. They are ignored by default. 81 | #show_authors = False 82 | 83 | highlight_language = 'scala' 84 | 85 | # The name of the Pygments (syntax highlighting) style to use. 86 | pygments_style = 'sphinx' 87 | 88 | # A list of ignored prefixes for module index sorting. 89 | #modindex_common_prefix = [] 90 | 91 | 92 | # -- Options for HTML output --------------------------------------------------- 93 | 94 | # The theme to use for HTML and HTML Help pages. Major themes that come with 95 | # Sphinx are currently 'default' and 'sphinxdoc'. 96 | html_theme = 'default' 97 | 98 | # Theme options are theme-specific and customize the look and feel of a theme 99 | # further. For a list of options available for each theme, see the 100 | # documentation. 101 | html_theme_options = { 102 | "nosidebar": "true" 103 | } 104 | 105 | # Add any paths that contain custom themes here, relative to this directory. 106 | #html_theme_path = [] 107 | 108 | # The name for this set of Sphinx documents. If None, it defaults to 109 | # " v documentation". 110 | #html_title = None 111 | 112 | # A shorter title for the navigation bar. Default is the same as html_title. 113 | #html_short_title = None 114 | 115 | # The name of an image file (relative to this directory) to place at the top 116 | # of the sidebar. 117 | #html_logo = None 118 | 119 | # The name of an image file (within the static path) to use as favicon of the 120 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 121 | # pixels large. 122 | #html_favicon = None 123 | 124 | # Add any paths that contain custom static files (such as style sheets) here, 125 | # relative to this directory. They are copied after the builtin static files, 126 | # so a file named "default.css" will overwrite the builtin "default.css". 127 | html_static_path = ['_static'] 128 | 129 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 130 | # using the given strftime format. 131 | #html_last_updated_fmt = '%b %d, %Y' 132 | 133 | # If true, SmartyPants will be used to convert quotes and dashes to 134 | # typographically correct entities. 135 | #html_use_smartypants = True 136 | 137 | # Custom sidebar templates, maps document names to template names. 138 | #html_sidebars = {} 139 | 140 | # Additional templates that should be rendered to pages, maps page names to 141 | # template names. 142 | #html_additional_pages = {} 143 | 144 | # If false, no module index is generated. 145 | #html_use_modindex = True 146 | 147 | # If false, no index is generated. 148 | #html_use_index = True 149 | 150 | # If true, the index is split into individual pages for each letter. 151 | #html_split_index = False 152 | 153 | # If true, links to the reST sources are added to the pages. 154 | #html_show_sourcelink = True 155 | 156 | # If true, an OpenSearch description file will be output, and all pages will 157 | # contain a tag referring to it. The value of this option must be the 158 | # base URL from which the finished HTML is served. 159 | #html_use_opensearch = '' 160 | 161 | # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). 162 | #html_file_suffix = '' 163 | 164 | # Output file base name for HTML help builder. 165 | htmlhelp_basename = 'Scalariformdoc' 166 | 167 | 168 | # -- Options for LaTeX output -------------------------------------------------- 169 | 170 | # The paper size ('letter' or 'a4'). 171 | #latex_paper_size = 'letter' 172 | 173 | # The font size ('10pt', '11pt' or '12pt'). 174 | #latex_font_size = '10pt' 175 | 176 | # Grouping the document tree into LaTeX files. List of tuples 177 | # (source start file, target name, title, author, documentclass [howto/manual]). 178 | latex_documents = [ 179 | ('README', 'Scalariform.tex', u'Scalariform Documentation', 180 | u'Matt Russell', 'manual'), 181 | ] 182 | 183 | # The name of an image file (relative to this directory) to place at the top of 184 | # the title page. 185 | #latex_logo = None 186 | 187 | # For "manual" documents, if this is true, then toplevel headings are parts, 188 | # not chapters. 189 | #latex_use_parts = False 190 | 191 | # Additional stuff for the LaTeX preamble. 192 | #latex_preamble = '' 193 | 194 | # Documents to append as an appendix to all manuals. 195 | #latex_appendices = [] 196 | 197 | # If false, no module index is generated. 198 | #latex_use_modindex = True 199 | -------------------------------------------------------------------------------- /scalariform/src/test/scala/com/danieltrinh/scalariform/formatter/CaseClausesFormatterTest.scala: -------------------------------------------------------------------------------- 1 | package scalariform.formatter 2 | 3 | import scalariform.parser._ 4 | import scalariform.formatter._ 5 | import scalariform.formatter.preferences._ 6 | 7 | // format: OFF 8 | class CaseClausesFormatterTest extends AbstractExpressionFormatterTest { 9 | 10 | override val debug = false 11 | 12 | """{ 13 | | case x => x * x 14 | | case y => y * 2 15 | |}""" ==> 16 | """{ 17 | | case x => x * x 18 | | case y => y * 2 19 | |}""" 20 | 21 | """{ 22 | |case 1 => // comment 23 | |2 24 | |}""" ==> 25 | """{ 26 | | case 1 => // comment 27 | | 2 28 | |}""" 29 | 30 | """{ 31 | |case x => 32 | |while (true) { 33 | |1 34 | |} 35 | |}""" ==> 36 | """{ 37 | | case x => 38 | | while (true) { 39 | | 1 40 | | } 41 | |}""" 42 | 43 | """a match { 44 | | case b => c(); 45 | |d() 46 | | }""" ==> 47 | """a match { 48 | | case b => 49 | | c(); 50 | | d() 51 | |}""" 52 | 53 | """a match { 54 | |case b => { 55 | |c 56 | |} 57 | |}""" ==> 58 | """a match { 59 | | case b => { 60 | | c 61 | | } 62 | |}""" 63 | 64 | """a match { 65 | |case b => { 66 | |c 67 | |} 68 | |d 69 | |}""" ==> 70 | """a match { 71 | | case b => 72 | | { 73 | | c 74 | | } 75 | | d 76 | |}""" 77 | 78 | """a match { case a => b; c; d }""" ==> 79 | """a match { case a => b; c; d }""" 80 | 81 | """a match { case a => b; c; 82 | |d }""" ==> 83 | """a match { 84 | | case a => 85 | | b; c; 86 | | d 87 | |}""" 88 | 89 | "a match { case b => ; c }" ==> "a match { case b => ; c }" 90 | 91 | // See issue #60 92 | """a match { 93 | |case b => 94 | |val c = d 95 | |case e => 96 | |}""" ==> 97 | """a match { 98 | | case b => 99 | | val c = d 100 | | case e => 101 | |}""" 102 | 103 | """a match { 104 | |/* foo*/ 105 | |case x if z=> 1 106 | |/* bar*/ 107 | |case yy => 2 108 | |/* baz*/ 109 | |case zzz => 3 110 | |}""" ==> 111 | """a match { 112 | | /* foo*/ 113 | | case x if z => 1 114 | | /* bar*/ 115 | | case yy => 2 116 | | /* baz*/ 117 | | case zzz => 3 118 | |}""" 119 | 120 | """a match { 121 | |case x => x 122 | |a => 123 | |x 124 | |case y => b => 125 | |b + 1 126 | |}""" ==> 127 | """a match { 128 | | case x => 129 | | x 130 | | a => 131 | | x 132 | | case y => b => 133 | | b + 1 134 | |}""" 135 | 136 | """a match { 137 | |case x => a => 138 | |x 139 | |case y => b => 140 | |b + 1 141 | |}""" ==> 142 | """a match { 143 | | case x => a => 144 | | x 145 | | case y => b => 146 | | b + 1 147 | |}""" 148 | 149 | { 150 | implicit val formattingPreferences = FormattingPreferences.setPreference(SpacesWithinPatternBinders, false) 151 | 152 | """a match { 153 | | case b(c @ ~()) => 154 | | case b(c@ ~()) => 155 | |}""" ==> 156 | """a match { 157 | | case b(c@ ~()) => 158 | | case b(c@ ~()) => 159 | |}""" 160 | } 161 | 162 | { 163 | 164 | implicit val formattingPreferences = FormattingPreferences.setPreference(AlignSingleLineCaseStatements, true) 165 | 166 | """a match { 167 | |case x => 1 168 | |case yy => 2 169 | |case zzz => 3 170 | |}""" ==> 171 | """a match { 172 | | case x => 1 173 | | case yy => 2 174 | | case zzz => 3 175 | |}""" 176 | 177 | """a match { 178 | |/* foo*/ 179 | |case x if z=> 1 180 | |/* bar*/ 181 | |case yy => 2 182 | |/* baz*/ 183 | |case zzz => 3 184 | |}""" ==> 185 | """a match { 186 | | /* foo*/ 187 | | case x if z => 1 188 | | /* bar*/ 189 | | case yy => 2 190 | | /* baz*/ 191 | | case zzz => 3 192 | |}""" 193 | 194 | """a match { 195 | |case b => 1; case c => 2 196 | |}""" ==> 197 | """a match { 198 | | case b => 1; case c => 2 199 | |}""" 200 | 201 | """a match { case b => 1; 202 | |case ccc => 2}""" ==> 203 | """a match { 204 | | case b => 1; 205 | | case ccc => 2 206 | |}""" 207 | 208 | """a match { 209 | | case b /* comment */ => 1 210 | | case c => 2 211 | |}""" ==> 212 | """a match { 213 | | case b /* comment */ => 1 214 | | case c => 2 215 | |}""" 216 | 217 | """a match { 218 | | case b 219 | |=> 1 220 | | case ccc => 2 221 | |}""" ==> 222 | """a match { 223 | | case b => 1 224 | | case ccc => 2 225 | |}""" 226 | 227 | """a match { 228 | | case b => 229 | | 1 230 | | case cc => 2 231 | |}""" ==> 232 | """a match { 233 | | case b => 234 | | 1 235 | | case cc => 2 236 | |}""" 237 | 238 | "{ case a=> b }" ==> "{ case a => b }"} 239 | 240 | """t match { 241 | | case Cell [ a ] (x: Int) => c.x = 5 242 | |}""" ==> 243 | """t match { 244 | | case Cell[a](x: Int) => c.x = 5 245 | |}""" 246 | 247 | 248 | { 249 | implicit val formattingPreferences = 250 | FormattingPreferences.setPreference(AlignSingleLineCaseStatements, true).setPreference(RewriteArrowSymbols, true) 251 | 252 | """a match { 253 | |case b => 42 254 | |case ccc => 24 255 | |}""" ==> 256 | """a match { 257 | | case b ⇒ 42 258 | | case ccc ⇒ 24 259 | |}""" 260 | } 261 | 262 | { 263 | 264 | implicit val formattingPreferences = 265 | FormattingPreferences 266 | .setPreference(AlignSingleLineCaseStatements, true) 267 | .setPreference(AlignSingleLineCaseStatements.MaxArrowIndent, 5) 268 | 269 | """x match { 270 | | case 123456789 => a 271 | | case _ => b 272 | |}""" ==> 273 | """x match { 274 | | case 123456789 => a 275 | | case _ => b 276 | |}""" 277 | 278 | } 279 | 280 | """a match { 281 | | case true => { 282 | | case true => false 283 | | case false => true 284 | | } 285 | | case false => b match { 286 | | case true => true 287 | | case false => false 288 | | } 289 | |}""" ==> 290 | """a match { 291 | | case true => { 292 | | case true => false 293 | | case false => true 294 | | } 295 | | case false => b match { 296 | | case true => true 297 | | case false => false 298 | | } 299 | |}""" 300 | 301 | 302 | "{ case a ::(b) ⇒ }" ==> 303 | "{ case a :: (b) ⇒ }" 304 | 305 | """{ 306 | | case a ⇒ 307 | | { 308 | | a 309 | | } 310 | |}""" ==> 311 | """{ 312 | | case a ⇒ 313 | | { 314 | | a 315 | | } 316 | |}""" 317 | 318 | 319 | { 320 | implicit val formattingPreferences = FormattingPreferences.setPreference(SpacesWithinPatternBinders, false) 321 | 322 | """(a: @switch) match { 323 | |case elem@Multi(values@_*) => 324 | |}""" ==> 325 | """(a: @switch) match { 326 | | case elem@Multi(values@_*) => 327 | |}""" 328 | } 329 | 330 | """(a: @switch) match { 331 | |case elem@Multi(values@_*) => 332 | |}""" ==> 333 | """(a: @switch) match { 334 | | case elem @ Multi(values @ _*) => 335 | |}""" 336 | 337 | } 338 | -------------------------------------------------------------------------------- /scalariform/src/test/scala/com/danieltrinh/scalariform/formatter/DefOrDclFormatterTest.scala: -------------------------------------------------------------------------------- 1 | 2 | package scalariform.formatter 3 | 4 | import scalariform.parser._ 5 | import scalariform.formatter._ 6 | import scalariform.formatter.preferences._ 7 | 8 | // format: OFF 9 | class DefOrDclFormatterTest extends AbstractFormatterTest { 10 | 11 | override val debug = false 12 | 13 | "def foo" ==> "def foo" 14 | 15 | "def foo()" ==> "def foo()" 16 | 17 | "def foo=42" ==> "def foo = 42" 18 | 19 | "def foo{doStuff()}" ==> "def foo { doStuff() }" 20 | 21 | "def foo={doStuff()}" ==> "def foo = { doStuff() }" 22 | 23 | "def foo ()" ==> "def foo()" 24 | 25 | "def foo(n:Int)" ==> "def foo(n: Int)" 26 | 27 | "def foo( n:Int,m:String )" ==> "def foo(n: Int, m: String)" 28 | 29 | "def foo:Int" ==> "def foo: Int" 30 | 31 | "def modN ( n :Int ) (x: Int) = ((x % n) == 0)" ==> "def modN(n: Int)(x: Int) = ((x % n) == 0)" 32 | 33 | "def foo(a: Int=123+2, b: Int=456+7)" ==> "def foo(a: Int = 123 + 2, b: Int = 456 + 7)" 34 | 35 | "def foo[X<%T1,Y<:T2,Z>:T3]()" ==> "def foo[X <% T1, Y <: T2, Z >: T3]()" 36 | 37 | "def foo(x: =>Int, y: Int *)" ==> "def foo(x: => Int, y: Int*)" 38 | 39 | "def a[+[+_]]" ==> "def a[+[+_]]" 40 | "def a[+]" ==> "def a[+]" 41 | "def a[-]" ==> "def a[-]" 42 | 43 | "def foo(x_ : Int)" ==> "def foo(x_ : Int)" 44 | 45 | "private def x" ==> "private def x" 46 | 47 | "private[pack]def x" ==> "private[pack] def x" 48 | "private[pack]val x" ==> "private[pack] val x" 49 | "private[pack]var x" ==> "private[pack] var x" 50 | 51 | """def a 52 | |: String""" ==> 53 | """def a: String""" 54 | 55 | """def foo = 56 | | if (true) 57 | | 1 58 | | else 59 | | 2""" ==> 60 | """def foo = 61 | | if (true) 62 | | 1 63 | | else 64 | | 2""" 65 | 66 | """val x = 67 | | if (true) 68 | | 42 69 | | else 70 | | 24""" ==> 71 | """val x = 72 | | if (true) 73 | | 42 74 | | else 75 | | 24""" 76 | 77 | """def a() 78 | |{ 79 | |b 80 | | }""" ==> 81 | """def a() { 82 | | b 83 | |}""" 84 | 85 | """type 86 | |B""" ==> 87 | """type B""" 88 | 89 | """type 90 | | 91 | |B""" ==> 92 | """type B""" 93 | 94 | "class A { val b = _.c }" ==> "class A { val b = _.c }" 95 | 96 | """def a() { return }""" ==> """def a() { return }""" 97 | 98 | """def a[B, 99 | |C] 100 | |()""" ==> 101 | """def a[B, C]()""" 102 | 103 | """private [a] 104 | |sealed trait B""" ==> 105 | """private[a] sealed trait B""" 106 | 107 | { 108 | 109 | implicit val formattingPreferences = 110 | FormattingPreferences 111 | .setPreference(IndentLocalDefs, true) 112 | 113 | """class A { 114 | | def b() = { 115 | | def c() = 42 116 | | println("d") 117 | | def e() = 118 | | 42 119 | |println("f") 120 | | def g() { 121 | |println("h") 122 | |} 123 | | c() * e() 124 | | } 125 | |}""" ==> 126 | """class A { 127 | | def b() = { 128 | | def c() = 42 129 | | println("d") 130 | | def e() = 131 | | 42 132 | | println("f") 133 | | def g() { 134 | | println("h") 135 | | } 136 | | c() * e() 137 | | } 138 | |}""" 139 | 140 | """val x = { i => 141 | | def a 142 | | def b 143 | |}""" ==> 144 | """val x = { i => 145 | | def a 146 | | def b 147 | |}""" 148 | 149 | // See issue #24 150 | """val inc: Int => Int = { i => 151 | | def plus1 = i + 1 152 | | plus1 153 | |}""" ==> 154 | """val inc: Int => Int = { i => 155 | | def plus1 = i + 1 156 | | plus1 157 | |}""" 158 | 159 | } 160 | 161 | { 162 | implicit val formattingPreferences = FormattingPreferences.setPreference(DanglingCloseParenthesis, Preserve) 163 | 164 | """def foo( 165 | | alpha: Int, 166 | | beta: String = "default", 167 | | gamma: Boolean = true, 168 | | delta: Boolean = false): Double = { 169 | | 170 | | bar 171 | |}""" ==> 172 | """def foo( 173 | | alpha: Int, 174 | | beta: String = "default", 175 | | gamma: Boolean = true, 176 | | delta: Boolean = false): Double = { 177 | | 178 | | bar 179 | |}""" 180 | 181 | """def foo( 182 | | alpha: Int, 183 | | beta: String = "default", 184 | | gamma: Boolean = true, 185 | | delta: Boolean = false 186 | |): Double = { 187 | | 188 | | bar 189 | |}""" ==> 190 | """def foo( 191 | | alpha: Int, 192 | | beta: String = "default", 193 | | gamma: Boolean = true, 194 | | delta: Boolean = false 195 | |): Double = { 196 | | 197 | | bar 198 | |}""" 199 | } 200 | 201 | { 202 | implicit val formattingPreferences = FormattingPreferences.setPreference(DanglingCloseParenthesis, Prevent) 203 | 204 | """private def foo( 205 | | alpha: Int, 206 | | beta: String = "default", 207 | | gamma: Boolean = true, 208 | | delta: Boolean = false 209 | |): Double = { 210 | | 211 | | bar 212 | |}""" ==> 213 | """private def foo( 214 | | alpha: Int, 215 | | beta: String = "default", 216 | | gamma: Boolean = true, 217 | | delta: Boolean = false): Double = { 218 | | 219 | | bar 220 | |}""" 221 | 222 | """case class FqnSymbol( 223 | | id: Option[Int], 224 | | file: String, // the underlying file 225 | | path: String, // the VFS handle (e.g. classes in jars) 226 | | fqn: String, 227 | | descriptor: Option[String], // for methods 228 | | internal: Option[String], // for fields 229 | | source: Option[String], // VFS 230 | | line: Option[Int], 231 | | offset: Option[Int] = None // future features: 232 | |)""" ==> 233 | """case class FqnSymbol( 234 | | id: Option[Int], 235 | | file: String, // the underlying file 236 | | path: String, // the VFS handle (e.g. classes in jars) 237 | | fqn: String, 238 | | descriptor: Option[String], // for methods 239 | | internal: Option[String], // for fields 240 | | source: Option[String], // VFS 241 | | line: Option[Int], 242 | | offset: Option[Int] = None // future features: 243 | |)""" 244 | } 245 | 246 | { 247 | implicit val formattingPreferences = FormattingPreferences.setPreference(DanglingCloseParenthesis, Force) 248 | 249 | """private def foo( 250 | | alpha: Int, 251 | | beta: String = "default", 252 | | gamma: Boolean = true, 253 | | delta: Boolean = false): Double = { 254 | | 255 | | bar 256 | |}""" ==> 257 | """private def foo( 258 | | alpha: Int, 259 | | beta: String = "default", 260 | | gamma: Boolean = true, 261 | | delta: Boolean = false 262 | |): Double = { 263 | | 264 | | bar 265 | |}""" 266 | } 267 | 268 | """def foo(n, m)""" ==> 269 | """def foo(n, m)""" 270 | 271 | """def test(test: ^^ *)""" ==> 272 | """def test(test: ^^ *)""" 273 | 274 | 275 | def parse(parser: ScalaParser) = parser.nonLocalDefOrDcl() 276 | 277 | type Result = FullDefOrDcl 278 | 279 | def format(formatter: ScalaFormatter, result: Result) = formatter.format(result)(FormatterState(indentLevel = 0)) 280 | 281 | 282 | } 283 | 284 | 285 | --------------------------------------------------------------------------------