From 919d7987fad82f59f998a29d79a82116049af1b9 Mon Sep 17 00:00:00 2001 From: Christopher Vogt Date: Thu, 9 Mar 2017 00:54:45 -0500 Subject: refactor scalafmt to new plugin style turn replaceSections into helper function instead of trait method proguard scalafmt build currently suffers from non-deterministic formatting. Try a few times to reproduce commited Proguard.scala --- plugins/scalafmt/Scalafmt.scala | 145 ++++++++++++++++++++++--------------- plugins/scalafmt/build/build.scala | 2 +- 2 files changed, 89 insertions(+), 58 deletions(-) (limited to 'plugins/scalafmt') diff --git a/plugins/scalafmt/Scalafmt.scala b/plugins/scalafmt/Scalafmt.scala index 5f4e054..9d42cbd 100644 --- a/plugins/scalafmt/Scalafmt.scala +++ b/plugins/scalafmt/Scalafmt.scala @@ -8,75 +8,106 @@ import java.io.File import java.nio.file.Files._ import java.nio.file._ -/** - * This plugin provides scalafmt support for cbt. - * - */ +/** This plugin provides scalafmt support for cbt. */ trait Scalafmt extends BaseBuild { - /** - * Reformat scala source code according to `scalafmtConfig` rules - * - * @return always returns `ExitCode.Success` - */ - final def scalafmt: ExitCode = Scalafmt.format(sourceFiles, scalafmtConfig) - - /** - * Scalafmt formatting config. - * - * Tries to get style in following order: - * • project local .scalafmt.conf - * • global ~/.scalafmt.conf - * • default scalafmt config - * - * Override this task if you want to provide - * scalafmt config programmatically on your own. - */ - def scalafmtConfig: ScalafmtConfig = - Scalafmt.getStyle( - project = projectDirectory.toPath, - home = Option(System.getProperty("user.home")) map (p => Paths.get(p)) + /** Reformat scala source code according to `scalafmtConfig` rules */ + def scalafmt = { + val scalafmtLib = new ScalafmtLib(lib) + scalafmtLib.format( sourceFiles ).config( + scalafmtLib.loadConfig( + projectDirectory.toPath + ) getOrElse ScalafmtConfig.default ) + } } -object Scalafmt { +class ScalafmtLib(lib: Lib){ scalafmtLib => + def userHome = Option( System.getProperty("user.home") ).map(Paths.get(_)) - def getStyle(project: Path, home: Option[Path]): ScalafmtConfig = { - val local = getConfigPath(project) - val global = home flatMap getConfigPath - val customStyle = for { - configPath <- local.orElse(global) - style <- StyleCache.getStyleForFile(configPath.toString) - } yield style + /** Tries to load config from .scalafmt.conf in given directory or fallback directory */ + def loadConfig( directory: Path, fallback: Option[Path] = userHome ): Option[ScalafmtConfig] = { + def findIn( base: Path ): Option[Path] = { + Some( base.resolve(".scalafmt.conf").toAbsolutePath ).filter(isRegularFile(_)) + } - customStyle.getOrElse(ScalafmtConfig.default) + findIn( directory ).orElse( fallback flatMap findIn ) + .flatMap ( file => StyleCache.getStyleForFile(file.toString) ) } - def format(files: Seq[File], style: ScalafmtConfig): ExitCode = { - val results = files.filter(_.string endsWith ".scala").map(_.toPath).map{ path => - val original = new String(readAllBytes(path)) - org.scalafmt.Scalafmt.format(original, style) match { - case Formatted.Success(formatted) => - if (original != formatted) { - val tmpPath = Paths.get(path.toString ++ ".scalafmt-tmp") - write(tmpPath, formatted.getBytes) - move(tmpPath, path, StandardCopyOption.REPLACE_EXISTING) - Some(1) - } else { - Some(0) + case class format(files: Seq[File]){ + /** + * @param whiteSpaceInParenthesis more of a hack to make up for missing support in Scalafmt. Does not respect alignment and maxColumn. + */ + case class config( + config: ScalafmtConfig, + whiteSpaceInParenthesis: Boolean = false + ) extends (() => Seq[File]){ + def lib = scalafmtLib + def apply = { + val (successes, errors) = scalafmtLib.lib.transformFilesOrError( + files, + org.scalafmt.Scalafmt.format(_, config) match { + case Formatted.Success(formatted) => Right( + if( whiteSpaceInParenthesis ){ + scalafmtLib.whiteSpaceInParenthesis(formatted) + } else formatted + ) + case Formatted.Failure( e ) => Left( e ) } - case Formatted.Failure(e) => - System.err.println(s"Scalafmt failed for $path\nCause: $e\n") - None + ) + if(errors.nonEmpty) + throw new RuntimeException( + "Scalafmt failed to format some files:\n" ++ errors.map{ + case (file, error) => file.string ++ ": " ++ error.getMessage + }.mkString("\n"), + errors.head._2 + ) + successes } } - if(results.forall(_.nonEmpty)){ - System.err.println(s"Formatted ${results.flatten.sum} Scala sources") - ExitCode.Success - } else ExitCode.Failure } - private def getConfigPath(base: Path): Option[Path] = { - Some( base.resolve(".scalafmt.conf").toFile ) - .collect{ case f if f.exists && f.isFile => f.toPath.toAbsolutePath } + private def whiteSpaceInParenthesis: String => String = + Seq( + "(\\(+)([^\\s\\)])".r.replaceAllIn(_:String, m => m.group(1).mkString(" ") ++ " " ++ m.group(2) ), + "([^\\s\\(])(\\)+)".r.replaceAllIn(_:String, m => m.group(1) ++ " " ++ m.group(2).mkString(" ") ) + ).reduce(_ andThen _) + + def cbtRecommendedConfig = { + import org.scalafmt.config._ + val c = ScalafmtConfig.defaultWithAlign + c.copy( + maxColumn = 110, + continuationIndent = c.continuationIndent.copy( + defnSite = 2 + ), + align = c.align.copy( + tokens = AlignToken.default ++ Set( + // formatting for these alignment tokens seems to be deterministic right now + // Maybe because of scala meta. Killing the class loader between runs seems not to help. + // Starting a new jvm each time does. + AlignToken( "=>", ".*" ), + AlignToken( ":", ".*" ), + AlignToken( "=", ".*" ) + ) + AlignToken.caseArrow, + arrowEnumeratorGenerator = true, + mixedOwners = true + ), + binPack = c.binPack.copy( + parentConstructors = true + ), + spaces = c.spaces.copy( + inImportCurlyBraces = true + ), + lineEndings = LineEndings.unix, + newlines = c.newlines.copy( + penalizeSingleSelectMultiArgList = false + ), + runner = c.runner.copy( + optimizer = c.runner.optimizer.copy( + forceConfigStyleOnOffset = -1 + ) + ) + ) } } diff --git a/plugins/scalafmt/build/build.scala b/plugins/scalafmt/build/build.scala index b37d769..e592873 100644 --- a/plugins/scalafmt/build/build.scala +++ b/plugins/scalafmt/build/build.scala @@ -1,7 +1,7 @@ import cbt._ class Build(val context: Context) extends Plugin { - private val ScalafmtVersion = "0.5.7" + private val ScalafmtVersion = "0.6.2" override def dependencies = super.dependencies ++ -- cgit v1.2.3