-
-
Notifications
You must be signed in to change notification settings - Fork 382
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Spotless plugin support #4464
base: main
Are you sure you want to change the base?
Conversation
ktfmtVersion: String = "0.53", | ||
ktfmtOptions: Option[KtfmtOptions] = None, | ||
klintVersion: String = "1.5.0" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there some way to configure whether Spotless uses ktfmt
or ktlint
? AFAIK both of them provide formatters. Or is Spotless hardcoded to use one or the other?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's is currently no way to configure whether Spotless uses ktlint
. It's currently set as the default.
If a valid config for ktfmt
is provided via KotlinConfig(...)
in the build.mill
, then ktfmt
will also be used, alongside the default formatter i.e. ktlint
.
You want both of them to be configurable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need then, I think we can just follow the upstream convention
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Based on your answer, it sounds like you were asking about upstream Spotless, not my implementation?
My answer was regarding my implementation.
If you were specifically asking regarding the Spotless project, this is how they do it for Kotlin:
kotlin {
// by default the target is every '.kt' and '.kts` file in the java sourcesets
ktfmt() // has its own section below
ktlint() // has its own section below
diktat() // has its own section below
prettier() // has its own section below
licenseHeader '/* (C)$YEAR */' // or licenseHeaderFile
}
In the example build.gradle
file above, the presence of ktfmt()
would correspond to the invocation of the default version of ktfmt
via this config method ktfmt()
.
kotlin {
ktfmt("0.51")
...
}
In the example above, passing a specific version i.e. ktfmt("0.51")
would invoke a similar config method that takes a version argument: ktfmt(String version)
.
kotlin {
licenseHeader '/* (C)$YEAR */' // or licenseHeaderFile
}
In this example, ktfmt
will be skipped entirely since it is completely absent from the steps.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Following the upstream convention would mean refactoring KotlinConfig
and similar case classes to look like this:
case class KotlinConfig(
target: String = ".kt",
licenseHeader: Option[String] = None,
licenseHeaderFile: Option[String] = None,
override val licenseHeaderDelimiter: String = "(package |@file|import )",
ktfmt: Boolean = false,
ktfmtVersion: String = "0.53",
ktfmtOptions: Option[KtfmtOptions] = None,
klint: Boolean = false,
klintVersion: String = "1.5.0"
) extends JVMLangConfig {
require(
!(licenseHeader.isDefined && licenseHeaderFile.isDefined),
"Please specify only licenseHeader or licenseHeaderFile but not both"
)
def ktfmt(): KotlinConfig =
copy(ktfmt = true)
def ktfmt(version: String): KotlinConfig =
copy(ktfmt = true, ktfmtVersion = version)
def ktfmtOptions(options: KtfmtOptions): KotlinConfig =
copy(ktfmtOptions = Some(options))
def ktlint(): KotlinConfig =
copy(klint = true)
def ktlint(version: String): KotlinConfig =
copy(klint = true, klintVersion = version)
...
}
With this change, ktlint
would no longer added as a formatter step by default.
I think the config looks fine as is. Let's drop the |
Is there a reason you want to use forwarders rather than using the |
No reason other than I thought you wanted it to be similar to the style of Spotless in Groovy. |
Let's use the |
I've updated to use |
@lihaoyi Anything else left for this to be merged ? |
val googleFormatter = GoogleJavaFormat() | ||
.copy(version = "1.25.2") | ||
.copy(aosp = true) | ||
.copy(reflowLongStrings = true) | ||
.copy(formatJavadoc = false) | ||
.copy(reorderImports = false) | ||
.copy(groupArtifact = "com.google.googlejavaformat:google-java-format") | ||
|
||
val palantirFormatter = PalantirJavaFormat() | ||
.copy(version = "2.50.0") | ||
.copy(style = "GOOGLE") | ||
.copy(formatJavadoc = true) | ||
|
||
def jvmLangConfig = new JavaConfig() | ||
.copy(importOrder = Some(Seq())) | ||
.copy(formatter = Some(googleFormatter)) | ||
.copy(formatter = | ||
Some(palantirFormatter) | ||
) // this replaces `googleFormatter` as only 1 `JavaFormatter` can be active | ||
.copy(licenseHeader = Some("/* (C) 2025. Licensed under Apache-2.0. */\n")) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should not need to use .copy
here at all I think, we can just use the GoogleJavaFormat(...)
constructor. Same as the others?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed as requested.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer to have the top-level classes in their own files.
2a48a95
to
e57ddfe
Compare
@lefou Done. |
* Google Java Format version. Defaults to `1.25.2`. | ||
*/ | ||
def googleJavaFormatVersion: T[String] = Task { | ||
"1.25.2" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where does this version come from? Is this the newest version? Do users expect it to stay stable or use always the newest?
We should not hardcode any versions. We either should leave it undefined or use a BuildInfo
value that comes from the outer Mill build. That way, we can keep it up-to-date more easily.
* Google Java Format version. Defaults to `2.50.0`. | ||
*/ | ||
def palantirJavaFormatVersion: T[String] = Task { | ||
"2.50.0" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as for googleJavaFormatVersion
, versions should not be hardcoded. Have a look at PR #4552, which replaces another instance of a hardcoded version.
* Defaults to `0.53`. | ||
*/ | ||
def ktfmtVersion: T[String] = Task { | ||
"0.53" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should not be hardcoded. This version is already managed.
Versions.ktfmtVersion |
* Defaults to `1.5.0`. | ||
*/ | ||
def ktlintVersion: T[String] = Task { | ||
"1.5.0" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use managed Versions.ktlintVersion
instead.
* Scala Format version. Defaults to `3.8.1`. | ||
*/ | ||
def scalafmtVersion: T[String] = Task { | ||
"3.8.1" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't hardcode versions. This should be undefined or managed from the outer build.
sealed trait JavaFormatter | ||
|
||
case class GoogleJavaFormat( | ||
version: String = "1.25.2", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't hardcode versions.
} | ||
|
||
case class PalantirJavaFormat( | ||
version: String = "2.50.0", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't hardcode versions.
ktfmtVersion: String = "0.53", | ||
ktfmtOptions: Option[KtfmtOptions] = None, | ||
ktlintFlag: Boolean = false, | ||
ktlintVersion: String = "1.5.0" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't hardcode versions. These are already available via the Versions
object.
This PR adds official support in Mill for a
SpotlessModule
via the addition of 3 Spotless traits:JavaSpotlessModule
KotlinSpotlessModule
andScalaSpotlessModule
which can be used with
JavaModule
,KotlinModule
andScalaModule
respectively. This is because each of those 3 JVM languages have different 3rd-party dependencies.It works similar in style to
PalantirFormatModule
.This is intended to close #3888