I would like to use KtLint to format some generate...
# ktlint
r
I would like to use KtLint to format some generated code but I'm having some issue with backticks because the generated code contains some of them. Is it possible to tell KtLint to ignore them?
One example is the following enum:
Copy code
public enum class TransformCategory(
    public val nativeValue: GskTransformCategory,
) {
    UNKNOWN(GSK_TRANSFORM_CATEGORY_UNKNOWN),
    ANY(GSK_TRANSFORM_CATEGORY_ANY),
    `3D`(GSK_TRANSFORM_CATEGORY_3D),
    `2D`(GSK_TRANSFORM_CATEGORY_2D),
    `2D_AFFINE`(GSK_TRANSFORM_CATEGORY_2D_AFFINE),
    `2D_TRANSLATE`(GSK_TRANSFORM_CATEGORY_2D_TRANSLATE),
    IDENTITY(GSK_TRANSFORM_CATEGORY_IDENTITY),
    ;
}
The backticks are there because this code is generating some bindings for a C library. The generation is automated via GObject introspection so we can't easily avoid backticks.
p
It is not possible to ignore backtick generically. But in the case of the enum, following works with ktlint `0.48.x`:
Copy code
@Suppress("ktlint:enum-entry-name-case")
public enum class TransformCategory(
    public val nativeValue: GskTransformCategory,
) {
    UNKNOWN(GSK_TRANSFORM_CATEGORY_UNKNOWN),
    ANY(GSK_TRANSFORM_CATEGORY_ANY),
    `3D`(GSK_TRANSFORM_CATEGORY_3D),
    `2D`(GSK_TRANSFORM_CATEGORY_2D),
    `2D_AFFINE`(GSK_TRANSFORM_CATEGORY_2D_AFFINE),
    `2D_TRANSLATE`(GSK_TRANSFORM_CATEGORY_2D_TRANSLATE),
    IDENTITY(GSK_TRANSFORM_CATEGORY_IDENTITY),
    ;
}
r
unfortunately the bindings are generating several enums, for now I just disable the rule completely via `.editorconfig`:
Copy code
ktlint_standard_enum-entry-name-case = disabled
p
Or, if possible, generate the suppression statement
e
I don't see much reason to be checking the formatting of generated code
r
I don't want to check for formatting, I want to format the code, because KotlinPoet doesn't even allow to change the max line length and they just say to use a formatter like ktlint to deal with formatting of the code: https://github.com/square/kotlinpoet/issues/1020#issuecomment-737547356
e
I just leave the kotlinpoet output as-is, generally
but probably you could just run ktlint directly and ignore errors. I think the default callback doesn't do anything with them
Copy code
val ruleProviders = buildSet {
    ServiceLoader.load(RuleSetProviderV2::class.java).flatMapTo(this) { it.getRuleProviders() }
}
val ktLintRuleEngine = KtLintRuleEngine(ruleProviders = ruleProviders)
for (file in files) {
    file.writeText(ktLintRuleEngine.format(file.toPath()))
}
r
some generated code looks strange without formatting
but after running ktlint it looks much better:
p
If you invoke the
ktlintRuleEngine
to format the generated code, then you can disable the rule via the editorConfigOverride parameter instead of disabling the rule in the
.editorconfig
of the project. The advantage would be that the rule is kept enabled for non-generated code.
r
This seems very interesting, but I'm not familiar with
ktlintRuleEngine
. Is there some documentation that I can look at? The best would be a link to some open source project that is already doing it.
p
The Ktlint project has lots of unit tests that you can refer to 😉 But I think that this test case exactly matches with what you need https://github.com/pinterest/ktlint/blob/0.48.1-prep/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/DisabledRulesTest.kt#L100. This is based on the Ktlint
0.48.2
branch. This is refactored in the upcoming release, but that won’t be difficult to change.
r
Thanks!!
stupid question: which artifact is exposing
KtLintRuleEngine
? I've tried with these but still can't find it:
Copy code
ktlint = { module = "com.pinterest:ktlint", version.ref = "ktlint" }
ktlint-core = { module = "com.pinterest:ktlint-core", version.ref = "ktlint" }
ktlint-api-consumer = { module = "com.pinterest:ktlint-api-consumer", version.ref = "ktlint" }
ktlint-ruleset-standard = { module = "com.pinterest:ktlint-ruleset-standard", version.ref = "ktlint" }
p
Depends a bit against which ktlint version you are developing. There is a kind of big restructuring happening in current master.
r
I'm trying with the latest stable (I think):
0.48.2
r
oh ok sorry, I didn't noticed that for core, the group ID is different than from ktlint:
com.pinterest.ktlint:ktlint-core
vs
com.pinterest:ktlint
p
hmm, that was not intended …
e
p
In that case I will not change it. But it feels a bit unexpected to me.
It might be related to this remark:
Copy code
<!-- keeping original (pre 0.2.0) qualifier (com.github.shyiko:ktlint) so that no one would notice a thing -->
    <groupId>com.github.shyiko</groupId>
    <artifactId>ktlint</artifactId>
in https://central.sonatype.com/artifact/com.github.shyiko/ktlint/0.31.0
r
Hey, it works well! I can now format the file immediately after having generated them, without the need to involve Gradle 👍 There is only one issue left: is it possible to remove all the console output when I format? I get spammed with this logs for every single file it scans and I generate hundreds if not thousands of them:
Copy code
20:32:51.072 [main] DEBUG com.pinterest.ktlint.core.internal.RuleExecutionContext - Editor config properties for file '/home/leinardi/Workspace/gitlab/gtk-kn/bindings/core/gio/src/nativeMain/kotlin/bindings/gio/ThemedIcon.kt': {charset=charset = utf-8, indent_style=indent_style = space, insert_final_newline=insert_final_newline = true, max_line_length=max_line_length = 120, trim_trailing_whitespace=trim_trailing_whitespace = true, ij_continuation_indent_size=ij_continuation_indent_size = 4, ij_formatter_off_tag=ij_formatter_off_tag = @formatter:off, ij_formatter_on_tag=ij_formatter_on_tag = @formatter:on, ij_formatter_tags_enabled=ij_formatter_tags_enabled = true, ij_smart_tabs=ij_smart_tabs = false, ij_visual_guides=ij_visual_guides = 80, 100, 120, ij_wrap_on_typing=ij_wrap_on_typing = false, indent_size=indent_size = 4, tab_width=tab_width = 4, ktlint_standard_enum-entry-name-case=ktlint_standard_enum-entry-name-case = disabled, ij_kotlin_align_in_columns_case_branch=ij_kotlin_align_in_columns_case_branch = false, ij_kotlin_align_multiline_binary_operation=ij_kotlin_align_multiline_binary_operation = false, ij_kotlin_align_multiline_extends_list=ij_kotlin_align_multiline_extends_list = false, ij_kotlin_align_multiline_method_parentheses=ij_kotlin_align_multiline_method_parentheses = false, ij_kotlin_align_multiline_parameters=ij_kotlin_align_multiline_parameters = false, ij_kotlin_align_multiline_parameters_in_calls=ij_kotlin_align_multiline_parameters_in_calls = false, ij_kotlin_allow_trailing_comma=ij_kotlin_allow_trailing_comma = true, 
[...]
p
I think that should be possible but I am not sure about how to do that exactly when invoking the API directly. Ktlint exposes following extension function which impact the logger:
Copy code
/**
 * Set the [defaultLoggerModifier]. Note that it can only be set once. It should be set before the first invocation to
 * [initKtLintKLogger].
 */
public fun KLogger.setDefaultLoggerModifier(loggerModifier: (KLogger) -> Unit): KLogger {
and
Copy code
/**
 * Initializes the logger with the [defaultLoggerModifier].
 */
public fun KLogger.initKtLintKLogger(): KLogger {
Another thing that you might try is playing around with configuration of logback. In the unit tests of ktlint this configuration is used to affect logging: https://github.com/pinterest/ktlint/blob/0.48.2/ktlint-test-logging/src/main/resources/logback-test.xml
r
unfortunately I have a clash of dependencies: I'm using the version 4 of
kotlin-logger
and I'm using
log4j2
instead of
logback
...
even if I try to disable the ktlint logs like this:
Copy code
KotlinLogging
                .logger {}
                .setDefaultLoggerModifier { logger ->
                    (logger.underlyingLogger as Logger).level = Level.OFF
                }
it still messes with the logs of the rest of the code
is there any chance to get in the future an artifacts that doesn't log when used directly as dependency?
p
Sure, as long as if somebody can help me to figure out how to achieve that without breaking the logging functionality for other consumers
I have to go now…
r
btw, I saw that you are using logback because it allows to change the log level at runtime. I manage to achieve the same with log4j2:
Copy code
import io.github.oshai.KLogger
import io.github.oshai.KotlinLogging
import org.apache.logging.log4j.Level
import org.apache.logging.log4j.core.appender.ConsoleAppender
import org.apache.logging.log4j.core.config.Configurator
import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory

val logger: KLogger by lazy { KotlinLogging.logger("gir") }

private const val LOG4J_PATTERN = "%highlight{%-5level: [%c] %msg%n%throwable}" +
    "{FATAL=bright_red, ERROR=bright_red, WARN=bright_yellow, INFO=bright_green, DEBUG=bright_white, TRACE=white}"

fun configureLog4j(level: io.github.oshai.Level) {
    val builder = ConfigurationBuilderFactory.newConfigurationBuilder()
    val console = builder
        .newAppender("Stdout", "CONSOLE")
        .addAttribute("target", ConsoleAppender.Target.SYSTEM_OUT)
    console.add(builder.newLayout("PatternLayout").addAttribute("pattern", LOG4J_PATTERN))
    builder.add(console)
    builder.add(builder.newRootLogger(convertToLog4jLevel(level)).add(builder.newAppenderRef("Stdout")))
    Configurator.initialize(builder.build())
}

private fun convertToLog4jLevel(level: io.github.oshai.Level) = when (level) {
    io.github.oshai.Level.TRACE -> Level.TRACE
    io.github.oshai.Level.DEBUG -> Level.DEBUG
    <http://io.github.oshai.Level.INFO|io.github.oshai.Level.INFO> -> <http://Level.INFO|Level.INFO>
    io.github.oshai.Level.WARN -> Level.WARN
    io.github.oshai.Level.ERROR -> Level.ERROR
}
e
I'd expect that you can edit the configuration to target only logs from ktlint classes
Copy code
<logger name="com.pinterest.ktlint" level="NONE" />
or something like that
r
I'm not familiar with logback, where should I put this xml config? will it be loaded automatically by logback?
r
ok, I put it in
resources/logback.xml
and it works in the way that I don't see logs from ktlint. But the dependency clash still prevent my logs to work:
Copy code
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.jetbrains.kotlin.com.intellij.util.ReflectionUtil (file:/home/leinardi/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-compiler-embeddable/1.8.0/eb9118d4bcceaa2a94b2ae2a33a4ddba7c9a947f/kotlin-compiler-embeddable-1.8.0.jar) to field java.lang.Throwable.backtrace
WARNING: Please consider reporting this to the maintainers of org.jetbrains.kotlin.com.intellij.util.ReflectionUtil
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
Any plan to migrate to
kotlin-logger
v4?
p
The warnings above are related to using java 16+. You need to open-up some libraries to make them disappear. There is no specific reason for not upgrading the kotlin-logging dependency to v4 afaik. I will investigate if I can make it easier for API consumers to set the log level. Most likely, a parameter can be added to the ktlintRuleEngine to set the log-level.
r
Hey thanks, I found this issue with more info about the warnings: https://github.com/pinterest/ktlint/issues/1618 Unfortunately all the workaround I tried didn't work for me, maybe because I'm on JDK11? Anyway, I have good news about the logs: if I avoid importing
com.pinterest:ktlint
and instead I import only
ktlint-core
and the
ktlint-ruleset-*
I do not have any issues with the logs: ktlint doesn't log anything and log4j2 still works for me! Do you think there are some side effects in doing this (not importing
com.pinterest:ktlint
but only core and rulesets)?
p
I don’t expect any side effect. The root of the project only contains the build logic for all sub modules.
Would you be willing to write a kind of how-to-guide about what you tried to achieve and how you solved it? It would be nice to add this to the Ktlint documentation.
r
sure! Is it ok if I do it in this ticket I opened yesterday? https://github.com/pinterest/ktlint/issues/1875
p
Preferably as a PR on the Ktlint project in the markdown format we use. For example see https://raw.githubusercontent.com/pinterest/ktlint/master/docs/faq.md
r
OK, I'll try to do it this evening 👍
638 Views