Skip to content

Commit

Permalink
Rewrite interfaces to scala for Scala Native
Browse files Browse the repository at this point in the history
  • Loading branch information
jchyb committed Sep 23, 2024
1 parent c985670 commit c39eee8
Show file tree
Hide file tree
Showing 9 changed files with 372 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package org.scalafmt.interfaces

import java.nio.file.Path

/** An exception that happened at a position in a source file such as a parse
* error.
*/
abstract class PositionException(message: String, cause: Throwable)
extends Exception(message, cause) {

override def fillInStackTrace(): Throwable = synchronized(this)

/** @return
* The file where the error occurred.
*/
def file(): Path

/** @return
* The text contents of the file being formatted.
*/
def code(): String

/** @return
* The fully formatted error message including line content and caret.
*/
def longMessage(): String

/** @return
* Only the error message itself without line content and caret.
*/
def shortMessage(): String

def startLine(): Int
def startCharacter(): Int
def endLine(): Int
def endCharacter(): Int
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.scalafmt.interfaces

final class RepositoryCredential(
val host: String,
val username: String,
val password: String,
)

object RepositoryCredential {
trait ScalafmtExtension {
def withRepositoryCredentials(credentials: RepositoryCredential*): Scalafmt
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package org.scalafmt.interfaces

import java.nio.file.Path
import java.util
import java.util.NoSuchElementException
import java.util.ServiceLoader

/** A stable public interface to run Scalafmt.
*
* This interface is designed for integrations such as editor plugins and build
* tools. An implementation of this interface is available in the module
* 'scalafmt-dynamic'.
*
* It is recommended to use this interface over the org.scalafmt.Scalafmt
* object for several reasons:
*
* - this API is guaranteed to be binary compatible with all future versions
* of Scalafmt.
* - it downloads the Scalafmt version matching the 'version' setting in
* .scalafmt.conf. All versions down to v1.2.0 are supported.
* - it respects the 'project.{excludeFilters,includeFilters}' setting in
* .scalafmt.conf.
* - it uses the correct parser for `*.sbt` and `*.sc` files.
* - it automatically caches parsing of configuration files avoiding
* redundant work when possible.
* - it has two external library dependencies (com.geirsson:coursier-small
* and com.typesafe:config), which is a smaller dependency footprint
* compared to scalafmt-core.
*/
trait Scalafmt {

/** Format a single file with the given .scalafmt.conf configuration.
*
* @param config
* the absolute path to the configuration file. This file must exist or an
* exception will be thrown.
* @param file
* relative or absolute path to the file being formatted. Used only for the
* path name, the file does not have to exist on disk.
* @param code
* the text contents to format.
* @return
* the formatted contents if formatting was successful, otherwise the
* original text contents.
*/
def format(config: Path, file: Path, code: String): String

/** Builder method.
*
* @param respectExcludeFilters
* If true (default), returns the original file contents when formatting a
* file that does not matches the project settings in .scalafmt.conf. If
* false, formats every file that is passed to the
* {@link #format(Path, Path, String)} method regardless of .scalafmt.conf
* settings.
* @return
* an updated interface instance controlling whether to respect the
* 'project.{excludeFilters,includeFilters}' setting in .scalafmt.conf.
*/
def withRespectProjectFilters(respectExcludeFilters: Boolean): Scalafmt

/** Builder method.
*
* @param respectVersion
* If true (default), refuses to format files when the 'version' setting is
* missing in .scalafmt.conf and users must update .scalafmt.conf to format
* files. If false, falls back to the default version provided via
* {@link #withDefaultVersion(String)} .
* @return
* an updated interface instance controlling whether to respect the
* 'version' setting in .scalafmt.conf.
*/
@Deprecated
def withRespectVersion(respectVersion: Boolean): Scalafmt

/** Builder method.
*
* @param defaultVersion
* the fallback Scalafmt version to use when there is no 'version' setting
* in `.scalafmt.conf`; N.B. ignored when
* {@link #withRespectVersion(boolean)} is true
* @return
* an updated interface instance with the default version set
*/
@Deprecated
def withDefaultVersion(defaultVersion: String): Scalafmt

/** Builder method.
*
* @param reporter
* Use this instance to report errors and information messages
* @return
* an updated interface instance with the reporter instance set
*/
def withReporter(reporter: ScalafmtReporter): Scalafmt

/** Builder method.
*
* @param repositories
* maven repositories to use when resolving
* @return
* an updated interface instance with the repositories to use to resolve
* dependencies.
*/
def withMavenRepositories(repositories: String*): Scalafmt

/** Builder method.
*
* @param credentials
* repository credentials to use when resolving
* @return
* an updated interface instance.
*/
def withRepositoryCredentials(credentials: RepositoryCredential*): Scalafmt

/** Clear internal caches such as classloaded Scalafmt instances.
*/
def clear(): Unit

/** Create a ScalafmtSession to format a batch of files using fixed
* configuration.
* @param config
* location of the configuration file
* @return
* a new session instance
*/
def createSession(config: Path): ScalafmtSession
}

object Scalafmt {

/** Classload a new instance of this Scalafmt instance.
*
* @param loader
* the classloader containing the 'scalafmt-dynamic' module.
* @return
* a new instance of this interface
* @throws NoSuchElementException
* if the classloader does not have the 'scalafmt-dynamic' module.
*/
def create(loader: ClassLoader): Scalafmt =
throw new Exception("Can't use different version for native CLI")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.scalafmt.interfaces

/** A classloader that shares only scalafmt-interfaces classes from the parent
* classloader.
*
* This classloader is intended to be used as a parent when class-loading
* scalafmt-dynamic. By using this classloader as a parent, it's possible to
* cast runtime instances from the scalafmt-dynamic classloader into
* `org.scalafmt.interfaces.Scalafmt` from this classloader.
*/
class ScalafmtClassLoader(val parent: ClassLoader) extends ClassLoader(null) {
@throws[ClassNotFoundException]
override protected def findClass(name: String): Class[_] =
if (name.startsWith("org.scalafmt.interfaces")) parent.loadClass(name)
else super.findClass(name)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package org.scalafmt.interfaces

class ScalafmtException(message: String, cause: Throwable)
extends RuntimeException(message, cause, true, false)
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package org.scalafmt.interfaces

import java.io.OutputStreamWriter
import java.io.PrintWriter
import java.nio.file.Path

/** A reporter to handle error and information messages from Scalafmt.
*/
trait ScalafmtReporter {

/** An error occurred while trying to process this file.
*
* @param file
* can be either a Scala source file or .scalafmt.conf.
* @param message
* the error message.
*/
def error(file: Path, message: String): Unit

/** An exception occurred while trying to process this file.
*
* @param file
* can be either a Scala source file or .scalafmt.conf.
* @param e
* the exception that occurred, has type {@link PositionException} when the
* error appeared as a position.
*/
def error(file: Path, e: Throwable): Unit

/** An exception occurred while trying to process this file.
*
* @param file
* can be either a Scala source file or .scalafmt.conf.
* @param message
* additional error message
* @param e
* the exception that occurred, has type {@link PositionException} when the
* error appeared as a position.
*/
def error(file: Path, message: String, e: Throwable): Unit =
if (e == null) error(file, message)
else error(file, new ScalafmtException(message, e))

/** This file was not formatted because it's excluded by project settings from
* .scalafmt.conf.
*
* @param file
* the file path that was not formatted.
*/
def excluded(file: Path): Unit

/** This .scalafmt.conf file is missing the 'version' setting.
*
* @param config
* the .scalafmt.conf file.
* @param defaultVersion
* the configured default Scalafmt version.
*/
def missingVersion(config: Path, defaultVersion: String): Unit = {
val message = String.format(
"missing setting 'version'. To fix this problem, add the following line to .scalafmt.conf: 'version=%s'.",
defaultVersion,
)
error(config, message)
}

/** Record the configuration file and version used
*
* @param config
* location of the configuration file parsed
* @param scalafmtVersion
* the version of scalafmt used to parse
*/
def parsedConfig(config: Path, scalafmtVersion: String): Unit

/** Use {@link #downloadOutputStreamWriter} instead.
*
* @return
* an instance of progress writer
*/
@deprecated
def downloadWriter(): PrintWriter

/** Use this writer for printing progress while downloading new Scalafmt
* versions.
*
* @return
* an instance of progress writer
*/
def downloadOutputStreamWriter(): OutputStreamWriter
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.scalafmt.interfaces

final class ScalafmtResult(val value: String, val exception: Throwable) {
def this(value: String) = this(value, null)
def this(exception: Throwable) = this(null, exception)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.scalafmt.interfaces

import java.nio.file.Path

/** A session based on a fixed configuration.
*/
trait ScalafmtSession {

/** Format a single file with the given configuration.
*
* @param file
* relative or absolute path to the file being formatted. Used only for the
* path name, the file does not have to exist on disk.
* @param code
* the text contents to format.
* @return
* the formatted contents if formatting was successful, otherwise the
* original text contents.
*/
def format(file: Path, code: String): String

/** Format a single file with the given configuration.
*
* @param file
* relative or absolute path to the file being formatted. Used only for the
* path name, the file does not have to exist on disk.
* @param code
* the text contents to format.
* @return
* the formatted contents if formatting was successful, otherwise an error.
*/
def formatOrError(file: Path, code: String): ScalafmtResult

/** Whether the path matches the 'project.{excludeFilters,includeFilters}'
* setting.
* @param file
* path to match
* @return
* true if the path matched the filters
*/
def matchesProjectFilters(file: Path): Boolean;

/** Whether this configuration intends to limit files to those managed by git.
*/
def isGitOnly: Boolean

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.scalafmt.interfaces

import java.nio.file.Path

trait ScalafmtSessionFactory {

/** Create a ScalafmtSession to format a batch of files using fixed
* configuration.
* @param config
* location of the configuration file
* @return
* a new session instance
*/
def createSession(config: Path): ScalafmtSession
}

0 comments on commit c39eee8

Please sign in to comment.