├── .announce ├── .editorconfig ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yml │ ├── enhancement.md │ └── new_rule_proposal.md ├── PULL_REQUEST_TEMPLATE.md ├── actions │ └── setup-gradle-build │ │ └── action.yml └── workflows │ ├── generate-changelog.yml │ ├── publish-release-build.yml │ ├── publish-release-docs.yml │ ├── publish-snapshot-build.yml │ ├── publish-snapshot-docs.yml │ ├── pull-request-with-code.yml │ └── pull-request-without-code.yml ├── .gitignore ├── .homebrew ├── ADOPTERS.md ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── RELEASE_TESTING.MD ├── RELEASING.md ├── SIGNING.md ├── build-logic ├── build.gradle.kts ├── settings.gradle.kts └── src │ └── main │ └── kotlin │ ├── ktlint-dokka.gradle.kts │ ├── ktlint-kotlin-common.gradle.kts │ ├── ktlint-publication-library.gradle.kts │ └── ktlint-publication.gradle.kts ├── build.gradle.kts ├── documentation ├── readme.md ├── release-latest │ ├── docs │ │ ├── api │ │ │ ├── badge.md │ │ │ ├── custom-integration.md │ │ │ ├── custom-reporter.md │ │ │ ├── custom-rule-set.md │ │ │ ├── index.md │ │ │ └── overview.md │ │ ├── assets │ │ │ └── images │ │ │ │ ├── favicon.ico │ │ │ │ ├── ktlint-intellij-plugin-distract-free-mode-1.png │ │ │ │ ├── ktlint-intellij-plugin-distract-free-mode-2.png │ │ │ │ ├── ktlint-intellij-plugin-manual-mode.png │ │ │ │ ├── ktlint-intellij-plugin-preferences.png │ │ │ │ ├── ktlint-intellij-plugin-suppress-violation.png │ │ │ │ ├── module-dependencies.png │ │ │ │ ├── psi-viewer.png │ │ │ │ └── rule-dependencies.png │ │ ├── contributing │ │ │ ├── code-of-conduct.md │ │ │ ├── guidelines.md │ │ │ ├── index.md │ │ │ └── overview.md │ │ ├── faq.md │ │ ├── index.md │ │ ├── install │ │ │ ├── cli.md │ │ │ ├── index.md │ │ │ ├── integrations.md │ │ │ ├── setup.md │ │ │ └── snapshot-build.md │ │ ├── quick-start.md │ │ ├── readme.md │ │ └── rules │ │ │ ├── code-styles.md │ │ │ ├── configuration-intellij-idea.md │ │ │ ├── configuration-ktlint.md │ │ │ ├── dependencies.md │ │ │ ├── experimental.md │ │ │ ├── index.md │ │ │ └── standard.md │ ├── mkdocs.yml │ ├── overrides │ │ └── main.html │ └── serve-docs-locally.sh └── snapshot │ ├── docs │ ├── api │ │ ├── badge.md │ │ ├── custom-integration.md │ │ ├── custom-reporter.md │ │ ├── custom-rule-set.md │ │ ├── index.md │ │ └── overview.md │ ├── assets │ │ └── images │ │ │ ├── favicon.ico │ │ │ ├── ktlint-intellij-plugin-distract-free-mode-1.png │ │ │ ├── ktlint-intellij-plugin-distract-free-mode-2.png │ │ │ ├── ktlint-intellij-plugin-manual-mode.png │ │ │ ├── ktlint-intellij-plugin-preferences.png │ │ │ ├── ktlint-intellij-plugin-suppress-violation.png │ │ │ ├── module-dependencies.png │ │ │ ├── psi-viewer.png │ │ │ └── rule-dependencies.png │ ├── contributing │ │ ├── code-of-conduct.md │ │ ├── guidelines.md │ │ ├── index.md │ │ └── overview.md │ ├── faq.md │ ├── index.md │ ├── install │ │ ├── cli.md │ │ ├── index.md │ │ ├── integrations.md │ │ ├── setup.md │ │ └── snapshot-build.md │ ├── quick-start.md │ ├── readme.md │ └── rules │ │ ├── code-styles.md │ │ ├── configuration-intellij-idea.md │ │ ├── configuration-ktlint.md │ │ ├── dependencies.md │ │ ├── experimental.md │ │ ├── index.md │ │ └── standard.md │ ├── mkdocs.yml │ ├── overrides │ └── main.html │ └── serve-docs-locally.sh ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── jitpack.yml ├── ktlint-api-consumer ├── README.md ├── build.gradle.kts └── src │ ├── main │ ├── kotlin │ │ └── com │ │ │ └── example │ │ │ └── ktlint │ │ │ └── api │ │ │ └── consumer │ │ │ ├── KtlintApiConsumer.kt │ │ │ └── rules │ │ │ ├── CustomRuleSetProvider.kt │ │ │ └── NoVarRule.kt │ ├── readme.md │ └── resources │ │ └── simplelogger.properties │ └── test │ ├── kotlin │ └── com │ │ └── pinterest │ │ └── ktlint │ │ └── api │ │ └── consumer │ │ └── KtLintRuleEngineTest.kt │ └── readme.md ├── ktlint-bom ├── build.gradle.kts └── gradle.properties ├── ktlint-cli-reporter-baseline ├── api │ └── ktlint-cli-reporter-baseline.api ├── build.gradle.kts ├── gradle.properties └── src │ ├── main │ ├── kotlin │ │ └── com │ │ │ └── pinterest │ │ │ └── ktlint │ │ │ └── cli │ │ │ └── reporter │ │ │ └── baseline │ │ │ ├── Baseline.kt │ │ │ ├── BaselineReporter.kt │ │ │ └── BaselineReporterProvider.kt │ └── resources │ │ └── META-INF │ │ └── services │ │ └── com.pinterest.ktlint.cli.reporter.core.api.ReporterProviderV2 │ └── test │ ├── kotlin │ └── com │ │ └── pinterest │ │ └── ktlint │ │ └── cli │ │ └── reporter │ │ └── baseline │ │ ├── BaselineReporterTest.kt │ │ └── BaselineTest.kt │ └── resources │ ├── baseline-invalid.xml │ ├── baseline-valid.xml │ └── simplelogger.properties ├── ktlint-cli-reporter-checkstyle ├── api │ └── ktlint-cli-reporter-checkstyle.api ├── build.gradle.kts ├── gradle.properties └── src │ ├── main │ ├── kotlin │ │ └── com │ │ │ └── pinterest │ │ │ └── ktlint │ │ │ └── cli │ │ │ └── reporter │ │ │ └── checkstyle │ │ │ ├── CheckStyleReporter.kt │ │ │ └── CheckStyleReporterProvider.kt │ └── resources │ │ └── META-INF │ │ └── services │ │ └── com.pinterest.ktlint.cli.reporter.core.api.ReporterProviderV2 │ └── test │ └── kotlin │ └── com │ └── pinterest │ └── ktlint │ └── cli │ └── reporter │ └── checkstyle │ └── CheckStyleReporterTest.kt ├── ktlint-cli-reporter-core ├── api │ └── ktlint-cli-reporter-core.api ├── build.gradle.kts ├── gradle.properties └── src │ └── main │ └── kotlin │ └── com │ └── pinterest │ └── ktlint │ └── cli │ └── reporter │ └── core │ └── api │ ├── KtlintCliError.kt │ ├── KtlintVersion.kt │ ├── ReporterProviderV2.kt │ └── ReporterV2.kt ├── ktlint-cli-reporter-format ├── api │ └── ktlint-cli-reporter-format.api ├── build.gradle.kts ├── gradle.properties └── src │ ├── main │ ├── kotlin │ │ └── com │ │ │ └── pinterest │ │ │ └── ktlint │ │ │ └── cli │ │ │ └── reporter │ │ │ └── format │ │ │ ├── Color.kt │ │ │ ├── FormatReporter.kt │ │ │ └── FormatReporterProvider.kt │ └── resources │ │ └── META-INF │ │ └── services │ │ └── com.pinterest.ktlint.cli.reporter.core.api.ReporterProviderV2 │ └── test │ └── kotlin │ └── com │ └── pinterest │ └── ktlint │ └── cli │ └── reporter │ └── format │ ├── FormatReporterProviderTest.kt │ └── FormatReporterTest.kt ├── ktlint-cli-reporter-html ├── api │ └── ktlint-cli-reporter-html.api ├── build.gradle.kts ├── gradle.properties └── src │ ├── main │ ├── kotlin │ │ └── com │ │ │ └── pinterest │ │ │ └── ktlint │ │ │ └── cli │ │ │ └── reporter │ │ │ └── html │ │ │ ├── HtmlReporter.kt │ │ │ └── HtmlReporterProvider.kt │ └── resources │ │ └── META-INF │ │ └── services │ │ └── com.pinterest.ktlint.cli.reporter.core.api.ReporterProviderV2 │ └── test │ └── kotlin │ └── com │ └── pinterest │ └── ktlint │ └── cli │ └── reporter │ └── html │ └── HtmlReporterTest.kt ├── ktlint-cli-reporter-json ├── api │ └── ktlint-cli-reporter-json.api ├── build.gradle.kts ├── gradle.properties └── src │ ├── main │ ├── kotlin │ │ └── com │ │ │ └── pinterest │ │ │ └── ktlint │ │ │ └── cli │ │ │ └── reporter │ │ │ └── json │ │ │ ├── JsonReporter.kt │ │ │ └── JsonReporterProvider.kt │ └── resources │ │ └── META-INF │ │ └── services │ │ └── com.pinterest.ktlint.cli.reporter.core.api.ReporterProviderV2 │ └── test │ └── kotlin │ └── com │ └── pinterest │ └── ktlint │ └── cli │ └── reporter │ └── json │ └── JsonReporterTest.kt ├── ktlint-cli-reporter-plain-summary ├── api │ └── ktlint-cli-reporter-plain-summary.api ├── build.gradle.kts ├── gradle.properties └── src │ ├── main │ ├── kotlin │ │ └── com │ │ │ └── pinterest │ │ │ └── ktlint │ │ │ └── cli │ │ │ └── reporter │ │ │ └── plainsummary │ │ │ ├── PlainSummaryReporter.kt │ │ │ └── PlainSummaryReporterProvider.kt │ └── resources │ │ └── META-INF │ │ └── services │ │ └── com.pinterest.ktlint.cli.reporter.core.api.ReporterProviderV2 │ └── test │ └── kotlin │ └── com │ └── pinterest │ └── ktlint │ └── cli │ └── reporter │ └── plain │ ├── PlainSummaryReporterProviderTest.kt │ └── PlainSummaryReporterTest.kt ├── ktlint-cli-reporter-plain ├── api │ └── ktlint-cli-reporter-plain.api ├── build.gradle.kts ├── gradle.properties └── src │ ├── main │ ├── kotlin │ │ └── com │ │ │ └── pinterest │ │ │ └── ktlint │ │ │ └── cli │ │ │ └── reporter │ │ │ └── plain │ │ │ ├── Color.kt │ │ │ ├── PlainReporter.kt │ │ │ └── PlainReporterProvider.kt │ └── resources │ │ └── META-INF │ │ └── services │ │ └── com.pinterest.ktlint.cli.reporter.core.api.ReporterProviderV2 │ └── test │ └── kotlin │ └── com │ └── pinterest │ └── ktlint │ └── cli │ └── reporter │ └── plain │ ├── PlainReporterProviderTest.kt │ └── PlainReporterTest.kt ├── ktlint-cli-reporter-sarif ├── api │ └── ktlint-cli-reporter-sarif.api ├── build.gradle.kts ├── gradle.properties └── src │ ├── main │ ├── kotlin │ │ └── com │ │ │ └── pinterest │ │ │ └── ktlint │ │ │ └── cli │ │ │ └── reporter │ │ │ └── sarif │ │ │ ├── SarifReporter.kt │ │ │ └── SarifReporterProvider.kt │ └── resources │ │ └── META-INF │ │ └── services │ │ └── com.pinterest.ktlint.cli.reporter.core.api.ReporterProviderV2 │ └── test │ ├── kotlin │ └── com │ │ └── pinterest │ │ └── ktlint │ │ └── cli │ │ └── reporter │ │ └── sarif │ │ └── SarifReporterTest.kt │ └── resources │ └── relative-path.sarif ├── ktlint-cli-ruleset-core ├── api │ └── ktlint-cli-ruleset-core.api ├── build.gradle.kts ├── gradle.properties └── src │ └── main │ └── kotlin │ └── com │ └── pinterest │ └── ktlint │ └── cli │ └── ruleset │ └── core │ └── api │ └── RuleSetProviderV3.kt ├── ktlint-cli ├── api │ └── ktlint-cli.api ├── build.gradle.kts ├── gradle.properties └── src │ ├── main │ ├── kotlin │ │ └── com │ │ │ └── pinterest │ │ │ └── ktlint │ │ │ ├── Main.kt │ │ │ └── cli │ │ │ └── internal │ │ │ ├── FileUtils.kt │ │ │ ├── GenerateEditorConfigSubCommand.kt │ │ │ ├── GitHookCliktCommand.kt │ │ │ ├── GitPreCommitHookSubCommand.kt │ │ │ ├── GitPrePushHookSubCommand.kt │ │ │ ├── KtlintCommandLine.kt │ │ │ ├── KtlintServiceLoader.kt │ │ │ ├── KtlintVersionProvider.kt │ │ │ ├── LoadReporterProviders.kt │ │ │ ├── LoadRuleProviders.kt │ │ │ └── ReporterAggregator.kt │ └── scripts │ │ └── ktlint.bat │ └── test │ ├── kotlin │ └── com │ │ └── pinterest │ │ └── ktlint │ │ └── cli │ │ ├── CommandLineTestRunner.kt │ │ ├── EditorConfigDefaultsLoaderCLITest.kt │ │ ├── RuleSetsLoaderCLITest.kt │ │ ├── SimpleCLITest.kt │ │ ├── api │ │ └── BaselineCLITest.kt │ │ ├── environment │ │ └── OsEnvironment.kt │ │ └── internal │ │ └── FileUtilsTest.kt │ └── resources │ └── cli │ ├── .editorconfig │ ├── baseline │ ├── TestBaselineExtraErrorFile.kt.test │ ├── TestBaselineFile.kt.test │ ├── config │ │ └── test-baseline.xml │ ├── readme.md │ ├── some │ │ └── path │ │ │ └── to │ │ │ ├── TestBaselineExtraErrorFile.kt.test │ │ │ └── TestBaselineFile.kt.test │ └── test-baseline.xml │ ├── custom-ruleset │ └── rule-set-provider-v2 │ │ ├── Main.kt.test │ │ ├── ktlint-cli-reporter-html.jar │ │ ├── ktlint-test-ruleset-provider-v2-deprecated.jar │ │ └── readme.md │ ├── editorconfig-path │ └── project │ │ ├── .editorconfig │ │ ├── .editorconfig-bar │ │ ├── .editorconfig-default-max-line-length-on-tests-only │ │ ├── .editorconfig-disable-no-wildcard-imports-rule │ │ ├── editorconfig-alternative │ │ ├── editorconfig-boolean-setting │ │ └── src │ │ ├── main │ │ └── kotlin │ │ │ ├── example │ │ │ ├── Bar.kts.test │ │ │ ├── Foo.kt.test │ │ │ └── Wildard1.kt.test │ │ │ └── filename-example │ │ │ └── Wildcard2.kt.test │ │ └── test │ │ └── kotlin │ │ └── example │ │ ├── BarTest.kts.test │ │ └── FooTest.kt.test │ ├── no-code-style-error │ └── Main.kt │ ├── readme.md │ └── too-many-empty-lines │ └── Main.kt.test ├── ktlint-logger ├── api │ └── ktlint-logger.api ├── build.gradle.kts ├── gradle.properties └── src │ └── main │ └── kotlin │ └── com │ └── pinterest │ └── ktlint │ └── logger │ └── api │ └── KtLintKLoggerInitializer.kt ├── ktlint-rule-engine-core ├── api │ └── ktlint-rule-engine-core.api ├── build.gradle.kts ├── gradle.properties └── src │ ├── main │ └── kotlin │ │ └── com │ │ └── pinterest │ │ └── ktlint │ │ └── rule │ │ └── engine │ │ └── core │ │ ├── api │ │ ├── ASTNodeExtension.kt │ │ ├── AutocorrectDecision.kt │ │ ├── ElementType.kt │ │ ├── IgnoreKtlintSuppressions.kt │ │ ├── IndentConfig.kt │ │ ├── KtlintKotlinCompiler.kt │ │ ├── OptInFeatures.kt │ │ ├── Rule.kt │ │ ├── RuleAutocorrectApproveHandler.kt │ │ ├── RuleProvider.kt │ │ ├── SinceKtlint.kt │ │ ├── TokenSets.kt │ │ └── editorconfig │ │ │ ├── CodeStyleEditorConfigProperty.kt │ │ │ ├── CommaSeparatedListValueParser.kt │ │ │ ├── EditorConfig.kt │ │ │ ├── EditorConfigProperty.kt │ │ │ ├── EndOfLineProperty.kt │ │ │ ├── IndentSizeEditorConfigProperty.kt │ │ │ ├── IndentStyleEditorConfigProperty.kt │ │ │ ├── InsertFinalNewLineEditorConfigProperty.kt │ │ │ ├── MaxLineLengthEditorConfigProperty.kt │ │ │ ├── RuleExecutionEditorConfigProperty.kt │ │ │ ├── SafeEnumValueParser.kt │ │ │ └── ec4j │ │ │ └── EditorConfigProperty.kt │ │ ├── internal │ │ └── IdNamingPolicy.kt │ │ └── util │ │ └── Any.kt │ └── test │ └── kotlin │ └── com │ └── pinterest │ └── ktlint │ └── rule │ └── engine │ └── core │ └── api │ ├── ASTNodeExtensionTest.kt │ ├── RuleKtTest.kt │ └── editorconfig │ ├── CodeStyleEditorConfigPropertyTest.kt │ ├── CommaSeparatedListValueParserTest.kt │ ├── EditorConfigTest.kt │ ├── IndentSizeEditorConfigPropertyTest.kt │ ├── MaxLineLengthEditorConfigPropertyTest.kt │ ├── RuleExecutionEditorConfigPropertyTest.kt │ └── SafeEnumValueParserTest.kt ├── ktlint-rule-engine ├── api │ └── ktlint-rule-engine.api ├── build.gradle.kts ├── gradle.properties └── src │ ├── main │ └── kotlin │ │ └── com │ │ └── pinterest │ │ └── ktlint │ │ └── rule │ │ └── engine │ │ ├── api │ │ ├── Code.kt │ │ ├── EditorConfigDefaults.kt │ │ ├── EditorConfigOverride.kt │ │ ├── EditorConfigPropertyRegistry.kt │ │ ├── KtLintParseException.kt │ │ ├── KtLintRuleEngine.kt │ │ ├── KtLintRuleException.kt │ │ ├── KtlintRuleEngineSuppression.kt │ │ └── LintError.kt │ │ └── internal │ │ ├── AutocorrectHandler.kt │ │ ├── CodeFormatter.kt │ │ ├── DefaultEditorConfigProperties.kt │ │ ├── EditorConfigDefaultsLoader.kt │ │ ├── EditorConfigFinder.kt │ │ ├── EditorConfigGenerator.kt │ │ ├── EditorConfigLoader.kt │ │ ├── FormatterTags.kt │ │ ├── KtlintSuppression.kt │ │ ├── PositionInTextLocator.kt │ │ ├── RuleExecutionContext.kt │ │ ├── RuleProviderSorter.kt │ │ ├── SuppressionLocator.kt │ │ ├── ThreadSafeEditorConfigCache.kt │ │ ├── VisitorProvider.kt │ │ ├── rulefilter │ │ ├── InternalRuleProvidersFilter.kt │ │ ├── RuleExecutionRuleFilter.kt │ │ ├── RuleFilter.kt │ │ └── RunAfterRuleFilter.kt │ │ └── rules │ │ ├── InternalRule.kt │ │ └── KtlintSuppressionRule.kt │ └── test │ ├── kotlin │ └── com │ │ └── pinterest │ │ └── ktlint │ │ └── rule │ │ └── engine │ │ ├── api │ │ ├── DisabledRulesTest.kt │ │ ├── EditorConfigPropertyRegistryTest.kt │ │ ├── KtLintTest.kt │ │ └── KtlintRuleEngineSuppressionKtTest.kt │ │ └── internal │ │ ├── EditorConfigDefaultsLoaderTest.kt │ │ ├── EditorConfigFinderTest.kt │ │ ├── EditorConfigGeneratorTest.kt │ │ ├── EditorConfigLoaderTest.kt │ │ ├── KtlintSuppressionKtTest.kt │ │ ├── RuleProviderSorterTest.kt │ │ ├── SuppressionLocatorTest.kt │ │ ├── ThreadSafeEditorConfigCacheTest.kt │ │ ├── VisitorProviderTest.kt │ │ ├── rulefilter │ │ ├── InternalRuleProvidersFilterTest.kt │ │ ├── RuleExecutionRuleFilterTest.kt │ │ ├── RuleFilterKtTest.kt │ │ └── RunAfterRuleFilterTest.kt │ │ └── rules │ │ └── KtlintSuppressionRuleTest.kt │ └── resources │ └── spec │ └── format-unicode-bom.kt.spec ├── ktlint-ruleset-standard ├── api │ └── ktlint-ruleset-standard.api ├── build.gradle.kts ├── gradle.properties └── src │ ├── main │ ├── kotlin │ │ └── com │ │ │ └── pinterest │ │ │ └── ktlint │ │ │ └── ruleset │ │ │ └── standard │ │ │ ├── StandardRule.kt │ │ │ ├── StandardRuleSetProvider.kt │ │ │ └── rules │ │ │ ├── AnnotationRule.kt │ │ │ ├── AnnotationSpacingRule.kt │ │ │ ├── ArgumentListWrappingRule.kt │ │ │ ├── BackingPropertyNamingRule.kt │ │ │ ├── BinaryExpressionWrappingRule.kt │ │ │ ├── BlankLineBeforeDeclarationRule.kt │ │ │ ├── BlankLineBetweenWhenConditions.kt │ │ │ ├── BlockCommentInitialStarAlignmentRule.kt │ │ │ ├── ChainMethodContinuationRule.kt │ │ │ ├── ChainWrappingRule.kt │ │ │ ├── ClassNamingRule.kt │ │ │ ├── ClassSignatureRule.kt │ │ │ ├── CommentSpacingRule.kt │ │ │ ├── CommentWrappingRule.kt │ │ │ ├── ConditionWrappingRule.kt │ │ │ ├── ContextReceiverWrappingRule.kt │ │ │ ├── DiscouragedCommentLocationRule.kt │ │ │ ├── EnumEntryNameCaseRule.kt │ │ │ ├── EnumWrappingRule.kt │ │ │ ├── FilenameRule.kt │ │ │ ├── FinalNewlineRule.kt │ │ │ ├── FunKeywordSpacingRule.kt │ │ │ ├── FunctionExpressionBodyRule.kt │ │ │ ├── FunctionLiteralRule.kt │ │ │ ├── FunctionNamingRule.kt │ │ │ ├── FunctionReturnTypeSpacingRule.kt │ │ │ ├── FunctionSignatureRule.kt │ │ │ ├── FunctionStartOfBodySpacingRule.kt │ │ │ ├── FunctionTypeModifierSpacingRule.kt │ │ │ ├── FunctionTypeReferenceSpacingRule.kt │ │ │ ├── IfElseBracingRule.kt │ │ │ ├── IfElseWrappingRule.kt │ │ │ ├── ImportOrderingRule.kt │ │ │ ├── IndentationRule.kt │ │ │ ├── KdocRule.kt │ │ │ ├── KdocWrappingRule.kt │ │ │ ├── MaxLineLengthRule.kt │ │ │ ├── MixedConditionOperatorsRule.kt │ │ │ ├── ModifierListSpacingRule.kt │ │ │ ├── ModifierOrderRule.kt │ │ │ ├── MultiLineIfElseRule.kt │ │ │ ├── MultilineExpressionWrappingRule.kt │ │ │ ├── MultilineLoopRule.kt │ │ │ ├── NoBlankLineBeforeRbraceRule.kt │ │ │ ├── NoBlankLineInListRule.kt │ │ │ ├── NoBlankLinesInChainedMethodCallsRule.kt │ │ │ ├── NoConsecutiveBlankLinesRule.kt │ │ │ ├── NoConsecutiveCommentsRule.kt │ │ │ ├── NoEmptyClassBodyRule.kt │ │ │ ├── NoEmptyFileRule.kt │ │ │ ├── NoEmptyFirstLineInClassBodyRule.kt │ │ │ ├── NoEmptyFirstLineInMethodBlockRule.kt │ │ │ ├── NoLineBreakAfterElseRule.kt │ │ │ ├── NoLineBreakBeforeAssignmentRule.kt │ │ │ ├── NoMultipleSpacesRule.kt │ │ │ ├── NoSemicolonsRule.kt │ │ │ ├── NoSingleLineBlockCommentRule.kt │ │ │ ├── NoTrailingSpacesRule.kt │ │ │ ├── NoUnitReturnRule.kt │ │ │ ├── NoUnusedImportsRule.kt │ │ │ ├── NoWildcardImportsRule.kt │ │ │ ├── NullableTypeSpacingRule.kt │ │ │ ├── PackageNameRule.kt │ │ │ ├── ParameterListSpacingRule.kt │ │ │ ├── ParameterListWrappingRule.kt │ │ │ ├── ParameterWrappingRule.kt │ │ │ ├── PropertyNamingRule.kt │ │ │ ├── PropertyWrappingRule.kt │ │ │ ├── SpacingAroundAngleBracketsRule.kt │ │ │ ├── SpacingAroundColonRule.kt │ │ │ ├── SpacingAroundCommaRule.kt │ │ │ ├── SpacingAroundCurlyRule.kt │ │ │ ├── SpacingAroundDotRule.kt │ │ │ ├── SpacingAroundDoubleColonRule.kt │ │ │ ├── SpacingAroundKeywordRule.kt │ │ │ ├── SpacingAroundOperatorsRule.kt │ │ │ ├── SpacingAroundParensRule.kt │ │ │ ├── SpacingAroundRangeOperatorRule.kt │ │ │ ├── SpacingAroundSquareBracketsRule.kt │ │ │ ├── SpacingAroundUnaryOperatorRule.kt │ │ │ ├── SpacingBetweenDeclarationsWithAnnotationsRule.kt │ │ │ ├── SpacingBetweenDeclarationsWithCommentsRule.kt │ │ │ ├── SpacingBetweenFunctionNameAndOpeningParenthesisRule.kt │ │ │ ├── StatementWrappingRule.kt │ │ │ ├── StringTemplateIndentRule.kt │ │ │ ├── StringTemplateRule.kt │ │ │ ├── TrailingCommaOnCallSiteRule.kt │ │ │ ├── TrailingCommaOnDeclarationSiteRule.kt │ │ │ ├── TryCatchFinallySpacingRule.kt │ │ │ ├── TypeArgumentCommentRule.kt │ │ │ ├── TypeArgumentListSpacingRule.kt │ │ │ ├── TypeParameterCommentRule.kt │ │ │ ├── TypeParameterListSpacingRule.kt │ │ │ ├── UnnecessaryParenthesesBeforeTrailingLambdaRule.kt │ │ │ ├── ValueArgumentCommentRule.kt │ │ │ ├── ValueParameterCommentRule.kt │ │ │ ├── WhenEntryBracing.kt │ │ │ ├── WrappingRule.kt │ │ │ └── internal │ │ │ ├── RegExIgnoringDiacriticsAndStrokesOnLetters.kt │ │ │ └── importordering │ │ │ ├── ImportLayoutParser.kt │ │ │ ├── ImportSorter.kt │ │ │ └── PatternEntry.kt │ └── resources │ │ └── META-INF │ │ └── services │ │ └── com.pinterest.ktlint.cli.ruleset.core.api.RuleSetProviderV3 │ └── test │ └── kotlin │ └── com │ └── pinterest │ └── ktlint │ └── ruleset │ └── standard │ ├── StandardRuleSetProviderTest.kt │ └── rules │ ├── AnnotationRuleTest.kt │ ├── AnnotationSpacingRuleTest.kt │ ├── ArgumentListWrappingRuleTest.kt │ ├── BackingPropertyNamingRuleTest.kt │ ├── BinaryExpressionWrappingRuleTest.kt │ ├── BlankLineBeforeDeclarationRuleTest.kt │ ├── BlankLineBetweenWhenConditionsTest.kt │ ├── BlockCommentInitialStarAlignmentRuleTest.kt │ ├── ChainMethodContinuationRuleTest.kt │ ├── ChainWrappingRuleTest.kt │ ├── ClassNamingRuleTest.kt │ ├── ClassSignatureRuleTest.kt │ ├── CommentSpacingRuleTest.kt │ ├── CommentWrappingRuleTest.kt │ ├── ConditionWrappingRuleTest.kt │ ├── ContextReceiverWrappingRuleTest.kt │ ├── EnumEntryNameCaseRuleTest.kt │ ├── EnumWrappingRuleTest.kt │ ├── FilenameRuleTest.kt │ ├── FinalNewlineRuleTest.kt │ ├── FunKeywordSpacingRuleTest.kt │ ├── FunctionExpressionBodyRuleTest.kt │ ├── FunctionLiteralRuleTest.kt │ ├── FunctionNamingRuleTest.kt │ ├── FunctionReturnTypeSpacingRuleTest.kt │ ├── FunctionSignatureRuleTest.kt │ ├── FunctionStartOfBodySpacingRuleTest.kt │ ├── FunctionTypeModifierSpacingRuleTest.kt │ ├── FunctionTypeReferenceSpacingRuleTest.kt │ ├── IfElseBracingRuleTest.kt │ ├── IfElseWrappingRuleTest.kt │ ├── IndentationRuleTest.kt │ ├── KdocRuleTest.kt │ ├── KdocWrappingRuleTest.kt │ ├── MaxLineLengthRuleTest.kt │ ├── MixedConditionOperatorsRuleTest.kt │ ├── ModifierListSpacingRuleTest.kt │ ├── ModifierOrderRuleTest.kt │ ├── MultiLineIfElseRuleTest.kt │ ├── MultilineExpressionWrappingRuleTest.kt │ ├── MultilineLoopRuleTest.kt │ ├── NoBlankLineBeforeRbraceRuleTest.kt │ ├── NoBlankLineInListRuleTest.kt │ ├── NoBlankLinesInChainedMethodCallsRuleTest.kt │ ├── NoConsecutiveBlankLinesRuleTest.kt │ ├── NoConsecutiveCommentsRuleTest.kt │ ├── NoEmptyClassBodyRuleTest.kt │ ├── NoEmptyFileRuleTest.kt │ ├── NoEmptyFirstLineInClassBodyRuleTest.kt │ ├── NoEmptyFirstLineInMethodBlockRuleTest.kt │ ├── NoLineBreakAfterElseRuleTest.kt │ ├── NoLineBreakBeforeAssignmentRuleTest.kt │ ├── NoMultipleSpacesRuleTest.kt │ ├── NoSemicolonsRuleTest.kt │ ├── NoSingleLineBlockCommentRuleTest.kt │ ├── NoTrailingSpacesRuleTest.kt │ ├── NoUnitReturnRuleTest.kt │ ├── NoUnusedImportsRuleTest.kt │ ├── NoWildcardImportsRuleTest.kt │ ├── NullableTypeSpacingRuleTest.kt │ ├── PackageNameRuleTest.kt │ ├── ParameterListSpacingRuleTest.kt │ ├── ParameterListWrappingRuleTest.kt │ ├── ParameterWrappingRuleTest.kt │ ├── PropertyNamingRuleTest.kt │ ├── PropertyWrappingRuleTest.kt │ ├── SpacingAroundAngleBracketsRuleTest.kt │ ├── SpacingAroundColonRuleTest.kt │ ├── SpacingAroundCommaRuleTest.kt │ ├── SpacingAroundCurlyRuleTest.kt │ ├── SpacingAroundDotRuleTest.kt │ ├── SpacingAroundDoubleColonRuleTest.kt │ ├── SpacingAroundKeywordRuleTest.kt │ ├── SpacingAroundOperatorsRuleTest.kt │ ├── SpacingAroundParensRuleTest.kt │ ├── SpacingAroundRangeOperatorRuleTest.kt │ ├── SpacingAroundSquareBracketsRuleTest.kt │ ├── SpacingAroundUnaryOperatorRuleTest.kt │ ├── SpacingBetweenDeclarationsWithAnnotationsRuleTest.kt │ ├── SpacingBetweenDeclarationsWithCommentsRuleTest.kt │ ├── SpacingBetweenFunctionNameAndOpeningParenthesisRuleTest.kt │ ├── StatementWrappingRuleTest.kt │ ├── StringTemplateIndentRuleTest.kt │ ├── StringTemplateRuleTest.kt │ ├── TrailingCommaOnCallSiteRuleTest.kt │ ├── TrailingCommaOnDeclarationSiteRuleTest.kt │ ├── TryCatchFinallySpacingRuleTest.kt │ ├── TypeArgumentCommentRuleTest.kt │ ├── TypeArgumentListSpacingRuleTest.kt │ ├── TypeParameterCommentRuleTest.kt │ ├── TypeParameterListSpacingRuleTest.kt │ ├── UnnecessaryParenthesesBeforeTrailingLambdaRuleTest.kt │ ├── ValueArgumentCommentRuleTest.kt │ ├── ValueParameterCommentRuleTest.kt │ ├── WhenEntryBracingTest.kt │ ├── WrappingRuleTest.kt │ ├── importordering │ ├── ImportLayoutParserTest.kt │ ├── ImportOrderingRuleAsciiTest.kt │ ├── ImportOrderingRuleCustomTest.kt │ ├── ImportOrderingRuleIdeaTest.kt │ └── ImportOrderingRuleTest.kt │ └── internal │ └── RemoveDiacriticsFromLettersTest.kt ├── ktlint-ruleset-template ├── build.gradle.kts └── src │ ├── main │ ├── kotlin │ │ └── yourpkgname │ │ │ ├── CustomRuleSetProvider.kt │ │ │ └── NoVarRule.kt │ └── resources │ │ └── META-INF │ │ └── services │ │ └── com.pinterest.ktlint.cli.ruleset.core.api.RuleSetProviderV3 │ └── test │ └── kotlin │ └── yourpkgname │ └── NoVarRuleTest.kt ├── ktlint-test-ruleset-provider-v2-deprecated ├── README.md ├── build.gradle.kts └── src │ └── main │ ├── kotlin │ └── com │ │ └── pinterest │ │ └── ruleset │ │ └── provider │ │ └── deprecated │ │ ├── SomeRule.kt │ │ └── SomeRuleSetProvider.kt │ └── resources │ └── META-INF │ └── services │ └── com.pinterest.ktlint.core.RuleSetProviderV2 ├── ktlint-test ├── api │ └── ktlint-test.api ├── build.gradle.kts ├── gradle.properties └── src │ └── main │ ├── kotlin │ └── com │ │ └── pinterest │ │ └── ktlint │ │ └── test │ │ ├── KtLintAssertThat.kt │ │ ├── KtlintDocumentationTest.kt │ │ ├── KtlintTestFileSystem.kt │ │ ├── README.MD │ │ ├── ReplaceStringTemplatePlaceholder.kt │ │ ├── RuleSetProviderTest.kt │ │ └── TestLiterals.kt │ └── resources │ └── logback-test.xml ├── renovate.json └── settings.gradle.kts /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | max_line_length = 140 8 | 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | 14 | [*.{java,kt,kts,scala,rs,xml,kt.spec,kts.spec}] 15 | indent_size = 4 16 | 17 | [*.{kt,kts}] 18 | ktlint_code_style = ktlint_official 19 | ktlint_ignore_back_ticked_identifier = true 20 | 21 | ktlint_standard = enabled 22 | 23 | # Experimental rules run by default run on the ktlint code base itself. Experimental rules should not be released if 24 | # we are not pleased ourselves with the results on the ktlint code base. 25 | ktlint_experimental = enabled 26 | 27 | # Don't allow any wildcard imports 28 | ij_kotlin_packages_to_use_import_on_demand = unset 29 | 30 | # Prevent wildcard imports 31 | ij_kotlin_name_count_to_use_star_import = 99 32 | ij_kotlin_name_count_to_use_star_import_for_members = 99 33 | 34 | [*.md] 35 | trim_trailing_whitespace = false 36 | max_line_length = unset 37 | 38 | [gradle/verification-metadata.xml] 39 | indent_size = 3 40 | 41 | [*.yml] 42 | ij_yaml_spaces_within_brackets = false 43 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.bat eol=crlf 3 | *.gpg binary 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Report ktlint bug 4 | 5 | --- 6 | 7 | 9 | 10 | ## Expected Behavior 11 | 12 | 13 | 14 | 15 | 16 | ## Observed Behavior 17 | 18 | 19 | 20 | 21 | 22 | ## Steps to Reproduce 23 | 24 | 25 | 26 | 27 | 28 | ## Your Environment 29 | 30 | * Version of ktlint used: 31 | * Relevant parts of the `.editorconfig` settings 32 | * Name and version (or code for custom task) of integration used (Gradle plugin, Maven plugin, command line, custom Gradle task): 33 | * Version of Gradle used (if applicable): 34 | * Operating System and version: 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | contact_links: 3 | - name: Kotlin slack ktlint channel 4 | url: https://kotlinlang.slack.com/archives/CKS3XG0LS 5 | about: Please ask and answer questions here. 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/enhancement.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Enhancement 3 | about: Propose new enhancement to the Ktlint API 4 | 5 | --- 6 | 7 | 9 | 10 | ## Expected Behavior 11 | 12 | 13 | 14 | 15 | 16 | ## Current Behavior 17 | 18 | 19 | ## Additional information 20 | * Current version of ktlint: 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/new_rule_proposal.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: New Rule 3 | about: Propose new rule 4 | 5 | --- 6 | 7 | 9 | 10 | ## Expected Rule behavior 11 | 12 | 13 | 14 | 15 | 16 | ## Additional information 17 | * Current version of ktlint: 18 | * Styleguide section: 19 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | ## Description 7 | 8 | 12 | 13 | ## Checklist 14 | 15 | Before submitting the PR, please check following (checks which are not relevant may be ignored): 16 | - [ ] Commit message are well written. In addition to a short title, the commit message also explain why a change is made. 17 | - [ ] At least one commit message contains a reference `Closes #` or `Fixes #` (replace`` with issue number) 18 | - [ ] Tests are added 19 | - [ ] KtLint format has been applied on source code itself and violations are fixed 20 | - [ ] PR title is short and clear (it is used as description in the release changelog) 21 | - [ ] PR description added (background information) 22 | 23 | [Documentation](https://pinterest.github.io/ktlint/) is updated. See [difference between snapshot and release documentation](https://github.com/pinterest/ktlint/tree/master/documentation) 24 | - [ ] [Snapshot documentation](https://github.com/pinterest/ktlint/tree/master/documentation/snapshot) in case documentation is to be released together with a code change 25 | - [ ] [Release documentation](https://github.com/pinterest/ktlint/tree/master/documentation/release-latest) in case documentation is related to a released version of ktlint and has to be published as soon as the change is merged to master 26 | -------------------------------------------------------------------------------- /.github/actions/setup-gradle-build/action.yml: -------------------------------------------------------------------------------- 1 | name: Setup Gradle 2 | description: Sets up the environment to run Gradle 3 | 4 | inputs: 5 | gradle-jvm-args: 6 | description: "JVM args to pass to Gradle" 7 | required: true 8 | # Github-Hosted nodes have only 7GB of RAM available. Looking at build scans Gradle process requires slightly more than 0.5GB. 9 | # Keeping this setting low, allows other, forked JVM processes (like tests) to use remaining memory. 10 | # Increase this value, only if GC time visible in build scans will take more than a few seconds. 11 | default: "-Xmx1g" 12 | additional-java-versions: 13 | description: "Java versions installed on the side of the default Java version required by the build" 14 | required: false 15 | 16 | runs: 17 | using: composite 18 | 19 | steps: 20 | - uses: actions/setup-java@v4 21 | with: 22 | distribution: 'zulu' 23 | java-version: | # last version (set as default) should match all `jvmToolchain(xxx)` calls in the project 24 | ${{ inputs.additional-java-versions }} 25 | 21 26 | 27 | # Please note these settings will override the ones set via `gradle.properties` committed to the repository - https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties 28 | # List of optimizations: 29 | # - `jvm-args` fine-tuned to the CI runner & tasks being invoked 30 | # - disabled File System Watching to improve Windows build times. CI runs don't modify source files, hence they don't need to pay extra cost to efficiently track changed files. 31 | - name: Optimize Gradle build properties for CI 32 | run: | 33 | mkdir -p ~/.gradle 34 | printf "org.gradle.jvmargs=${{ inputs.gradle-jvm-args }}\n" >> ~/.gradle/gradle.properties 35 | printf "org.gradle.vfs.watch=false\n" >> ~/.gradle/gradle.properties 36 | shell: bash 37 | 38 | - uses: gradle/actions/setup-gradle@v4 39 | -------------------------------------------------------------------------------- /.github/workflows/publish-release-docs.yml: -------------------------------------------------------------------------------- 1 | name: Publish release documentation 2 | 3 | on: 4 | push: 5 | branches: ['master'] 6 | paths: ['documentation/release-latest/**'] 7 | 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-latest 11 | if: github.repository == 'pinterest/ktlint' 12 | steps: 13 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 14 | with: 15 | fetch-depth: 0 # fetch all commits/branches 16 | - uses: actions/setup-python@v5 17 | with: 18 | python-version: 3.x 19 | 20 | - name: Install mkdocs and mike 21 | run: pip install mkdocs-material mike 22 | 23 | - name: Config git 24 | run: | 25 | git config user.email "ktlint@github.com" 26 | git config user.name "Ktlint Release Workflow" 27 | 28 | - name: Get last released version 29 | run: echo "version=$(git describe --abbrev=0 --tags)" >> $GITHUB_ENV 30 | 31 | - name: Deploy release docs 32 | run: | 33 | echo "Deploy release docs to version ${{ env.version }}" 34 | cd documentation/release-latest 35 | # Release docs are versioned so that user can use relevant docs for the ktlint version they use 36 | mike deploy --push --update-aliases ${{ env.version }} latest 37 | 38 | - name: Update default release docs 39 | run: | 40 | cd documentation/release-latest 41 | mike set-default --push latest 42 | -------------------------------------------------------------------------------- /.github/workflows/publish-snapshot-build.yml: -------------------------------------------------------------------------------- 1 | name: Publish snapshot build 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | paths: ['**/*.kt', '**/*.kts', '**/*.properties', '**/*.toml'] 7 | 8 | env: 9 | SONATYPE_NEXUS_USERNAME: ${{ secrets.SONATYPE_NEXUS_USERNAME }} 10 | SONATYPE_NEXUS_PASSWORD: ${{ secrets.SONATYPE_NEXUS_PASSWORD }} 11 | ORG_GRADLE_PROJECT_signingKey: ${{ secrets.ORG_GRADLE_PROJECT_SIGNINGKEY }} 12 | ORG_GRADLE_PROJECT_signingKeyId: ${{ secrets.ORG_GRADLE_PROJECT_SIGNINGKEYID }} 13 | ORG_GRADLE_PROJECT_signingKeyPassword: ${{ secrets.ORG_GRADLE_PROJECT_SIGNINGPASSWORD }} 14 | 15 | jobs: 16 | publish: 17 | runs-on: ubuntu-latest 18 | if: github.repository == 'pinterest/ktlint' 19 | steps: 20 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 21 | 22 | - uses: ./.github/actions/setup-gradle-build 23 | 24 | - name: Publish snapshot to Maven 25 | run: ./gradlew clean publishMavenPublicationToMavenCentralRepository --no-daemon --no-parallel --no-configuration-cache 26 | 27 | - name: Publish Kotlin-dev snapshot to Maven 28 | run: ./gradlew -PkotlinDev clean publishMavenPublicationToMavenCentralRepository --no-daemon --no-parallel --no-configuration-cache 29 | -------------------------------------------------------------------------------- /.github/workflows/publish-snapshot-docs.yml: -------------------------------------------------------------------------------- 1 | name: Publish snapshot documentation 2 | 3 | on: 4 | push: 5 | branches: ['master'] 6 | paths: ['documentation/snapshot/**'] 7 | 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-latest 11 | if: github.repository == 'pinterest/ktlint' 12 | steps: 13 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 14 | with: 15 | fetch-depth: 0 # fetch all commits/branches 16 | - uses: actions/setup-python@v5 17 | with: 18 | python-version: 3.x 19 | 20 | - name: Install mkdocs and mike 21 | run: pip install mkdocs-material mike 22 | 23 | - name: Config git 24 | run: | 25 | git config user.email "ktlint@github.com" 26 | git config user.name "Ktlint Release Workflow" 27 | 28 | - run: | 29 | cd documentation/snapshot 30 | # The dev-snapshot version has no release number as the version will only be tagged when an official release is build 31 | # This also prevents that multiple snapshot versions of the documentation will be published in parallel 32 | mike deploy --push dev-snapshot 33 | -------------------------------------------------------------------------------- /.github/workflows/pull-request-without-code.yml: -------------------------------------------------------------------------------- 1 | # name must be identical to name of 'pull-request-with-code' 2 | name: Pull request 3 | 4 | on: 5 | push: 6 | branches: ['master'] 7 | paths: 8 | - '**/*' 9 | - '!**/*.kt' 10 | - '!**/*.kts' 11 | - '!**/*.properties' 12 | - '!**/*.toml' 13 | pull_request: 14 | paths: 15 | - '**/*' 16 | - '!**/*.kt' 17 | - '!**/*.kts' 18 | - '!**/*.properties' 19 | - '!**/*.toml' 20 | 21 | # Add a dummy job that return true so that a PR not containing any code can be merged to master 22 | # Note that all "jobs" (build, tests) including "jobs.*.runs-on" should be kept in sync with "pull-request-with-code" 23 | jobs: 24 | build: 25 | strategy: 26 | matrix: 27 | os: [ubuntu-latest, windows-latest] 28 | runs-on: ${{ matrix.os }} 29 | name: "[build] OS=${{ matrix.os }} Kotlin=stable" 30 | steps: 31 | - run: 'echo "No build required"' 32 | 33 | build-dev: 34 | strategy: 35 | matrix: 36 | os: [ubuntu-latest, windows-latest] 37 | runs-on: ${{ matrix.os }} 38 | name: "[build] OS=${{ matrix.os }}, Kotlin=dev" 39 | steps: 40 | - run: 'echo "No build required"' 41 | 42 | tests: 43 | strategy: 44 | matrix: 45 | os: [ubuntu-latest, windows-latest] 46 | jdk: [ 8, 11, 17 ] 47 | runs-on: ${{ matrix.os }} 48 | name: "[tests] OS=${{ matrix.os }}, Java=${{ matrix.jdk }}" 49 | steps: 50 | - run: 'echo "No build required"' 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .gradle 3 | build 4 | !ktlint/src/main/resources/config/.idea 5 | /.idea 6 | *.iml 7 | out 8 | /site 9 | # https://youtrack.jetbrains.com/issue/KT-58223/Kotlin-Gradle-plugin-shouldnt-store-data-in-project-cache-directory 10 | .kotlin 11 | -------------------------------------------------------------------------------- /.homebrew: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # project.version 4 | if [ "$VERSION" == "" ]; then exit 1; fi 5 | 6 | brew update 7 | shasum -a 256 ./ktlint-cli/build/run/ktlint | awk -v new_version="$VERSION" '{system(sprintf("brew bump-formula-pr --url=\"https://github.com/pinterest/ktlint/releases/download/%s/ktlint\" --sha256=\"%s\" ktlint", new_version, $1))}' 8 | -------------------------------------------------------------------------------- /ADOPTERS.md: -------------------------------------------------------------------------------- 1 | # Adopters 2 | 3 | This is an alphabetical list of people and organizations who are using this 4 | project. If you'd like to be included here, please send a Pull Request that 5 | adds your information to this file. 6 | 7 | - [Affirm](https://www.affirm.com/) 8 | - [Airbnb](https://www.airbnb.com/) 9 | - [Duolingo](https://www.duolingo.com/) 10 | - [Ellation](http://www.ellation.com/) 11 | - [Faithlife](https://faithlife.com/about) 12 | - [Flickr](https://flickr.com) 13 | - [Freeletics](https://www.freeletics.com) 14 | - [Gradle](https://gradle.org) 15 | - [M1 Finance](https://www.m1finance.com/) 16 | - [Pinterest](https://www.pinterest.com/) 17 | - [Runtastic](https://www.runtastic.com/) 18 | - [Shopify](https://www.shopify.com/) 19 | - [Square](https://squareup.com) 20 | - [Tinder](https://tinder.com) 21 | - [Uber](https://uber.com) 22 | - [Workable](https://www.workable.com) 23 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | See [Code of conduct](documentation/release-latest/docs/contributing/code-of-conduct.md). 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | See [Contributing guidelines](https://pinterest.github.io/ktlint/latest/contributing/overview/) 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 Pinterest, Inc. 2 | Copyright 2016-2019 Stanley Shyiko 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /SIGNING.md: -------------------------------------------------------------------------------- 1 | Release artifacts are signed. This is handled by the GitHub workflow. 2 | 3 | To test the creation of signed artifacts on the local machine, follow steps below: 4 | 5 | * Change property `VERSION_NAME` in `gradle.properties` so that it does not end with `-SNAPSHOT`. Whenever the version ends with `-SNAPSHOT`, the artifacts are never signed. Make sure that this change is not committed. 6 | * Use [GPG](https://infra.apache.org/openpgp.html#key-gen-generate-key) to generate a key. Maven requires a RSA key of at least 4096 bits. Most likely it does not matter for local publication whether this is used. Execute command: 7 | ``` 8 | gpg --full-gen-key 9 | ``` 10 | * Please select what kind of key you want: 1) RSA - RSA 11 | * What keysize do you want? 4096 12 | * Key is valid for? 0 (does not expire) 13 | * Real-name: ktlint-test 14 | * Email address: ktlint-test@nowhere.com 15 | * Comment: Test signing of ktlint artifacts on local machine only 16 | * Passphrase: i-will-try-no-to-forget-this-passphrase 17 | * The output ends with something like 18 | ```text 19 | pub rsa4096 2023-10-08 [SC] 20 | 5B0ABB03277D2FEB46FE7E8E22D6006063A5D3C3 21 | uid ktlint-test (Test signing of ktlint artifacts on local machine only) 22 | sub rsa4096 2023-10-08 [E] 23 | ``` 24 | * Execute all commands below in the same terminal 25 | ```shell 26 | export ORG_GRADLE_PROJECT_signingKeyId="63A5D3C3" # Last 8 characters of full id of the public key generated by gpg command above 27 | export ORG_GRADLE_PROJECT_signingKeyPassword="i-will-try-no-to-forget-this-passphrase" 28 | # Command below will export the armored GPG signing key and store it in an environment variable. Note that this command will ask for the password (see ORG_GRADLE_PROJECT_signingKeyPassword). 29 | export ORG_GRADLE_PROJECT_signingKey="$(gpg --export-secret-keys --armor $ORG_GRADLE_PROJECT_signingKeyId | tail -r | tail -n +3 | tail -r | tail -n +3 | tr -d '\n')" 30 | ``` 31 | * Execute the Gradle publication (this can not be combined with the export statements above because of the manual input of the passphrase) 32 | ``` 33 | ./gradlew publishToMavenLocal 34 | ``` 35 | -------------------------------------------------------------------------------- /build-logic/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `kotlin-dsl` 3 | } 4 | 5 | repositories { 6 | mavenCentral() 7 | } 8 | 9 | kotlin { 10 | jvmToolchain( 11 | libs 12 | .versions 13 | .java 14 | .compilation 15 | .get() 16 | .toInt(), 17 | ) 18 | } 19 | 20 | dependencies { 21 | val kotlinPlugin = 22 | if (hasProperty("kotlinDev")) { 23 | // Pass '-PkotlinDev' to command line to enable kotlin-in-development version 24 | logger.warn("Enabling kotlin dev version!") 25 | libs.kotlin.plugin.dev 26 | } else { 27 | libs.kotlin.plugin.asProvider() 28 | } 29 | implementation(kotlinPlugin) 30 | implementation(libs.dokka) 31 | implementation(libs.poko) 32 | } 33 | -------------------------------------------------------------------------------- /build-logic/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencyResolutionManagement { 2 | versionCatalogs { 3 | create("libs") { 4 | from(files("../gradle/libs.versions.toml")) 5 | } 6 | } 7 | } 8 | 9 | plugins { 10 | id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" 11 | } 12 | -------------------------------------------------------------------------------- /build-logic/src/main/kotlin/ktlint-dokka.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `java-library` 3 | // Generates Javadoc documentation 4 | id("org.jetbrains.dokka-javadoc") 5 | } 6 | 7 | dokka { 8 | dokkaPublications.javadoc { 9 | outputDirectory.set(layout.buildDirectory.dir("javadoc")) 10 | } 11 | } 12 | 13 | java { 14 | withSourcesJar() 15 | withJavadocJar() 16 | } 17 | -------------------------------------------------------------------------------- /build-logic/src/main/kotlin/ktlint-publication-library.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("ktlint-kotlin-common") 3 | id("ktlint-dokka") 4 | id("ktlint-publication") 5 | } 6 | 7 | publishing.publications.named("maven") { 8 | from(components["java"]) 9 | } 10 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | import java.net.URI 2 | 3 | plugins { 4 | alias(libs.plugins.kotlin.jvm) apply false 5 | alias(libs.plugins.checksum) apply false 6 | alias(libs.plugins.shadow) apply false 7 | alias( 8 | libs 9 | .plugins 10 | .kotlinx 11 | .binary 12 | .compatibiltiy 13 | .validator, 14 | ) 15 | } 16 | 17 | val internalNonPublishableProjects by extra( 18 | setOf( 19 | "ktlint-api-consumer", 20 | "ktlint-bom", 21 | "ktlint-ruleset-template", 22 | ), 23 | ) 24 | 25 | apiValidation { 26 | ignoredProjects += internalNonPublishableProjects 27 | } 28 | 29 | val ktlint: Configuration by configurations.creating 30 | 31 | dependencies { 32 | ktlint(projects.ktlintCli) 33 | } 34 | 35 | tasks.register("ktlintCheck") { 36 | group = LifecycleBasePlugin.VERIFICATION_GROUP 37 | description = "Check Kotlin code style" 38 | classpath = ktlint 39 | mainClass = "com.pinterest.ktlint.Main" 40 | args( 41 | "**/src/**/*.kt", 42 | "**.kts", 43 | "!**/build/**", 44 | // Do not run with option "--log-level=debug" or "--log-level=trace" as the lint violations will be difficult 45 | // to spot between the amount of output lines. 46 | ) 47 | } 48 | 49 | tasks.register("ktlintFormat") { 50 | group = LifecycleBasePlugin.VERIFICATION_GROUP 51 | description = "Check Kotlin code style and format" 52 | classpath = ktlint 53 | mainClass = "com.pinterest.ktlint.Main" 54 | jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") 55 | args( 56 | "-F", 57 | "**/src/**/*.kt", 58 | "**.kts", 59 | "!**/build/**", 60 | // Do not run with option "--log-level=debug" or "--log-level=trace" as the lint violations will be difficult 61 | // to spot between the amount of output lines. 62 | ) 63 | } 64 | 65 | tasks.wrapper { 66 | distributionSha256Sum = 67 | URI 68 | .create("$distributionUrl.sha256") 69 | .toURL() 70 | .openStream() 71 | .use { it.reader().readText().trim() } 72 | } 73 | -------------------------------------------------------------------------------- /documentation/release-latest/docs/api/badge.md: -------------------------------------------------------------------------------- 1 | If you want to display a badge to show that your project is linted and formatted using `'ktlint` than you can add the 2 | [![ktlint](https://img.shields.io/badge/ktlint%20code--style-%E2%9D%A4-FF4081)](https://pinterest.github.io/ktlint/) badge: 3 | 4 | ```md title="Ktlint code style badge" 5 | [![ktlint](https://img.shields.io/badge/ktlint%20code--style-%E2%9D%A4-FF4081)](https://pinterest.github.io/ktlint/) 6 | ``` 7 | -------------------------------------------------------------------------------- /documentation/release-latest/docs/api/custom-reporter.md: -------------------------------------------------------------------------------- 1 | ## Build a custom reporter 2 | Take a look at [ktlint-cli-reporter-plain](https://github.com/pinterest/ktlint/tree/master/ktlint-cli-reporter-plain). 3 | 4 | In short, all you need to do is to implement a 5 | [ReporterV2](https://github.com/pinterest/ktlint/blob/master/ktlint-cli-reporter-core/src/main/kotlin/com/pinterest/ktlint/cli/reporter/core/api/ReporterV2.kt) and make it available by registering 6 | a custom [ReporterProviderV2](https://github.com/pinterest/ktlint/blob/master/ktlint-cli-reporter-core/src/main/kotlin/com/pinterest/ktlint/cli/reporter/core/api/ReporterProviderV2.kt) using 7 | `META-INF/services/com.pinterest.ktlint.cli.reporter.core.api.ReporterProviderV2`. Pack all of that into a JAR and you're done. 8 | 9 | To load a custom (3rd party) reporter use `ktlint --reporter=name,artifact=/path/to/custom-ktlint-reporter.jar` 10 | (see `ktlint --help` for more). 11 | 12 | ## Third party reporters 13 | 14 | Known third-party reporters: 15 | 16 | * [kryanod/ktlint-junit-reporter](https://github.com/kryanod/ktlint-junit-reporter) reports ktlint output as an xml file in JUnit format so that the ktlint report can be made visible on the Merge Request page. 17 | * [musichin/ktlint-github-reporter](https://github.com/musichin/ktlint-github-reporter) uses [GitHub workflow commands](https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-error-message) to set error messages for `ktlint` issues. 18 | * [tobi2k/ktlint-gitlab-reporter](https://github.com/tobi2k/ktlint-gitlab-reporter) provides output in JSON format that can be parsed by GitLab automatically. 19 | -------------------------------------------------------------------------------- /documentation/release-latest/docs/api/index.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinterest/ktlint/2bec8b42ce4f130b839f2de574ec2cc767f779fa/documentation/release-latest/docs/api/index.md -------------------------------------------------------------------------------- /documentation/release-latest/docs/api/overview.md: -------------------------------------------------------------------------------- 1 | Ktlint has an open API with which you can integrate. 2 | 3 | The diagram below show the internal module structure of KtLint. 4 | 5 | ![Image](../../assets/images/module-dependencies.png) 6 | 7 | The `Ktlint Rule Engine` is by far the most important module. It is responsible for executing the linting and formatting of the source code. The Rule Engine itself does not contain any rules. Rules are provided by API Consumers. 8 | 9 | The `Ktlint CLI` is an API Consumer of the `Ktlint Rule Engine`. Together with the `Ktlint Ruleset Standard` and the `Ktlint Reporter` modules the CLI offers a standalone tool which can easily be run from the commandline. Also, the `Ktlint CLI` can easily be used with custom rulesets and/or reporters. 10 | 11 | The `Ktlint Ruleset Core` module contains the logic which is required by each API Consumer of the `Ktlint Rule Engine`, the `Ktlint Ruleset Standard` and custom rulesets. 12 | 13 | The module `Ktlint Test` provide functionalities like `assertThatRule` which is used to write unit tests in a fluent AssertJ look-a-like style and can also be used for testing of custom rules. 14 | 15 | The `Ktlint logger` module provides functionality for writing log messages. 16 | 17 | -------------------------------------------------------------------------------- /documentation/release-latest/docs/assets/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinterest/ktlint/2bec8b42ce4f130b839f2de574ec2cc767f779fa/documentation/release-latest/docs/assets/images/favicon.ico -------------------------------------------------------------------------------- /documentation/release-latest/docs/assets/images/ktlint-intellij-plugin-distract-free-mode-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinterest/ktlint/2bec8b42ce4f130b839f2de574ec2cc767f779fa/documentation/release-latest/docs/assets/images/ktlint-intellij-plugin-distract-free-mode-1.png -------------------------------------------------------------------------------- /documentation/release-latest/docs/assets/images/ktlint-intellij-plugin-distract-free-mode-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinterest/ktlint/2bec8b42ce4f130b839f2de574ec2cc767f779fa/documentation/release-latest/docs/assets/images/ktlint-intellij-plugin-distract-free-mode-2.png -------------------------------------------------------------------------------- /documentation/release-latest/docs/assets/images/ktlint-intellij-plugin-manual-mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinterest/ktlint/2bec8b42ce4f130b839f2de574ec2cc767f779fa/documentation/release-latest/docs/assets/images/ktlint-intellij-plugin-manual-mode.png -------------------------------------------------------------------------------- /documentation/release-latest/docs/assets/images/ktlint-intellij-plugin-preferences.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinterest/ktlint/2bec8b42ce4f130b839f2de574ec2cc767f779fa/documentation/release-latest/docs/assets/images/ktlint-intellij-plugin-preferences.png -------------------------------------------------------------------------------- /documentation/release-latest/docs/assets/images/ktlint-intellij-plugin-suppress-violation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinterest/ktlint/2bec8b42ce4f130b839f2de574ec2cc767f779fa/documentation/release-latest/docs/assets/images/ktlint-intellij-plugin-suppress-violation.png -------------------------------------------------------------------------------- /documentation/release-latest/docs/assets/images/module-dependencies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinterest/ktlint/2bec8b42ce4f130b839f2de574ec2cc767f779fa/documentation/release-latest/docs/assets/images/module-dependencies.png -------------------------------------------------------------------------------- /documentation/release-latest/docs/assets/images/psi-viewer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinterest/ktlint/2bec8b42ce4f130b839f2de574ec2cc767f779fa/documentation/release-latest/docs/assets/images/psi-viewer.png -------------------------------------------------------------------------------- /documentation/release-latest/docs/assets/images/rule-dependencies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinterest/ktlint/2bec8b42ce4f130b839f2de574ec2cc767f779fa/documentation/release-latest/docs/assets/images/rule-dependencies.png -------------------------------------------------------------------------------- /documentation/release-latest/docs/contributing/code-of-conduct.md: -------------------------------------------------------------------------------- 1 | At Pinterest, we work hard to ensure that our work environment is welcoming and inclusive to as many people as possible. We are committed to creating this environment for everyone involved in our open source projects as well. We welcome all participants regardless of ability, age, ethnicity, identified gender, religion (or lack there of), sexual orientation and socioeconomic status. 2 | 3 | This code of conduct details our expectations for upholding these values. 4 | 5 | ## Good behavior 6 | 7 | We expect members of our community to exhibit good behavior including (but of course not limited to): 8 | 9 | - Using intentional and empathetic language. 10 | - Focusing on resolving instead of escalating conflict. 11 | - Providing constructive feedback. 12 | 13 | ## Unacceptable behavior 14 | 15 | Some examples of unacceptable behavior (again, this is not an exhaustive list): 16 | 17 | - Harassment, publicly or in private. 18 | - Trolling. 19 | - Sexual advances (this isn’t the place for it). 20 | - Publishing other’s personal information. 21 | - Any behavior which would be deemed unacceptable in a professional environment. 22 | 23 | ## Recourse 24 | 25 | If you are witness to or the target of unacceptable behavior, it should be reported to Pinterest at opensource-policy@pinterest.com. All reporters will be kept confidential and an appropriate response for each incident will be evaluated. 26 | 27 | If the maintainers do not uphold and enforce this code of conduct in good faith, community leadership will hold them accountable. 28 | -------------------------------------------------------------------------------- /documentation/release-latest/docs/contributing/index.md: -------------------------------------------------------------------------------- 1 | ## Contributing guidelines 2 | -------------------------------------------------------------------------------- /documentation/release-latest/docs/contributing/overview.md: -------------------------------------------------------------------------------- 1 | !!! important 2 | Make sure to read the [Contributing guideline](guidelines.md) and the [code of conduct](code-of-conduct.md) first. 3 | 4 | ## Development 5 | 6 | Development starts with cloning and building the project on your local machine: 7 | 8 | ```sh 9 | git clone https://github.com/pinterest/ktlint && cd ktlint 10 | ./gradlew tasks # shows how to build, test, run, etc. project 11 | ``` 12 | 13 | !!! tip 14 | To open and run `ktlint` in Intellij IDEA: 15 | 16 | * File -> Open.... 17 | * You'll also need to set the "Project language level" to 8 in "Project Settings" (File -> Project Structure... -> Project). 18 | * To run `ktlint` - right-click on `ktlint/src/main/kotlin/com/pinterest/ktlint/Main.kt` -> Run. 19 | 20 | -------------------------------------------------------------------------------- /documentation/release-latest/docs/install/index.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinterest/ktlint/2bec8b42ce4f130b839f2de574ec2cc767f779fa/documentation/release-latest/docs/install/index.md -------------------------------------------------------------------------------- /documentation/release-latest/docs/install/snapshot-build.md: -------------------------------------------------------------------------------- 1 | ## Access to the latest `master` snapshot 2 | 3 | Whenever a commit is added to the `master` branch a snapshot build is automatically uploaded to [Sonatype's snapshots repository](https://oss.sonatype.org/content/repositories/snapshots/com/pinterest/ktlint/). 4 | If you are eager to try upcoming changes (that might or might not be included in the next stable release) you can do 5 | so by changing version of ktlint to `-SNAPSHOT` + adding a repo: 6 | 7 | ### Maven 8 | 9 | ```xml 10 | ... 11 | 12 | sonatype-snapshots 13 | https://oss.sonatype.org/content/repositories/snapshots 14 | 15 | true 16 | 17 | 18 | false 19 | 20 | 21 | ... 22 | ``` 23 | 24 | ### Gradle 25 | 26 | ```groovy 27 | repositories { 28 | maven { 29 | url "https://oss.sonatype.org/content/repositories/snapshots" 30 | } 31 | } 32 | ``` 33 | 34 | ### Kotlin development version snapshot 35 | 36 | Additionally, project publishes snapshots build against latest kotlin development version. To use them, change version 37 | of ktlint to `-kotlin-dev-SNAPSHOT`. 38 | -------------------------------------------------------------------------------- /documentation/release-latest/docs/quick-start.md: -------------------------------------------------------------------------------- 1 | Follow steps below for a quick start with latest ktlint release. 2 | 3 | ## Step 1: Install with brew 4 | ```shell 5 | brew install ktlint 6 | ``` 7 | See [download and verification from GitHub](../install/cli/#download-and-verification) or [other package managers](../install/cli/#package-managers) for alternative ways of installing ktlint. Or, use one of the [integrations like maven and gradle plugins](../install/integrations/). 8 | 9 | ## Step 2: Lint and format your code 10 | All files with extension `.kt` and `.kts` in the current directory and below will be scanned. Problems will be fixed automatically when possible. 11 | ```shell title="Autocorrect style violations" 12 | ktlint --format 13 | # or 14 | ktlint -F 15 | ``` 16 | See [cli usage](../install/cli/#command-line-usage) for a more extensive description on using ktlint. 17 | -------------------------------------------------------------------------------- /documentation/release-latest/docs/readme.md: -------------------------------------------------------------------------------- 1 | # Build & test documentation on local machine 2 | 3 | The documentation of ktlint is served with [mkdocs-material](https://squidfunk.github.io/mkdocs-material/creating-your-site/#advanced-configuration). For full documentation visit [mkdocs.org](https://www.mkdocs.org). 4 | 5 | To build and test documentation on your local development machine, follow steps below: 6 | 7 | ## Setup 8 | 1. In IntelliJ IDEA 9 | * Open `Preferences` 10 | * Search for `JSON Schema mappings` 11 | * Add new schema for url `https://squidfunk.github.io/mkdocs-material/schema.json` and add file `mkdocs.yml` for this url. 12 | 2. Pull docker image 13 | ```shell 14 | $ docker pull squidfunk/mkdocs-material 15 | ``` 16 | 17 | ## Build server 18 | The following steps build and host the documentation locally, updating automatically whenever a local file is changed. 19 | 20 | 1. Start mkdocs server from root of project (e.g. from same directory where file mkdocs.yml is located) 21 | ```shell 22 | docker run --rm -it -p 8000:8000 -v ${PWD}:/docs squidfunk/mkdocs-material 23 | ``` 24 | 2. Visit page `http://0.0.0.0:8000/` in your browser. 25 | 3. Edit the documentation and explicitly save the file. The mkdocs server refreshes its cached and the current page in the browser is automatically refreshed. 26 | 27 | ## Build once 28 | If you do not want to run a local server, or if you want to inspect the built files, you can run the following command from the project's main directory to build the documentation in the `site/` directory. 29 | 30 | ```shell 31 | docker run --rm -it -p 8000:8000 -v ${PWD}:/docs squidfunk/mkdocs-material build 32 | ``` 33 | -------------------------------------------------------------------------------- /documentation/release-latest/docs/rules/code-styles.md: -------------------------------------------------------------------------------- 1 | Starting from version `1.0`, `ktlint_official` is the default code style. If you want to revert to another code style, then set the `.editorconfig` property `ktlint_code_style`. 2 | 3 | ```ini 4 | [*.{kt,kts}] 5 | ktlint_code_style = intellij_idea # or android_studio or ktlint_official (default) 6 | ``` 7 | 8 | The `ktlint_official` code style combines the best elements from the [Kotlin Coding conventions](https://kotlinlang.org/docs/coding-conventions.html) and [Android's Kotlin styleguide](https://developer.android.com/kotlin/style-guide). This code style also provides additional formatting on topics which are not (explicitly) mentioned in those conventions and style guide. 9 | 10 | !!! note 11 | Be aware that this code style in some cases formats code in a way which is not accepted by the default code formatters in IntelliJ IDEA and Android Studio. The formatters of those editors produce nicely formatted code in the vast majority of cases. But in a number of edge cases, the formatting contains bugs which are waiting to be fixed for several years. The new code style formats code in a way which is compatible with the default formatting of the editors whenever possible. When using this codestyle, it is best to disable (e.g. not use) code formatting in the editor. 12 | 13 | * The `intellij_idea` (formerly `official`) code style aims to be compatible with default formatter of IntelliJ IDEA. This code style is based on [Kotlin Coding conventions](https://kotlinlang.org/docs/coding-conventions.html). 14 | 15 | * The `android_studio` (formerly `android`) aims to be compatible with default formatter of Android Studio. This code style is based on [Android's Kotlin styleguide](https://developer.android.com/kotlin/style-guide). 16 | -------------------------------------------------------------------------------- /documentation/release-latest/docs/rules/configuration-intellij-idea.md: -------------------------------------------------------------------------------- 1 | # Intellij IDEA configuration 2 | 3 | `ktlint` strives to prevent code formatting conflicts with IntelliJ IDEA / Android Studio as much as possible. In some cases, `ktlint` deliberately deviates from IDEA formatting. 4 | 5 | ## Preventing conflicts 6 | 7 | Many conflicts can be prevented by setting following `.editorconfig` settings: 8 | ``` 9 | root = true 10 | 11 | [*] 12 | insert_final_newline = true 13 | 14 | [{*.kt,*.kts}] 15 | ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL 16 | 17 | # Disable wildcard imports entirely 18 | ij_kotlin_name_count_to_use_star_import = 2147483647 19 | ij_kotlin_name_count_to_use_star_import_for_members = 2147483647 20 | ij_kotlin_packages_to_use_import_on_demand = unset 21 | ``` 22 | 23 | Conflicts between `ktlint` and IDEA formatting can also be resolved by using the [ktlint-intellij-plugin](https://plugins.jetbrains.com/plugin/15057-ktlint) (or install via Intellij IDEA plugin marketplace) in `distract free` mode. In this mode, the plugin formats your code with `ktlint` while you're editing the code. 24 | 25 | # Cleaning up old XML configuration settings 26 | 27 | Projects which have been created with (old)er versions of Intellij IDEA might still contain XML configuration regarding code styling. It is advised to remove the directory `.idea/codeStyles` whenever it still exists in your project directory. 28 | -------------------------------------------------------------------------------- /documentation/release-latest/docs/rules/dependencies.md: -------------------------------------------------------------------------------- 1 | Preferably rules run independent of each other. In some case this is however not feasible. The diagram below shows the dependencies between the rules provided by KtLint. 2 | 3 | ![Image](../../assets/images/rule-dependencies.png) 4 | -------------------------------------------------------------------------------- /documentation/release-latest/docs/rules/index.md: -------------------------------------------------------------------------------- 1 | = Rules 2 | -------------------------------------------------------------------------------- /documentation/release-latest/overrides/main.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block outdated %} 4 | You're not viewing the latest version. 5 | 6 | Click here to go to latest. 7 | 8 | {% endblock %} 9 | 10 | The maven coordinates of `ktlint` have been changed. See 11 | 12 | new Maven coordinates 13 | 14 | 15 | -------------------------------------------------------------------------------- /documentation/release-latest/serve-docs-locally.sh: -------------------------------------------------------------------------------- 1 | # !/bin/bash 2 | 3 | echo "Serving docs from directory '$(basename "${PWD##*/}")'" 4 | echo "" 5 | 6 | python3 -m mkdocs serve 7 | if [[ $? -ne 0 ]]; then 8 | echo "Invalid command. Please ensure that 'mkdocs' is installed." 9 | echo " - If needed install python3" 10 | echo " - If needed run 'pip3 install mkdocs'" 11 | echo " - If needed run 'pip3 install mkdocs-material'" 12 | echo "Or run 'docker run --rm -it -p 8000:8000 -v ${PWD}:/docs squidfunk/mkdocs-material'" 13 | fi 14 | -------------------------------------------------------------------------------- /documentation/snapshot/docs/api/badge.md: -------------------------------------------------------------------------------- 1 | If you want to display a badge to show that your project is linted and formatted using `'ktlint` than you can add the 2 | [![ktlint](https://img.shields.io/badge/ktlint%20code--style-%E2%9D%A4-FF4081)](https://pinterest.github.io/ktlint/) badge: 3 | 4 | ```md title="Ktlint code style badge" 5 | [![ktlint](https://img.shields.io/badge/ktlint%20code--style-%E2%9D%A4-FF4081)](https://pinterest.github.io/ktlint/) 6 | ``` 7 | -------------------------------------------------------------------------------- /documentation/snapshot/docs/api/custom-reporter.md: -------------------------------------------------------------------------------- 1 | ## Build a custom reporter 2 | Take a look at [ktlint-cli-reporter-plain](https://github.com/pinterest/ktlint/tree/master/ktlint-cli-reporter-plain). 3 | 4 | In short, all you need to do is to implement a 5 | [ReporterV2](https://github.com/pinterest/ktlint/blob/master/ktlint-cli-reporter-core/src/main/kotlin/com/pinterest/ktlint/cli/reporter/core/api/ReporterV2.kt) and make it available by registering 6 | a custom [ReporterProviderV2](https://github.com/pinterest/ktlint/blob/master/ktlint-cli-reporter-core/src/main/kotlin/com/pinterest/ktlint/cli/reporter/core/api/ReporterProviderV2.kt) using 7 | `META-INF/services/com.pinterest.ktlint.cli.reporter.core.api.ReporterProviderV2`. Pack all of that into a JAR and you're done. 8 | 9 | To load a custom (3rd party) reporter use `ktlint --reporter=name,artifact=/path/to/custom-ktlint-reporter.jar` 10 | (see `ktlint --help` for more). 11 | 12 | ## Third party reporters 13 | 14 | Known third-party reporters: 15 | 16 | * [kryanod/ktlint-junit-reporter](https://github.com/kryanod/ktlint-junit-reporter) reports ktlint output as an xml file in JUnit format so that the ktlint report can be made visible on the Merge Request page. 17 | * [musichin/ktlint-github-reporter](https://github.com/musichin/ktlint-github-reporter) uses [GitHub workflow commands](https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-error-message) to set error messages for `ktlint` issues. 18 | * [tobi2k/ktlint-gitlab-reporter](https://github.com/tobi2k/ktlint-gitlab-reporter) provides output in JSON format that can be parsed by GitLab automatically. 19 | -------------------------------------------------------------------------------- /documentation/snapshot/docs/api/index.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinterest/ktlint/2bec8b42ce4f130b839f2de574ec2cc767f779fa/documentation/snapshot/docs/api/index.md -------------------------------------------------------------------------------- /documentation/snapshot/docs/api/overview.md: -------------------------------------------------------------------------------- 1 | Ktlint has an open API with which you can integrate. 2 | 3 | The diagram below show the internal module structure of KtLint. 4 | 5 | ![Image](../../assets/images/module-dependencies.png) 6 | 7 | The `Ktlint Rule Engine` is by far the most important module. It is responsible for executing the linting and formatting of the source code. The Rule Engine itself does not contain any rules. Rules are provided by API Consumers. 8 | 9 | The `Ktlint CLI` is an API Consumer of the `Ktlint Rule Engine`. Together with the `Ktlint Ruleset Standard` and the `Ktlint Reporter` modules the CLI offers a standalone tool which can easily be run from the commandline. Also, the `Ktlint CLI` can easily be used with custom rulesets and/or reporters. 10 | 11 | The `Ktlint Ruleset Core` module contains the logic which is required by each API Consumer of the `Ktlint Rule Engine`, the `Ktlint Ruleset Standard` and custom rulesets. 12 | 13 | The module `Ktlint Test` provide functionalities like `assertThatRule` which is used to write unit tests in a fluent AssertJ look-a-like style and can also be used for testing of custom rules. 14 | 15 | The `Ktlint logger` module provides functionality for writing log messages. 16 | 17 | -------------------------------------------------------------------------------- /documentation/snapshot/docs/assets/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinterest/ktlint/2bec8b42ce4f130b839f2de574ec2cc767f779fa/documentation/snapshot/docs/assets/images/favicon.ico -------------------------------------------------------------------------------- /documentation/snapshot/docs/assets/images/ktlint-intellij-plugin-distract-free-mode-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinterest/ktlint/2bec8b42ce4f130b839f2de574ec2cc767f779fa/documentation/snapshot/docs/assets/images/ktlint-intellij-plugin-distract-free-mode-1.png -------------------------------------------------------------------------------- /documentation/snapshot/docs/assets/images/ktlint-intellij-plugin-distract-free-mode-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinterest/ktlint/2bec8b42ce4f130b839f2de574ec2cc767f779fa/documentation/snapshot/docs/assets/images/ktlint-intellij-plugin-distract-free-mode-2.png -------------------------------------------------------------------------------- /documentation/snapshot/docs/assets/images/ktlint-intellij-plugin-manual-mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinterest/ktlint/2bec8b42ce4f130b839f2de574ec2cc767f779fa/documentation/snapshot/docs/assets/images/ktlint-intellij-plugin-manual-mode.png -------------------------------------------------------------------------------- /documentation/snapshot/docs/assets/images/ktlint-intellij-plugin-preferences.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinterest/ktlint/2bec8b42ce4f130b839f2de574ec2cc767f779fa/documentation/snapshot/docs/assets/images/ktlint-intellij-plugin-preferences.png -------------------------------------------------------------------------------- /documentation/snapshot/docs/assets/images/ktlint-intellij-plugin-suppress-violation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinterest/ktlint/2bec8b42ce4f130b839f2de574ec2cc767f779fa/documentation/snapshot/docs/assets/images/ktlint-intellij-plugin-suppress-violation.png -------------------------------------------------------------------------------- /documentation/snapshot/docs/assets/images/module-dependencies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinterest/ktlint/2bec8b42ce4f130b839f2de574ec2cc767f779fa/documentation/snapshot/docs/assets/images/module-dependencies.png -------------------------------------------------------------------------------- /documentation/snapshot/docs/assets/images/psi-viewer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinterest/ktlint/2bec8b42ce4f130b839f2de574ec2cc767f779fa/documentation/snapshot/docs/assets/images/psi-viewer.png -------------------------------------------------------------------------------- /documentation/snapshot/docs/assets/images/rule-dependencies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinterest/ktlint/2bec8b42ce4f130b839f2de574ec2cc767f779fa/documentation/snapshot/docs/assets/images/rule-dependencies.png -------------------------------------------------------------------------------- /documentation/snapshot/docs/contributing/code-of-conduct.md: -------------------------------------------------------------------------------- 1 | At Pinterest, we work hard to ensure that our work environment is welcoming and inclusive to as many people as possible. We are committed to creating this environment for everyone involved in our open source projects as well. We welcome all participants regardless of ability, age, ethnicity, identified gender, religion (or lack there of), sexual orientation and socioeconomic status. 2 | 3 | This code of conduct details our expectations for upholding these values. 4 | 5 | ## Good behavior 6 | 7 | We expect members of our community to exhibit good behavior including (but of course not limited to): 8 | 9 | - Using intentional and empathetic language. 10 | - Focusing on resolving instead of escalating conflict. 11 | - Providing constructive feedback. 12 | 13 | ## Unacceptable behavior 14 | 15 | Some examples of unacceptable behavior (again, this is not an exhaustive list): 16 | 17 | - Harassment, publicly or in private. 18 | - Trolling. 19 | - Sexual advances (this isn’t the place for it). 20 | - Publishing other’s personal information. 21 | - Any behavior which would be deemed unacceptable in a professional environment. 22 | 23 | ## Recourse 24 | 25 | If you are witness to or the target of unacceptable behavior, it should be reported to Pinterest at opensource-policy@pinterest.com. All reporters will be kept confidential and an appropriate response for each incident will be evaluated. 26 | 27 | If the maintainers do not uphold and enforce this code of conduct in good faith, community leadership will hold them accountable. 28 | -------------------------------------------------------------------------------- /documentation/snapshot/docs/contributing/index.md: -------------------------------------------------------------------------------- 1 | ## Contributing guidelines 2 | -------------------------------------------------------------------------------- /documentation/snapshot/docs/contributing/overview.md: -------------------------------------------------------------------------------- 1 | !!! important 2 | Make sure to read the [Contributing guideline](guidelines.md) and the [code of conduct](code-of-conduct.md) first. 3 | 4 | ## Development 5 | 6 | Development starts with cloning and building the project on your local machine: 7 | 8 | ```sh 9 | git clone https://github.com/pinterest/ktlint && cd ktlint 10 | ./gradlew tasks # shows how to build, test, run, etc. project 11 | ``` 12 | 13 | !!! tip 14 | To open and run `ktlint` in Intellij IDEA: 15 | 16 | * File -> Open.... 17 | * You'll also need to set the "Project language level" to 8 in "Project Settings" (File -> Project Structure... -> Project). 18 | * To run `ktlint` - right-click on `ktlint/src/main/kotlin/com/pinterest/ktlint/Main.kt` -> Run. 19 | 20 | -------------------------------------------------------------------------------- /documentation/snapshot/docs/install/index.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinterest/ktlint/2bec8b42ce4f130b839f2de574ec2cc767f779fa/documentation/snapshot/docs/install/index.md -------------------------------------------------------------------------------- /documentation/snapshot/docs/install/snapshot-build.md: -------------------------------------------------------------------------------- 1 | ## Access to the latest `master` snapshot 2 | 3 | Whenever a commit is added to the `master` branch a snapshot build is automatically uploaded to [Sonatype's snapshots repository](https://oss.sonatype.org/content/repositories/snapshots/com/pinterest/ktlint/). 4 | If you are eager to try upcoming changes (that might or might not be included in the next stable release) you can do 5 | so by changing version of ktlint to `-SNAPSHOT` + adding a repo: 6 | 7 | ### Maven 8 | 9 | ```xml 10 | ... 11 | 12 | sonatype-snapshots 13 | https://oss.sonatype.org/content/repositories/snapshots 14 | 15 | true 16 | 17 | 18 | false 19 | 20 | 21 | ... 22 | ``` 23 | 24 | ### Gradle 25 | 26 | ```groovy 27 | repositories { 28 | maven { 29 | url "https://oss.sonatype.org/content/repositories/snapshots" 30 | } 31 | } 32 | ``` 33 | 34 | ### Kotlin development version snapshot 35 | 36 | Additionally, project publishes snapshots build against latest kotlin development version. To use them, change version 37 | of ktlint to `-kotlin-dev-SNAPSHOT`. 38 | -------------------------------------------------------------------------------- /documentation/snapshot/docs/quick-start.md: -------------------------------------------------------------------------------- 1 | Follow steps below for a quick start with latest ktlint release. 2 | 3 | ## Step 1: Install with brew 4 | ```shell 5 | brew install ktlint 6 | ``` 7 | See [download and verification from GitHub](../install/cli/#download-and-verification) or [other package managers](../install/cli/#package-managers) for alternative ways of installing ktlint. Or, use one of the [integrations like maven and gradle plugins](../install/integrations/). 8 | 9 | ## Step 2: Lint and format your code 10 | All files with extension `.kt` and `.kts` in the current directory and below will be scanned. Problems will be fixed automatically when possible. 11 | ```shell title="Autocorrect style violations" 12 | ktlint --format 13 | # or 14 | ktlint -F 15 | ``` 16 | See [cli usage](../install/cli/#command-line-usage) for a more extensive description on using ktlint. 17 | -------------------------------------------------------------------------------- /documentation/snapshot/docs/readme.md: -------------------------------------------------------------------------------- 1 | # Build & test documentation on local machine 2 | 3 | The documentation of ktlint is served with [mkdocs-material](https://squidfunk.github.io/mkdocs-material/creating-your-site/#advanced-configuration). For full documentation visit [mkdocs.org](https://www.mkdocs.org). 4 | 5 | To build and test documentation on your local development machine, follow steps below: 6 | 7 | ## Setup 8 | 1. In IntelliJ IDEA 9 | * Open `Preferences` 10 | * Search for `JSON Schema mappings` 11 | * Add new schema for url `https://squidfunk.github.io/mkdocs-material/schema.json` and add file `mkdocs.yml` for this url. 12 | 2. Pull docker image 13 | ```shell 14 | $ docker pull squidfunk/mkdocs-material 15 | ``` 16 | 17 | ## Build server 18 | The following steps build and host the documentation locally, updating automatically whenever a local file is changed. 19 | 20 | 1. Start mkdocs server from root of project (e.g. from same directory where file mkdocs.yml is located) 21 | ```shell 22 | docker run --rm -it -p 8000:8000 -v ${PWD}:/docs squidfunk/mkdocs-material 23 | ``` 24 | 2. Visit page `http://0.0.0.0:8000/` in your browser. 25 | 3. Edit the documentation and explicitly save the file. The mkdocs server refreshes its cached and the current page in the browser is automatically refreshed. 26 | 27 | ## Build once 28 | If you do not want to run a local server, or if you want to inspect the built files, you can run the following command from the project's main directory to build the documentation in the `site/` directory. 29 | 30 | ```shell 31 | docker run --rm -it -p 8000:8000 -v ${PWD}:/docs squidfunk/mkdocs-material build 32 | ``` 33 | -------------------------------------------------------------------------------- /documentation/snapshot/docs/rules/code-styles.md: -------------------------------------------------------------------------------- 1 | Starting from version `1.0`, `ktlint_official` is the default code style. If you want to revert to another code style, then set the `.editorconfig` property `ktlint_code_style`. 2 | 3 | ```ini 4 | [*.{kt,kts}] 5 | ktlint_code_style = intellij_idea # or android_studio or ktlint_official (default) 6 | ``` 7 | 8 | The `ktlint_official` code style combines the best elements from the [Kotlin Coding conventions](https://kotlinlang.org/docs/coding-conventions.html) and [Android's Kotlin styleguide](https://developer.android.com/kotlin/style-guide). This code style also provides additional formatting on topics which are not (explicitly) mentioned in those conventions and style guide. 9 | 10 | !!! note 11 | Be aware that this code style in some cases formats code in a way which is not accepted by the default code formatters in IntelliJ IDEA and Android Studio. The formatters of those editors produce nicely formatted code in the vast majority of cases. But in a number of edge cases, the formatting contains bugs which are waiting to be fixed for several years. The new code style formats code in a way which is compatible with the default formatting of the editors whenever possible. When using this codestyle, it is best to disable (e.g. not use) code formatting in the editor. 12 | 13 | * The `intellij_idea` (formerly `official`) code style aims to be compatible with default formatter of IntelliJ IDEA. This code style is based on [Kotlin Coding conventions](https://kotlinlang.org/docs/coding-conventions.html). 14 | 15 | * The `android_studio` (formerly `android`) aims to be compatible with default formatter of Android Studio. This code style is based on [Android's Kotlin styleguide](https://developer.android.com/kotlin/style-guide). 16 | -------------------------------------------------------------------------------- /documentation/snapshot/docs/rules/configuration-intellij-idea.md: -------------------------------------------------------------------------------- 1 | # Intellij IDEA configuration 2 | 3 | `ktlint` strives to prevent code formatting conflicts with IntelliJ IDEA / Android Studio as much as possible. In some cases, `ktlint` deliberately deviates from IDEA formatting. 4 | 5 | ## Preventing conflicts 6 | 7 | Many conflicts can be prevented by setting following `.editorconfig` settings: 8 | ``` 9 | root = true 10 | 11 | [*] 12 | insert_final_newline = true 13 | 14 | [{*.kt,*.kts}] 15 | ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL 16 | 17 | # Disable wildcard imports entirely 18 | ij_kotlin_name_count_to_use_star_import = 2147483647 19 | ij_kotlin_name_count_to_use_star_import_for_members = 2147483647 20 | ij_kotlin_packages_to_use_import_on_demand = unset 21 | ``` 22 | 23 | Conflicts between `ktlint` and IDEA formatting can also be resolved by using the [ktlint-intellij-plugin](https://plugins.jetbrains.com/plugin/15057-ktlint) (or install via Intellij IDEA plugin marketplace) in `distract free` mode. In this mode, the plugin formats your code with `ktlint` while you're editing the code. 24 | 25 | # Cleaning up old XML configuration settings 26 | 27 | Projects which have been created with (old)er versions of Intellij IDEA might still contain XML configuration regarding code styling. It is advised to remove the directory `.idea/codeStyles` whenever it still exists in your project directory. 28 | -------------------------------------------------------------------------------- /documentation/snapshot/docs/rules/dependencies.md: -------------------------------------------------------------------------------- 1 | Preferably rules run independent of each other. In some case this is however not feasible. The diagram below shows the dependencies between the rules provided by KtLint. 2 | 3 | ![Image](../../assets/images/rule-dependencies.png) 4 | -------------------------------------------------------------------------------- /documentation/snapshot/docs/rules/index.md: -------------------------------------------------------------------------------- 1 | = Rules 2 | -------------------------------------------------------------------------------- /documentation/snapshot/overrides/main.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block outdated %} 4 | You're viewing the SNAPSHOT version of the documentation. Be sure to use the 5 | 6 | SNAPSHOT version of ktlint 7 | 8 | , or go to the latest 9 | 10 | RELEASE version of the documentation 11 | 12 | {% endblock %} 13 | 14 | The maven coordinates of `ktlint` have been changed. See 15 | 16 | new Maven coordinates 17 | 18 | 19 | -------------------------------------------------------------------------------- /documentation/snapshot/serve-docs-locally.sh: -------------------------------------------------------------------------------- 1 | # !/bin/bash 2 | 3 | echo "Serving docs from directory '$(basename "${PWD##*/}")'" 4 | echo "" 5 | 6 | python3 -m mkdocs serve 7 | if [[ $? -ne 0 ]]; then 8 | echo "Invalid command. Please ensure that 'mkdocs' is installed." 9 | echo " - If needed install python3" 10 | echo " - If needed run 'pip3 install mkdocs'" 11 | echo " - If needed run 'pip3 install mkdocs-material'" 12 | echo "Or run 'docker run --rm -it -p 8000:8000 -v ${PWD}:/docs squidfunk/mkdocs-material'" 13 | fi 14 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | VERSION_NAME=1.6.1-SNAPSHOT 2 | POM_GROUP_ID=com.pinterest.ktlint 3 | 4 | POM_DESCRIPTION=An anti-bikeshedding Kotlin linter with built-in formatter. 5 | 6 | POM_URL=https://github.com/pinterest/ktlint 7 | POM_SCM_URL=https://github.com/pinterest/ktlint 8 | POM_SCM_CONNECTION=scm:git:git://github.com/pinterest/ktlint.git 9 | POM_SCM_DEV_CONNECTION=scm:git:ssh://git@github.com/pinterest/ktlint.git 10 | POM_PACKAGING=jar 11 | 12 | POM_LICENSE_NAME=MIT 13 | POM_LICENSE_URL=https://opensource.org/licenses/MIT 14 | POM_LICENSE_DIST=repo 15 | 16 | POM_DEVELOPER_ID=pinterest 17 | POM_DEVELOPER_NAME=Pinterest, Inc. 18 | 19 | # Default Gradle properties, can be overridden by other locations https://docs.gradle.org/8.2.1/userguide/build_environment.html#sec:gradle_configuration_properties 20 | org.gradle.jvmargs=-Xmx4g 21 | org.gradle.parallel=true 22 | org.gradle.caching=true 23 | # The release process of ktlint breaks whenever the configuration cache is enabled as not all gradle tasks fully supports this feature yet. 24 | # As the configuration cache only slightly improves the build performance, it is kept disabled for now. 25 | org.gradle.configuration-cache=false 26 | 27 | # Enable helpers for migration to Dokka V2 28 | org.jetbrains.dokka.experimental.gradle.pluginMode=V2EnabledWithHelpers 29 | -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | # The java-compilation version is the latest supported (not necessarily LTS) version of Java. It should be identical to Java-version used in `actions/setup-java` 3 | java-compilation = "21" 4 | # The java-target version is the lowest supported LTS version of Java. Jar's produced are bytecode compatible with this version. 5 | java-target = "8" 6 | kotlin = "2.1.21" 7 | kotlinDev = "2.2.0-RC" 8 | 9 | [plugins] 10 | kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } 11 | checksum = "org.gradle.crypto.checksum:1.4.0" 12 | shadow = "com.gradleup.shadow:8.3.6" 13 | kotlinx-binary-compatibiltiy-validator = "org.jetbrains.kotlinx.binary-compatibility-validator:0.17.0" 14 | 15 | [libraries] 16 | kotlin-compiler = { module = "org.jetbrains.kotlin:kotlin-compiler-embeddable", version.ref = "kotlin" } 17 | kotlin-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } 18 | kotlin-plugin-dev = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlinDev" } 19 | clikt = "com.github.ajalt.clikt:clikt:5.0.3" 20 | dokka = "org.jetbrains.dokka:dokka-gradle-plugin:2.0.0" 21 | ec4j = "org.ec4j.core:ec4j-core:1.1.1" 22 | logging = "io.github.oshai:kotlin-logging-jvm:7.0.7" 23 | slf4j = "org.slf4j:slf4j-simple:2.0.17" 24 | poko = "dev.drewhamilton.poko:poko-gradle-plugin:0.18.7" 25 | # Use logback-classic as the logger for kotlin-logging / slf4j as it allow changing the log level at runtime. 26 | # TODO: Update "renovate.json" once logback-classic is updated to 1.4 (once java8 support for ktlint is dropped) 27 | logback = "ch.qos.logback:logback-classic:1.3.15" 28 | logcaptor = "io.github.hakky54:logcaptor:2.11.0" 29 | # Required for logback-test.xml conditional configuration so that trace-logging in unit test can be automatically enabled using an 30 | # environment variable 31 | janino = "org.codehaus.janino:janino:3.1.12" 32 | # Testing libraries 33 | junit5-jupiter = "org.junit.jupiter:junit-jupiter:5.13.0" 34 | junit5-platform-launcher = "org.junit.platform:junit-platform-launcher:1.13.0" 35 | assertj = "org.assertj:assertj-core:3.27.3" 36 | sarif4k = "io.github.detekt.sarif4k:sarif4k:0.6.0" 37 | jimfs = "com.google.jimfs:jimfs:1.3.0" 38 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinterest/ktlint/2bec8b42ce4f130b839f2de574ec2cc767f779fa/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionSha256Sum=845952a9d6afa783db70bb3b0effaae45ae5542ca2bb7929619e8af49cb634cf 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip 5 | networkTimeout=10000 6 | validateDistributionUrl=true 7 | zipStoreBase=GRADLE_USER_HOME 8 | zipStorePath=wrapper/dists 9 | -------------------------------------------------------------------------------- /jitpack.yml: -------------------------------------------------------------------------------- 1 | install: 2 | - ./mvnw install -DskipTests 3 | 4 | -------------------------------------------------------------------------------- /ktlint-api-consumer/README.md: -------------------------------------------------------------------------------- 1 | # Ktlint API Consumer 2 | 3 | This module contains a very basic example of how to implement an API Consumer on top of the Ktlint API. If you want to implement your own custom API Consumer than you can copy this module into a stand-alone project or as submodule into another project. 4 | -------------------------------------------------------------------------------- /ktlint-api-consumer/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("ktlint-kotlin-common") 3 | } 4 | 5 | dependencies { 6 | // Any SLF4J compatible logging framework can be used. The "slf4j-simple" logging provider is configured in file 7 | // ktlint-api-consumer/src/main/resources/simplelogger.properties 8 | runtimeOnly(libs.slf4j) 9 | 10 | implementation(projects.ktlintLogger) 11 | implementation(projects.ktlintRuleEngine) 12 | 13 | // If the API consumer depends on a fixed set of ruleset, it might be best to provide those dependencies at compile time. In this way 14 | // statically typing can be used when defining the EditorConfigOverride for the KtlintRuleEngine. However, in this example, the 15 | // dependencies are provided at runtime. 16 | // implementation(projects.ktlintRulesetStandard) 17 | 18 | // For advanced use cases, the API consumer might prefer to provide the ruleset dependencies at runtime and load them dynamically using 19 | // the RuleSetProvider of ktlint-cli-ruleset-core. 20 | implementation(projects.ktlintCliRulesetCore) 21 | runtimeOnly(projects.ktlintRulesetStandard) 22 | 23 | // The standard ruleset is also provided as test dependency to demonstrate that rules that are provided at compile time can also be unit 24 | // tested. 25 | testImplementation(projects.ktlintRulesetStandard) 26 | 27 | testImplementation(projects.ktlintTest) 28 | 29 | testImplementation(libs.junit5.jupiter) 30 | // Since Gradle 8 the platform launcher needs explicitly be defined as runtime dependency to avoid classpath problems 31 | // https://docs.gradle.org/8.12/userguide/upgrading_version_8.html#test_framework_implementation_dependencies 32 | testRuntimeOnly(libs.junit5.platform.launcher) 33 | } 34 | -------------------------------------------------------------------------------- /ktlint-api-consumer/src/main/kotlin/com/example/ktlint/api/consumer/rules/CustomRuleSetProvider.kt: -------------------------------------------------------------------------------- 1 | package com.example.ktlint.api.consumer.rules 2 | 3 | import com.pinterest.ktlint.rule.engine.core.api.RuleProvider 4 | 5 | internal val CUSTOM_RULE_SET_ID = "custom-rule-set-id" 6 | 7 | internal val KTLINT_API_CONSUMER_RULE_PROVIDERS = 8 | setOf( 9 | // Can provide custom rules 10 | RuleProvider { NoVarRule() }, 11 | // If rulesets are include at compile time, they can be added to the custom rule provider. 12 | // RuleProvider { IndentationRule() }, 13 | ) 14 | -------------------------------------------------------------------------------- /ktlint-api-consumer/src/main/kotlin/com/example/ktlint/api/consumer/rules/NoVarRule.kt: -------------------------------------------------------------------------------- 1 | package com.example.ktlint.api.consumer.rules 2 | 3 | import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision 4 | import com.pinterest.ktlint.rule.engine.core.api.ElementType 5 | import com.pinterest.ktlint.rule.engine.core.api.Rule 6 | import com.pinterest.ktlint.rule.engine.core.api.RuleAutocorrectApproveHandler 7 | import com.pinterest.ktlint.rule.engine.core.api.RuleId 8 | import org.jetbrains.kotlin.com.intellij.lang.ASTNode 9 | 10 | public class NoVarRule : 11 | Rule( 12 | ruleId = RuleId("$CUSTOM_RULE_SET_ID:no-var"), 13 | about = About(), 14 | ), 15 | RuleAutocorrectApproveHandler { 16 | override fun beforeVisitChildNodes( 17 | node: ASTNode, 18 | emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, 19 | ) { 20 | if (node.elementType == ElementType.VAR_KEYWORD) { 21 | emit(node.startOffset, "Unexpected var, use val instead", false) 22 | // In case that LintError can be autocorrected, use syntax below 23 | // .ifAutocorrectAllowed { 24 | // // Fix 25 | // } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ktlint-api-consumer/src/main/readme.md: -------------------------------------------------------------------------------- 1 | # Ktlint API Consumer 2 | 3 | A very rudimentary example of how the Ktlint API can be used. For a more complete implementation see the `ktlint CLI` module. 4 | -------------------------------------------------------------------------------- /ktlint-api-consumer/src/test/readme.md: -------------------------------------------------------------------------------- 1 | # Ktlint API Consumer API tests 2 | 3 | The tests in this module do *not* test the Ktlint API Consumer itself. The tests verify the usage of the Ktlint API from the perspective of the API Consumer. This to ensure that the API is usable from an external perspective. 4 | -------------------------------------------------------------------------------- /ktlint-bom/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `java-platform` 3 | id("ktlint-publication") 4 | } 5 | 6 | publishing.publications.named("maven") { 7 | from(components["javaPlatform"]) 8 | } 9 | 10 | dependencies { 11 | logger.lifecycle("Creating dependencies for ktlint-bom") 12 | constraints { 13 | project.rootProject.subprojects.forEach { subproject -> 14 | subproject.plugins.withId("ktlint-publication") { 15 | // Exclude self project from BOM. 16 | if (subproject == project) return@withId 17 | logger.lifecycle(" + Add api dependency on '${subproject.name}' to ktlint-bom") 18 | api(subproject) 19 | } 20 | } 21 | } 22 | logger.lifecycle("Finished creating dependencies for ktlint-bom") 23 | } 24 | -------------------------------------------------------------------------------- /ktlint-bom/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=ktlint-bom 2 | POM_ARTIFACT_ID=ktlint-bom 3 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-baseline/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("ktlint-publication-library") 3 | } 4 | 5 | dependencies { 6 | implementation(projects.ktlintLogger) 7 | implementation(projects.ktlintRuleEngineCore) 8 | implementation(projects.ktlintCliReporterCore) 9 | 10 | testImplementation(projects.ktlintTest) 11 | testImplementation(libs.logback) 12 | testImplementation(libs.logcaptor) 13 | 14 | testImplementation(libs.junit5.jupiter) 15 | // Since Gradle 8 the platform launcher needs explicitly be defined as runtime dependency to avoid classpath problems 16 | // https://docs.gradle.org/8.12/userguide/upgrading_version_8.html#test_framework_implementation_dependencies 17 | testRuntimeOnly(libs.junit5.platform.launcher) 18 | } 19 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-baseline/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=ktlint-cli-reporter-baseline 2 | POM_ARTIFACT_ID=ktlint-cli-reporter-baseline 3 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-baseline/src/main/kotlin/com/pinterest/ktlint/cli/reporter/baseline/BaselineReporterProvider.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.cli.reporter.baseline 2 | 3 | import com.pinterest.ktlint.cli.reporter.core.api.ReporterProviderV2 4 | import java.io.PrintStream 5 | 6 | public class BaselineReporterProvider : ReporterProviderV2 { 7 | override val id: String = "baseline" 8 | 9 | override fun get( 10 | out: PrintStream, 11 | opt: Map, 12 | ): BaselineReporter = BaselineReporter(out) 13 | } 14 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-baseline/src/main/resources/META-INF/services/com.pinterest.ktlint.cli.reporter.core.api.ReporterProviderV2: -------------------------------------------------------------------------------- 1 | com.pinterest.ktlint.cli.reporter.baseline.BaselineReporterProvider 2 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-baseline/src/test/resources/baseline-invalid.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-baseline/src/test/resources/baseline-valid.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-checkstyle/api/ktlint-cli-reporter-checkstyle.api: -------------------------------------------------------------------------------- 1 | public final class com/pinterest/ktlint/cli/reporter/checkstyle/CheckStyleReporter : com/pinterest/ktlint/cli/reporter/core/api/ReporterV2 { 2 | public fun (Ljava/io/PrintStream;)V 3 | public fun after (Ljava/lang/String;)V 4 | public fun afterAll ()V 5 | public fun before (Ljava/lang/String;)V 6 | public fun beforeAll ()V 7 | public fun onLintError (Ljava/lang/String;Lcom/pinterest/ktlint/cli/reporter/core/api/KtlintCliError;)V 8 | } 9 | 10 | public final class com/pinterest/ktlint/cli/reporter/checkstyle/CheckStyleReporterProvider : com/pinterest/ktlint/cli/reporter/core/api/ReporterProviderV2 { 11 | public fun ()V 12 | public fun get (Ljava/io/PrintStream;Ljava/util/Map;)Lcom/pinterest/ktlint/cli/reporter/checkstyle/CheckStyleReporter; 13 | public synthetic fun get (Ljava/io/PrintStream;Ljava/util/Map;)Lcom/pinterest/ktlint/cli/reporter/core/api/ReporterV2; 14 | public fun getId ()Ljava/lang/String; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-checkstyle/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("ktlint-publication-library") 3 | } 4 | 5 | dependencies { 6 | implementation(projects.ktlintCliReporterCore) 7 | 8 | testImplementation(projects.ktlintTest) 9 | 10 | testImplementation(libs.junit5.jupiter) 11 | // Since Gradle 8 the platform launcher needs explicitly be defined as runtime dependency to avoid classpath problems 12 | // https://docs.gradle.org/8.12/userguide/upgrading_version_8.html#test_framework_implementation_dependencies 13 | testRuntimeOnly(libs.junit5.platform.launcher) 14 | } 15 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-checkstyle/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=ktlint-cli-reporter-checkstyle 2 | POM_ARTIFACT_ID=ktlint-cli-reporter-checkstyle 3 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-checkstyle/src/main/kotlin/com/pinterest/ktlint/cli/reporter/checkstyle/CheckStyleReporter.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.cli.reporter.checkstyle 2 | 3 | import com.pinterest.ktlint.cli.reporter.core.api.KtlintCliError 4 | import com.pinterest.ktlint.cli.reporter.core.api.KtlintCliError.Status.FORMAT_IS_AUTOCORRECTED 5 | import com.pinterest.ktlint.cli.reporter.core.api.ReporterV2 6 | import java.io.PrintStream 7 | import java.util.ArrayList 8 | import java.util.concurrent.ConcurrentHashMap 9 | 10 | public class CheckStyleReporter( 11 | private val out: PrintStream, 12 | ) : ReporterV2 { 13 | private val acc = ConcurrentHashMap>() 14 | 15 | override fun onLintError( 16 | file: String, 17 | ktlintCliError: KtlintCliError, 18 | ) { 19 | if (ktlintCliError.status != FORMAT_IS_AUTOCORRECTED) { 20 | acc.getOrPut(file) { ArrayList() }.add(ktlintCliError) 21 | } 22 | } 23 | 24 | override fun afterAll() { 25 | out.println("""""") 26 | out.println("""""") 27 | for ((file, errList) in acc.entries.sortedBy { it.key }) { 28 | out.println(""" """) 29 | for (err in errList) { 30 | with(err) { 31 | val message = detail.escapeXMLAttrValue() 32 | out.println( 33 | """ """, 34 | ) 35 | } 36 | } 37 | out.println(""" """) 38 | } 39 | out.println("""""") 40 | } 41 | 42 | private fun String.escapeXMLAttrValue() = 43 | this 44 | .replace("&", "&") 45 | .replace("\"", """) 46 | .replace("'", "'") 47 | .replace("<", "<") 48 | .replace(">", ">") 49 | } 50 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-checkstyle/src/main/kotlin/com/pinterest/ktlint/cli/reporter/checkstyle/CheckStyleReporterProvider.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.cli.reporter.checkstyle 2 | 3 | import com.pinterest.ktlint.cli.reporter.core.api.ReporterProviderV2 4 | import java.io.PrintStream 5 | 6 | public class CheckStyleReporterProvider : ReporterProviderV2 { 7 | override val id: String = "checkstyle" 8 | 9 | override fun get( 10 | out: PrintStream, 11 | opt: Map, 12 | ): CheckStyleReporter = CheckStyleReporter(out) 13 | } 14 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-checkstyle/src/main/resources/META-INF/services/com.pinterest.ktlint.cli.reporter.core.api.ReporterProviderV2: -------------------------------------------------------------------------------- 1 | com.pinterest.ktlint.cli.reporter.checkstyle.CheckStyleReporterProvider 2 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-core/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("ktlint-publication-library") 3 | } 4 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-core/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=ktlint-cli-reporter-core 2 | POM_ARTIFACT_ID=ktlint-cli-reporter-core 3 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-core/src/main/kotlin/com/pinterest/ktlint/cli/reporter/core/api/KtlintCliError.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.cli.reporter.core.api 2 | 3 | import dev.drewhamilton.poko.Poko 4 | import java.io.Serializable 5 | 6 | /** 7 | * Lint error. 8 | * 9 | * [line]: line number (one-based) 10 | * [col]: column number (one-based) 11 | * [ruleId]: rule id 12 | * [detail]: error message 13 | * [status]: status of error 14 | */ 15 | @Poko 16 | public class KtlintCliError( 17 | public val line: Int, 18 | public val col: Int, 19 | public val ruleId: String, 20 | public val detail: String, 21 | public val status: Status, 22 | ) : Serializable { 23 | public enum class Status { 24 | /** 25 | * An error that was ignored previously by adding it to the baseline file so that it will not be reported again at later invocations 26 | * of ktlint cli. 27 | */ 28 | BASELINE_IGNORED, 29 | 30 | /** 31 | * An error found by ktlint cli linter and which can not be autocorrected. 32 | */ 33 | LINT_CAN_NOT_BE_AUTOCORRECTED, 34 | 35 | /** 36 | * An error found by ktlint cli linter and which can be autocorrected. 37 | */ 38 | LINT_CAN_BE_AUTOCORRECTED, 39 | 40 | /** 41 | * An error found by ktlint cli linter and which has been autocorrected by the formatter. 42 | */ 43 | FORMAT_IS_AUTOCORRECTED, 44 | 45 | /** 46 | * An error found by the kotlin compiler when parsing. 47 | */ 48 | KOTLIN_PARSE_EXCEPTION, 49 | 50 | /** 51 | * An internal error which was not handled correctly in ktlint rule engine. 52 | */ 53 | KTLINT_RULE_ENGINE_EXCEPTION, 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-core/src/main/kotlin/com/pinterest/ktlint/cli/reporter/core/api/KtlintVersion.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.cli.reporter.core.api 2 | 3 | import java.util.jar.Manifest 4 | 5 | /** 6 | * Dynamically provides current ktlint version 7 | * 8 | * Note: The fallback reading from `/META-INF/MANIFEST.MF` is a 9 | * JDK 9 regression workaround (https://bugs.openjdk.java.net/browse/JDK-8190987, fixed in JDK 10) 10 | * (note that version reported by the fallback might not be null if META-INF/MANIFEST.MF is 11 | * loaded from another JAR on the classpath (e.g. if META-INF/MANIFEST.MF wasn't created as part of the build)) 12 | */ 13 | public fun ktlintVersion(javaClass: Class): String? = javaClass.`package`.implementationVersion ?: getManifestVersion(javaClass) 14 | 15 | private fun getManifestVersion(javaClass: Class) = 16 | javaClass 17 | .getResourceAsStream("/META-INF/MANIFEST.MF") 18 | ?.run { Manifest(this).mainAttributes.getValue("Implementation-Version") } 19 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-core/src/main/kotlin/com/pinterest/ktlint/cli/reporter/core/api/ReporterProviderV2.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.cli.reporter.core.api 2 | 3 | import java.io.PrintStream 4 | import java.io.Serializable 5 | 6 | /** 7 | * `ktlint` uses [ServiceLoader](https://docs.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html) to 8 | * discover all available [ReporterProviderV2]s on the classpath and so each [ReporterProviderV2] must be registered using 9 | * `META-INF/services/com.pinterest.ktlint.cli.reporter.core.api.ReporterProviderV2` 10 | * (see `ktlint-cli-reporter-plain/src/main/resources` for an example). 11 | */ 12 | public interface ReporterProviderV2 : Serializable { 13 | public val id: String 14 | 15 | public fun get( 16 | out: PrintStream, 17 | opt: Map, 18 | ): T 19 | } 20 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-core/src/main/kotlin/com/pinterest/ktlint/cli/reporter/core/api/ReporterV2.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.cli.reporter.core.api 2 | 3 | /** 4 | * Implementation must be thread-safe. In particular, [onLintError] might be called in parallel for 5 | * different files (but not for the same file) ([before], [onLintError] and [after] are guaranteed to be called 6 | * on the same thread). 7 | * @see ReporterProvider 8 | */ 9 | public interface ReporterV2 { 10 | /** 11 | * This function is called once, before the processing begins (regardless of whether any files matching the pattern are (going to) 12 | * found). It's guaranteed to be called before any of the other [ReporterV2]s methods. 13 | */ 14 | public fun beforeAll() {} 15 | 16 | /** 17 | * This function is called once for each file (matching the pattern) found, but before it's parsed. 18 | */ 19 | public fun before(file: String) {} 20 | 21 | /** 22 | * This function is called once for each lint error that is found. 23 | */ 24 | public fun onLintError( 25 | file: String, 26 | ktlintCliError: KtlintCliError, 27 | ) 28 | 29 | /** 30 | * This function is called once after the file has been parsed entirely. 31 | */ 32 | public fun after(file: String) {} 33 | 34 | /** 35 | * This function is called once, after all the files (if any) have been processed. It's guaranteed to be called after all other 36 | * [ReporterV2]s methods. 37 | */ 38 | public fun afterAll() {} 39 | } 40 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-format/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("ktlint-publication-library") 3 | } 4 | 5 | dependencies { 6 | implementation(projects.ktlintCliReporterCore) 7 | 8 | testImplementation(projects.ktlintTest) 9 | 10 | testImplementation(libs.junit5.jupiter) 11 | // Since Gradle 8 the platform launcher needs explicitly be defined as runtime dependency to avoid classpath problems 12 | // https://docs.gradle.org/8.12/userguide/upgrading_version_8.html#test_framework_implementation_dependencies 13 | testRuntimeOnly(libs.junit5.platform.launcher) 14 | } 15 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-format/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=ktlint-cli-reporter-format 2 | POM_ARTIFACT_ID=ktlint-cli-reporter-format 3 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-format/src/main/kotlin/com/pinterest/ktlint/cli/reporter/format/Color.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.cli.reporter.format 2 | 3 | /** 4 | * Stripped down version of https://github.com/ziggy42/kolor (ziggy42/kolor#6). 5 | */ 6 | public enum class Color( 7 | public val code: Int, 8 | ) { 9 | BLACK(30), 10 | RED(31), 11 | GREEN(32), 12 | YELLOW(33), 13 | BLUE(34), 14 | MAGENTA(35), 15 | CYAN(36), 16 | LIGHT_GRAY(37), 17 | DARK_GRAY(90), 18 | LIGHT_RED(91), 19 | LIGHT_GREEN(92), 20 | LIGHT_YELLOW(93), 21 | LIGHT_BLUE(94), 22 | LIGHT_MAGENTA(95), 23 | LIGHT_CYAN(96), 24 | WHITE(97), 25 | } 26 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-format/src/main/kotlin/com/pinterest/ktlint/cli/reporter/format/FormatReporterProvider.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.cli.reporter.format 2 | 3 | import com.pinterest.ktlint.cli.reporter.core.api.ReporterProviderV2 4 | import java.io.PrintStream 5 | 6 | public class FormatReporterProvider : ReporterProviderV2 { 7 | override val id: String = "format" 8 | 9 | override fun get( 10 | out: PrintStream, 11 | opt: Map, 12 | ): FormatReporter = 13 | FormatReporter( 14 | out, 15 | format = 16 | opt 17 | .getOrElse("format") { throw IllegalArgumentException("Format is not specified in config options") } 18 | .toBooleanStrict(), 19 | shouldColorOutput = opt["color"]?.emptyOrTrue() ?: false, 20 | outputColor = getColor(opt["color_name"]), 21 | ) 22 | 23 | private fun String.emptyOrTrue() = this == "" || this == "true" 24 | 25 | private fun getColor(color: String?): Color = 26 | Color.entries.firstOrNull { 27 | it.name == color 28 | } ?: throw IllegalArgumentException("Invalid color parameter.") 29 | } 30 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-format/src/main/resources/META-INF/services/com.pinterest.ktlint.cli.reporter.core.api.ReporterProviderV2: -------------------------------------------------------------------------------- 1 | com.pinterest.ktlint.cli.reporter.format.FormatReporterProvider 2 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-html/api/ktlint-cli-reporter-html.api: -------------------------------------------------------------------------------- 1 | public final class com/pinterest/ktlint/cli/reporter/html/HtmlReporter : com/pinterest/ktlint/cli/reporter/core/api/ReporterV2 { 2 | public fun (Ljava/io/PrintStream;)V 3 | public fun after (Ljava/lang/String;)V 4 | public fun afterAll ()V 5 | public fun before (Ljava/lang/String;)V 6 | public fun beforeAll ()V 7 | public fun onLintError (Ljava/lang/String;Lcom/pinterest/ktlint/cli/reporter/core/api/KtlintCliError;)V 8 | } 9 | 10 | public final class com/pinterest/ktlint/cli/reporter/html/HtmlReporterProvider : com/pinterest/ktlint/cli/reporter/core/api/ReporterProviderV2 { 11 | public fun ()V 12 | public synthetic fun get (Ljava/io/PrintStream;Ljava/util/Map;)Lcom/pinterest/ktlint/cli/reporter/core/api/ReporterV2; 13 | public fun get (Ljava/io/PrintStream;Ljava/util/Map;)Lcom/pinterest/ktlint/cli/reporter/html/HtmlReporter; 14 | public fun getId ()Ljava/lang/String; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-html/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("ktlint-publication-library") 3 | } 4 | 5 | dependencies { 6 | implementation(projects.ktlintCliReporterCore) 7 | 8 | testImplementation(projects.ktlintTest) 9 | 10 | testImplementation(libs.junit5.jupiter) 11 | // Since Gradle 8 the platform launcher needs explicitly be defined as runtime dependency to avoid classpath problems 12 | // https://docs.gradle.org/8.12/userguide/upgrading_version_8.html#test_framework_implementation_dependencies 13 | testRuntimeOnly(libs.junit5.platform.launcher) 14 | } 15 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-html/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=ktlint-cli-reporter-html 2 | POM_ARTIFACT_ID=ktlint-cli-reporter-html 3 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-html/src/main/kotlin/com/pinterest/ktlint/cli/reporter/html/HtmlReporterProvider.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019 Matheus Candido 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.pinterest.ktlint.cli.reporter.html 26 | 27 | import com.pinterest.ktlint.cli.reporter.core.api.ReporterProviderV2 28 | import java.io.PrintStream 29 | 30 | public class HtmlReporterProvider : ReporterProviderV2 { 31 | override val id: String = "html" 32 | 33 | override fun get( 34 | out: PrintStream, 35 | opt: Map, 36 | ): HtmlReporter = HtmlReporter(out) 37 | } 38 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-html/src/main/resources/META-INF/services/com.pinterest.ktlint.cli.reporter.core.api.ReporterProviderV2: -------------------------------------------------------------------------------- 1 | com.pinterest.ktlint.cli.reporter.html.HtmlReporterProvider 2 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-json/api/ktlint-cli-reporter-json.api: -------------------------------------------------------------------------------- 1 | public final class com/pinterest/ktlint/cli/reporter/json/JsonReporter : com/pinterest/ktlint/cli/reporter/core/api/ReporterV2 { 2 | public fun (Ljava/io/PrintStream;)V 3 | public fun after (Ljava/lang/String;)V 4 | public fun afterAll ()V 5 | public fun before (Ljava/lang/String;)V 6 | public fun beforeAll ()V 7 | public fun onLintError (Ljava/lang/String;Lcom/pinterest/ktlint/cli/reporter/core/api/KtlintCliError;)V 8 | } 9 | 10 | public final class com/pinterest/ktlint/cli/reporter/json/JsonReporterProvider : com/pinterest/ktlint/cli/reporter/core/api/ReporterProviderV2 { 11 | public fun ()V 12 | public synthetic fun get (Ljava/io/PrintStream;Ljava/util/Map;)Lcom/pinterest/ktlint/cli/reporter/core/api/ReporterV2; 13 | public fun get (Ljava/io/PrintStream;Ljava/util/Map;)Lcom/pinterest/ktlint/cli/reporter/json/JsonReporter; 14 | public fun getId ()Ljava/lang/String; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-json/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("ktlint-publication-library") 3 | } 4 | 5 | dependencies { 6 | implementation(projects.ktlintCliReporterCore) 7 | 8 | testImplementation(projects.ktlintTest) 9 | 10 | testImplementation(libs.junit5.jupiter) 11 | // Since Gradle 8 the platform launcher needs explicitly be defined as runtime dependency to avoid classpath problems 12 | // https://docs.gradle.org/8.12/userguide/upgrading_version_8.html#test_framework_implementation_dependencies 13 | testRuntimeOnly(libs.junit5.platform.launcher) 14 | } 15 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-json/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=ktlint-cli-reporter-json 2 | POM_ARTIFACT_ID=ktlint-cli-reporter-json 3 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-json/src/main/kotlin/com/pinterest/ktlint/cli/reporter/json/JsonReporterProvider.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.cli.reporter.json 2 | 3 | import com.pinterest.ktlint.cli.reporter.core.api.ReporterProviderV2 4 | import java.io.PrintStream 5 | 6 | public class JsonReporterProvider : ReporterProviderV2 { 7 | override val id: String = "json" 8 | 9 | override fun get( 10 | out: PrintStream, 11 | opt: Map, 12 | ): JsonReporter = JsonReporter(out) 13 | } 14 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-json/src/main/resources/META-INF/services/com.pinterest.ktlint.cli.reporter.core.api.ReporterProviderV2: -------------------------------------------------------------------------------- 1 | com.pinterest.ktlint.cli.reporter.json.JsonReporterProvider 2 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-plain-summary/api/ktlint-cli-reporter-plain-summary.api: -------------------------------------------------------------------------------- 1 | public final class com/pinterest/ktlint/cli/reporter/plainsummary/PlainSummaryReporter : com/pinterest/ktlint/cli/reporter/core/api/ReporterV2 { 2 | public static final field KOTLIN_PARSE_EXCEPTION_MESSAGE Ljava/lang/String; 3 | public static final field KTLINT_RULE_ENGINE_EXCEPTION_MESSAGE Ljava/lang/String; 4 | public static final field UNKNOWN_CAUSE_MESSAGE Ljava/lang/String; 5 | public fun (Ljava/io/PrintStream;)V 6 | public fun after (Ljava/lang/String;)V 7 | public fun afterAll ()V 8 | public fun before (Ljava/lang/String;)V 9 | public fun beforeAll ()V 10 | public fun onLintError (Ljava/lang/String;Lcom/pinterest/ktlint/cli/reporter/core/api/KtlintCliError;)V 11 | } 12 | 13 | public final class com/pinterest/ktlint/cli/reporter/plainsummary/PlainSummaryReporterProvider : com/pinterest/ktlint/cli/reporter/core/api/ReporterProviderV2 { 14 | public fun ()V 15 | public synthetic fun get (Ljava/io/PrintStream;Ljava/util/Map;)Lcom/pinterest/ktlint/cli/reporter/core/api/ReporterV2; 16 | public fun get (Ljava/io/PrintStream;Ljava/util/Map;)Lcom/pinterest/ktlint/cli/reporter/plainsummary/PlainSummaryReporter; 17 | public fun getId ()Ljava/lang/String; 18 | } 19 | 20 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-plain-summary/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("ktlint-publication-library") 3 | } 4 | 5 | dependencies { 6 | implementation(projects.ktlintCliReporterCore) 7 | 8 | testImplementation(projects.ktlintTest) 9 | 10 | testImplementation(libs.junit5.jupiter) 11 | // Since Gradle 8 the platform launcher needs explicitly be defined as runtime dependency to avoid classpath problems 12 | // https://docs.gradle.org/8.12/userguide/upgrading_version_8.html#test_framework_implementation_dependencies 13 | testRuntimeOnly(libs.junit5.platform.launcher) 14 | } 15 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-plain-summary/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=ktlint-cli-reporter-plain-summary 2 | POM_ARTIFACT_ID=ktlint-cli-reporter-plain-summary 3 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-plain-summary/src/main/kotlin/com/pinterest/ktlint/cli/reporter/plainsummary/PlainSummaryReporterProvider.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.cli.reporter.plainsummary 2 | 3 | import com.pinterest.ktlint.cli.reporter.core.api.ReporterProviderV2 4 | import java.io.PrintStream 5 | 6 | public class PlainSummaryReporterProvider : ReporterProviderV2 { 7 | override val id: String = "plain-summary" 8 | 9 | override fun get( 10 | out: PrintStream, 11 | opt: Map, 12 | ): PlainSummaryReporter = PlainSummaryReporter(out) 13 | } 14 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-plain-summary/src/main/resources/META-INF/services/com.pinterest.ktlint.cli.reporter.core.api.ReporterProviderV2: -------------------------------------------------------------------------------- 1 | com.pinterest.ktlint.cli.reporter.plainsummary.PlainSummaryReporterProvider 2 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-plain-summary/src/test/kotlin/com/pinterest/ktlint/cli/reporter/plain/PlainSummaryReporterProviderTest.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.cli.reporter.plain 2 | 3 | import com.pinterest.ktlint.cli.reporter.plainsummary.PlainSummaryReporterProvider 4 | import org.assertj.core.api.Assertions.assertThat 5 | import org.junit.jupiter.api.Test 6 | import java.io.PrintStream 7 | import java.lang.System.out 8 | 9 | class PlainSummaryReporterProviderTest { 10 | @Test 11 | fun `Get a plain summary reporter then create it without exception`() { 12 | val plainSummaryReporter = 13 | PlainSummaryReporterProvider().get( 14 | out = PrintStream(out, true), 15 | opt = emptyMap(), 16 | ) 17 | 18 | assertThat(plainSummaryReporter).isNotNull 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-plain/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("ktlint-publication-library") 3 | } 4 | 5 | dependencies { 6 | implementation(projects.ktlintCliReporterCore) 7 | 8 | testImplementation(projects.ktlintTest) 9 | 10 | testImplementation(libs.junit5.jupiter) 11 | // Since Gradle 8 the platform launcher needs explicitly be defined as runtime dependency to avoid classpath problems 12 | // https://docs.gradle.org/8.12/userguide/upgrading_version_8.html#test_framework_implementation_dependencies 13 | testRuntimeOnly(libs.junit5.platform.launcher) 14 | } 15 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-plain/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=ktlint-cli-reporter-plain 2 | POM_ARTIFACT_ID=ktlint-cli-reporter-plain 3 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-plain/src/main/kotlin/com/pinterest/ktlint/cli/reporter/plain/Color.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.cli.reporter.plain 2 | 3 | /** 4 | * Stripped down version of https://github.com/ziggy42/kolor (ziggy42/kolor#6). 5 | */ 6 | public enum class Color( 7 | public val code: Int, 8 | ) { 9 | BLACK(30), 10 | RED(31), 11 | GREEN(32), 12 | YELLOW(33), 13 | BLUE(34), 14 | MAGENTA(35), 15 | CYAN(36), 16 | LIGHT_GRAY(37), 17 | DARK_GRAY(90), 18 | LIGHT_RED(91), 19 | LIGHT_GREEN(92), 20 | LIGHT_YELLOW(93), 21 | LIGHT_BLUE(94), 22 | LIGHT_MAGENTA(95), 23 | LIGHT_CYAN(96), 24 | WHITE(97), 25 | } 26 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-plain/src/main/kotlin/com/pinterest/ktlint/cli/reporter/plain/PlainReporterProvider.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.cli.reporter.plain 2 | 3 | import com.pinterest.ktlint.cli.reporter.core.api.ReporterProviderV2 4 | import java.io.PrintStream 5 | 6 | public class PlainReporterProvider : ReporterProviderV2 { 7 | override val id: String = "plain" 8 | 9 | override fun get( 10 | out: PrintStream, 11 | opt: Map, 12 | ): PlainReporter = 13 | PlainReporter( 14 | out, 15 | groupByFile = opt["group_by_file"]?.emptyOrTrue() ?: false, 16 | shouldColorOutput = opt["color"]?.emptyOrTrue() ?: false, 17 | outputColor = getColor(opt["color_name"]), 18 | pad = opt["pad"]?.emptyOrTrue() ?: false, 19 | ) 20 | 21 | private fun String.emptyOrTrue() = this == "" || this == "true" 22 | 23 | private fun getColor(colorInput: String?): Color = 24 | Color.entries.firstOrNull { 25 | it.name == colorInput 26 | } ?: throw IllegalArgumentException("Invalid color parameter.") 27 | } 28 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-plain/src/main/resources/META-INF/services/com.pinterest.ktlint.cli.reporter.core.api.ReporterProviderV2: -------------------------------------------------------------------------------- 1 | com.pinterest.ktlint.cli.reporter.plain.PlainReporterProvider 2 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-sarif/api/ktlint-cli-reporter-sarif.api: -------------------------------------------------------------------------------- 1 | public final class com/pinterest/ktlint/cli/reporter/sarif/SarifReporter : com/pinterest/ktlint/cli/reporter/core/api/ReporterV2 { 2 | public fun (Ljava/io/PrintStream;)V 3 | public fun after (Ljava/lang/String;)V 4 | public fun afterAll ()V 5 | public fun before (Ljava/lang/String;)V 6 | public fun beforeAll ()V 7 | public fun onLintError (Ljava/lang/String;Lcom/pinterest/ktlint/cli/reporter/core/api/KtlintCliError;)V 8 | } 9 | 10 | public final class com/pinterest/ktlint/cli/reporter/sarif/SarifReporterProvider : com/pinterest/ktlint/cli/reporter/core/api/ReporterProviderV2 { 11 | public fun ()V 12 | public synthetic fun get (Ljava/io/PrintStream;Ljava/util/Map;)Lcom/pinterest/ktlint/cli/reporter/core/api/ReporterV2; 13 | public fun get (Ljava/io/PrintStream;Ljava/util/Map;)Lcom/pinterest/ktlint/cli/reporter/sarif/SarifReporter; 14 | public fun getId ()Ljava/lang/String; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-sarif/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("ktlint-publication-library") 3 | } 4 | 5 | dependencies { 6 | implementation(projects.ktlintCliReporterCore) 7 | implementation(libs.sarif4k) 8 | 9 | testImplementation(projects.ktlintTest) 10 | 11 | testImplementation(libs.junit5.jupiter) 12 | // Since Gradle 8 the platform launcher needs explicitly be defined as runtime dependency to avoid classpath problems 13 | // https://docs.gradle.org/8.12/userguide/upgrading_version_8.html#test_framework_implementation_dependencies 14 | testRuntimeOnly(libs.junit5.platform.launcher) 15 | } 16 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-sarif/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=ktlint-cli-reporter-sarif 2 | POM_ARTIFACT_ID=ktlint-cli-reporter-sarif 3 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-sarif/src/main/kotlin/com/pinterest/ktlint/cli/reporter/sarif/SarifReporterProvider.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.cli.reporter.sarif 2 | 3 | import com.pinterest.ktlint.cli.reporter.core.api.ReporterProviderV2 4 | import java.io.PrintStream 5 | 6 | public class SarifReporterProvider : ReporterProviderV2 { 7 | override val id: String = "sarif" 8 | 9 | override fun get( 10 | out: PrintStream, 11 | opt: Map, 12 | ): SarifReporter = SarifReporter(out) 13 | } 14 | -------------------------------------------------------------------------------- /ktlint-cli-reporter-sarif/src/main/resources/META-INF/services/com.pinterest.ktlint.cli.reporter.core.api.ReporterProviderV2: -------------------------------------------------------------------------------- 1 | com.pinterest.ktlint.cli.reporter.sarif.SarifReporterProvider 2 | -------------------------------------------------------------------------------- /ktlint-cli-ruleset-core/api/ktlint-cli-ruleset-core.api: -------------------------------------------------------------------------------- 1 | public abstract class com/pinterest/ktlint/cli/ruleset/core/api/RuleSetProviderV3 : java/io/Serializable { 2 | public fun (Lcom/pinterest/ktlint/rule/engine/core/api/RuleSetId;)V 3 | public final fun getId ()Lcom/pinterest/ktlint/rule/engine/core/api/RuleSetId; 4 | public abstract fun getRuleProviders ()Ljava/util/Set; 5 | } 6 | 7 | -------------------------------------------------------------------------------- /ktlint-cli-ruleset-core/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("ktlint-publication-library") 3 | } 4 | 5 | dependencies { 6 | api(projects.ktlintRuleEngineCore) 7 | } 8 | -------------------------------------------------------------------------------- /ktlint-cli-ruleset-core/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=ktlint-cli-ruleset-core 2 | POM_ARTIFACT_ID=ktlint-cli-ruleset-core 3 | -------------------------------------------------------------------------------- /ktlint-cli-ruleset-core/src/main/kotlin/com/pinterest/ktlint/cli/ruleset/core/api/RuleSetProviderV3.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.cli.ruleset.core.api 2 | 3 | import com.pinterest.ktlint.rule.engine.core.api.RuleProvider 4 | import com.pinterest.ktlint.rule.engine.core.api.RuleSetId 5 | import java.io.Serializable 6 | 7 | /** 8 | * KtLint uses [ServiceLoader](https://docs.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html) to 9 | * discover all available [RuleSetProviderV3]`s on the classpath and so each [RuleSetProviderV3] must be registered using 10 | * `META-INF/services/com.pinterest.ktlint.cli.ruleset.core.api.RuleSetProviderV3` (see `ktlint-ruleset-standard/src/main/resources` 11 | * for an example). 12 | */ 13 | public abstract class RuleSetProviderV3( 14 | public val id: RuleSetId, 15 | ) : Serializable { 16 | /** 17 | * Gets a group of related [RuleProvider]s. A provided rule is not guaranteed to be run as rules can be disabled, 18 | * for example via ".editorconfig" properties. 19 | * 20 | * Intended usage: 21 | * ``` 22 | * public class CustomRuleSetProvider : 23 | * RuleSetProviderV3(RuleSetId("custom")) { 24 | * override fun getRuleProviders(): Set = 25 | * setOf( 26 | * RuleProvider { CustomRule1() }, 27 | * RuleProvider { CustomRule2() } 28 | * ) 29 | * } 30 | * ``` 31 | */ 32 | public abstract fun getRuleProviders(): Set 33 | } 34 | -------------------------------------------------------------------------------- /ktlint-cli/api/ktlint-cli.api: -------------------------------------------------------------------------------- 1 | public final class com/pinterest/ktlint/Main { 2 | public static final fun main ([Ljava/lang/String;)V 3 | } 4 | 5 | -------------------------------------------------------------------------------- /ktlint-cli/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=ktlint-cli 2 | POM_ARTIFACT_ID=ktlint-cli 3 | -------------------------------------------------------------------------------- /ktlint-cli/src/main/kotlin/com/pinterest/ktlint/Main.kt: -------------------------------------------------------------------------------- 1 | @file:JvmName("Main") 2 | 3 | package com.pinterest.ktlint 4 | 5 | import com.github.ajalt.clikt.core.main 6 | import com.github.ajalt.clikt.core.subcommands 7 | import com.pinterest.ktlint.cli.internal.GenerateEditorConfigSubCommand 8 | import com.pinterest.ktlint.cli.internal.GitPreCommitHookSubCommand 9 | import com.pinterest.ktlint.cli.internal.GitPrePushHookSubCommand 10 | import com.pinterest.ktlint.cli.internal.KtlintCommandLine 11 | 12 | // Ideally this file would have been moved to the cli package as well. This however is breaking change that is likely to affect each project 13 | // that use either the Maven or Gradle and calls the Ktlint CLI. As those users likely will not read the changelog, this could lead to many 14 | // issues. So the class is to be kept at the old location. 15 | public fun main(args: Array) { 16 | KtlintCommandLine() 17 | .subcommands( 18 | GenerateEditorConfigSubCommand(), 19 | GitPreCommitHookSubCommand(), 20 | GitPrePushHookSubCommand(), 21 | ).main(args) 22 | } 23 | -------------------------------------------------------------------------------- /ktlint-cli/src/main/kotlin/com/pinterest/ktlint/cli/internal/GitPreCommitHookSubCommand.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.cli.internal 2 | 3 | internal class GitPreCommitHookSubCommand : 4 | GitHookCliktCommand( 5 | name = "installGitPreCommitHook", 6 | helpText = "Install git hook to automatically check files for style violations on commit", 7 | ) { 8 | override fun run() { 9 | installGitHook(gitHookName = "pre-commit") { 10 | """ 11 | #!/bin/sh 12 | 13 | # pre-commit hook 14 | 15 | git diff --name-only -z --cached --relative -- '*.kt' '*.kts' | ktlint --relative --patterns-from-stdin='' 16 | """.trimIndent().toByteArray() 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ktlint-cli/src/main/kotlin/com/pinterest/ktlint/cli/internal/GitPrePushHookSubCommand.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.cli.internal 2 | 3 | internal class GitPrePushHookSubCommand : 4 | GitHookCliktCommand( 5 | name = "installGitPrePushHook", 6 | helpText = "Install git hook to automatically check files for style violations before push", 7 | ) { 8 | override fun run() { 9 | installGitHook(gitHookName = "pre-push") { 10 | """ 11 | #!/bin/sh 12 | 13 | # pre-push hook 14 | 15 | git diff --name-only -z HEAD "origin/${'$'}(git rev-parse --abbrev-ref HEAD)" -- '*.kt' '*.kts' | ktlint --relative --patterns-from-stdin='' 16 | """.trimIndent().toByteArray() 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ktlint-cli/src/main/kotlin/com/pinterest/ktlint/cli/internal/KtlintVersionProvider.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.cli.internal 2 | 3 | import com.pinterest.ktlint.cli.reporter.core.api.ktlintVersion 4 | 5 | /** 6 | * Dynamically provides current ktlint version 7 | */ 8 | internal class KtlintVersionProvider { 9 | val version: String = 10 | ktlintVersion(KtlintVersionProvider::class.java) 11 | ?: error("Failed to determine ktlint version") 12 | } 13 | -------------------------------------------------------------------------------- /ktlint-cli/src/main/kotlin/com/pinterest/ktlint/cli/internal/LoadReporterProviders.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.cli.internal 2 | 3 | import com.pinterest.ktlint.cli.internal.CustomJarProviderCheck.ERROR_WHEN_REQUIRED_PROVIDER_IS_MISSING 4 | import com.pinterest.ktlint.cli.reporter.core.api.ReporterProviderV2 5 | import java.net.URL 6 | 7 | internal fun loadReporters(urls: List): Set> { 8 | // Code below is kept around as reference for future deprecation of current ReporterProvider 9 | // An error about finding a deprecated ReporterProvider is more important than reporting an error about a missing ReporterProviderV2 10 | // ReporterProvider::class.java.loadFromJarFiles(urls, providerId = { it.id }, ERROR_WHEN_DEPRECATED_PROVIDER_IS_FOUND) 11 | 12 | return ReporterProviderV2::class.java.loadFromJarFiles(urls, providerId = { it.id }, ERROR_WHEN_REQUIRED_PROVIDER_IS_MISSING) 13 | } 14 | -------------------------------------------------------------------------------- /ktlint-cli/src/main/kotlin/com/pinterest/ktlint/cli/internal/LoadRuleProviders.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.cli.internal 2 | 3 | import com.pinterest.ktlint.cli.internal.CustomJarProviderCheck.ERROR_WHEN_REQUIRED_PROVIDER_IS_MISSING 4 | import com.pinterest.ktlint.cli.ruleset.core.api.RuleSetProviderV3 5 | import com.pinterest.ktlint.rule.engine.core.api.RuleProvider 6 | import java.net.URL 7 | 8 | /** 9 | * Loads given list of paths to jar files. For files containing a [RuleSetProviderV3] class, get all [RuleProvider]s. 10 | */ 11 | internal fun loadRuleProviders(urls: List): Set { 12 | // Keep code around as example for future deprecation of current RuleSetProvider 13 | // An error about finding a deprecated RuleSetProviderV2 is more important than reporting an error about a missing RuleSetProviderV3 14 | // RuleSetProviderV2::class.java.loadFromJarFiles(urls, providerId = { it.id }, ERROR_WHEN_DEPRECATED_PROVIDER_IS_FOUND) 15 | 16 | return RuleSetProviderV3::class.java 17 | .loadFromJarFiles(urls, providerId = { it.id.value }, ERROR_WHEN_REQUIRED_PROVIDER_IS_MISSING) 18 | .flatMap { it.getRuleProviders() } 19 | .toSet() 20 | } 21 | -------------------------------------------------------------------------------- /ktlint-cli/src/main/scripts/ktlint.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | 4 | REM By default this batch file and the "ktlint" are located in same directory. Note that "ktlint" is a JAR 5 | REM despite it is not being suffixed with extension ".jar". Please adjust path below when this batch file 6 | REM and the jar file are located in different directories. 7 | if not defined JAR_PATH set JAR_PATH=%~dp0ktlint 8 | 9 | REM The --add-opens argument is needed for java 16+ (see https://github.com/pinterest/ktlint/issues/1986) 10 | java --add-opens=java.base/java.lang=ALL-UNNAMED -jar "%JAR_PATH%" %* 11 | -------------------------------------------------------------------------------- /ktlint-cli/src/test/kotlin/com/pinterest/ktlint/cli/environment/OsEnvironment.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.cli.environment 2 | 3 | import java.util.TreeMap 4 | 5 | /** 6 | * An immutable OS environment, with support for Windows-specific 7 | * case-insensitive keys (i.e. `OsEnvironment()["PATH"]` and 8 | * `OsEnvironment()["Path"]` will return the same non-`null` value on Windows). 9 | */ 10 | internal class OsEnvironment private constructor( 11 | initial: Map, 12 | environment: MutableMap, 13 | ) : Map by environment { 14 | /** 15 | * Creates a new OS environment, defaulting to the values returned by 16 | * [System.getenv]. 17 | * 18 | * @see System.getenv 19 | */ 20 | constructor(initial: Map = System.getenv()) : this( 21 | initial, 22 | environment = emptyEnvironment(), 23 | ) 24 | 25 | init { 26 | environment.putAll(initial) 27 | } 28 | 29 | companion object { 30 | private fun isWindows(): Boolean = 31 | System 32 | .getProperty("os.name") 33 | .startsWith("Windows") 34 | 35 | private fun emptyEnvironment(): MutableMap = 36 | when { 37 | isWindows() -> CaseInsensitiveMap() 38 | else -> mutableMapOf() 39 | } 40 | } 41 | } 42 | 43 | private class CaseInsensitiveMap : 44 | TreeMap(java.lang.String.CASE_INSENSITIVE_ORDER), 45 | MutableMap 46 | -------------------------------------------------------------------------------- /ktlint-cli/src/test/resources/cli/.editorconfig: -------------------------------------------------------------------------------- 1 | # Files in this directory and it subdirectories contain files with lint violations which are needed for the cli tests. 2 | # Those file should not be changed when ktlint is ran on its own source code as that would result in breaking tests. 3 | root = true 4 | 5 | [*.{kt,kts}] 6 | ktlint_standard = disabled 7 | ktlint_experimental = disabled 8 | -------------------------------------------------------------------------------- /ktlint-cli/src/test/resources/cli/baseline/TestBaselineExtraErrorFile.kt.test: -------------------------------------------------------------------------------- 1 | /* this should be an EOL comment */ 2 | class TestBaselineExtraErrorFile {} 3 | -------------------------------------------------------------------------------- /ktlint-cli/src/test/resources/cli/baseline/TestBaselineFile.kt.test: -------------------------------------------------------------------------------- 1 | class TestBaselineFile {} 2 | -------------------------------------------------------------------------------- /ktlint-cli/src/test/resources/cli/baseline/config/test-baseline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /ktlint-cli/src/test/resources/cli/baseline/readme.md: -------------------------------------------------------------------------------- 1 | Note that following is configured on purpose: 2 | * Files `test-baseline.xml` and `config/test-baseline.xml` are identical. 3 | * The references to the rules do not include the rule set id `standard:`. 4 | * All test files contain an empty function body which is registered in the baseline file. 5 | * The `TextBaselineExtraErrorFile` contains an additional error (a single line block comment). 6 | -------------------------------------------------------------------------------- /ktlint-cli/src/test/resources/cli/baseline/some/path/to/TestBaselineExtraErrorFile.kt.test: -------------------------------------------------------------------------------- 1 | /* this should be an EOL comment */ 2 | class TestBaselineExtraErrorFile {} 3 | -------------------------------------------------------------------------------- /ktlint-cli/src/test/resources/cli/baseline/some/path/to/TestBaselineFile.kt.test: -------------------------------------------------------------------------------- 1 | class TestBaselineFile {} 2 | -------------------------------------------------------------------------------- /ktlint-cli/src/test/resources/cli/baseline/test-baseline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /ktlint-cli/src/test/resources/cli/custom-ruleset/rule-set-provider-v2/Main.kt.test: -------------------------------------------------------------------------------- 1 | var helloWorld = "Hello World!" 2 | 3 | fun main() { 4 | println(helloWorld) 5 | } 6 | -------------------------------------------------------------------------------- /ktlint-cli/src/test/resources/cli/custom-ruleset/rule-set-provider-v2/ktlint-cli-reporter-html.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinterest/ktlint/2bec8b42ce4f130b839f2de574ec2cc767f779fa/ktlint-cli/src/test/resources/cli/custom-ruleset/rule-set-provider-v2/ktlint-cli-reporter-html.jar -------------------------------------------------------------------------------- /ktlint-cli/src/test/resources/cli/custom-ruleset/rule-set-provider-v2/ktlint-test-ruleset-provider-v2-deprecated.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinterest/ktlint/2bec8b42ce4f130b839f2de574ec2cc767f779fa/ktlint-cli/src/test/resources/cli/custom-ruleset/rule-set-provider-v2/ktlint-test-ruleset-provider-v2-deprecated.jar -------------------------------------------------------------------------------- /ktlint-cli/src/test/resources/cli/custom-ruleset/rule-set-provider-v2/readme.md: -------------------------------------------------------------------------------- 1 | This directory contains ruleset jar files. The files are copied from the build directories of the ktlint project (version identifiers have been removed from the names). It is not necessary to use the most recent ktlint version but, it must be at least KtLint version 0.47.x 2 | -------------------------------------------------------------------------------- /ktlint-cli/src/test/resources/cli/editorconfig-path/project/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [Foo*.{kt,kts}.test] 4 | max_line_length = 30 5 | 6 | [src/**/example/*.kt.test] 7 | ktlint_standard_no-wildcard-imports = disabled 8 | -------------------------------------------------------------------------------- /ktlint-cli/src/test/resources/cli/editorconfig-path/project/.editorconfig-bar: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [Bar*.{kt,kts}.test] 4 | max_line_length = 20 5 | -------------------------------------------------------------------------------- /ktlint-cli/src/test/resources/cli/editorconfig-path/project/.editorconfig-default-max-line-length-on-tests-only: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*Test.{kt,kts}.test] 4 | max_line_length = 25 5 | -------------------------------------------------------------------------------- /ktlint-cli/src/test/resources/cli/editorconfig-path/project/.editorconfig-disable-no-wildcard-imports-rule: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [**/*-example/*.{kt,kts}.test] 4 | ktlint_standard_no-wildcard-imports = disabled 5 | -------------------------------------------------------------------------------- /ktlint-cli/src/test/resources/cli/editorconfig-path/project/editorconfig-alternative: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [Bar*.{kt,kts}.test] 4 | max_line_length = 20 5 | -------------------------------------------------------------------------------- /ktlint-cli/src/test/resources/cli/editorconfig-path/project/editorconfig-boolean-setting: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.{kt,kts}.test] 4 | ij_kotlin_allow_trailing_comma=true 5 | ij_kotlin_allow_trailing_comma_on_call_site=true 6 | -------------------------------------------------------------------------------- /ktlint-cli/src/test/resources/cli/editorconfig-path/project/src/main/kotlin/example/Bar.kts.test: -------------------------------------------------------------------------------- 1 | val bar = "barbarbarbarbarbarbarbarbarbarbarbarbar" 2 | -------------------------------------------------------------------------------- /ktlint-cli/src/test/resources/cli/editorconfig-path/project/src/main/kotlin/example/Foo.kt.test: -------------------------------------------------------------------------------- 1 | val foo = "fooooooooooooooooooooooooooooooooooooooooo" 2 | -------------------------------------------------------------------------------- /ktlint-cli/src/test/resources/cli/editorconfig-path/project/src/main/kotlin/example/Wildard1.kt.test: -------------------------------------------------------------------------------- 1 | import foo.bar.* 2 | 3 | fun wildcard1() {} 4 | -------------------------------------------------------------------------------- /ktlint-cli/src/test/resources/cli/editorconfig-path/project/src/main/kotlin/filename-example/Wildcard2.kt.test: -------------------------------------------------------------------------------- 1 | import foo.bar.* 2 | 3 | fun wildcard2() {} 4 | -------------------------------------------------------------------------------- /ktlint-cli/src/test/resources/cli/editorconfig-path/project/src/test/kotlin/example/BarTest.kts.test: -------------------------------------------------------------------------------- 1 | val barTest = "barbarbarbarbarbarbarbarbarbarbarbarbar" 2 | -------------------------------------------------------------------------------- /ktlint-cli/src/test/resources/cli/editorconfig-path/project/src/test/kotlin/example/FooTest.kt.test: -------------------------------------------------------------------------------- 1 | val fooTest = "fooooooooooooooooooooooooooooooooooooooooo" 2 | -------------------------------------------------------------------------------- /ktlint-cli/src/test/resources/cli/no-code-style-error/Main.kt: -------------------------------------------------------------------------------- 1 | fun main() { 2 | println("Hello world!") 3 | } 4 | -------------------------------------------------------------------------------- /ktlint-cli/src/test/resources/cli/readme.md: -------------------------------------------------------------------------------- 1 | Most of the kotlin files in this directory contain lint violations which are required for testing. Files that do contain lint errors have extension ".test". This additional extension prevents that the file is being picked up by ktlint when running it on the project itself. Fixing the lint violations in those files results in failing tests in the next test run. 2 | 3 | File "no-code-style-error/Main.kt" does not contain a lint violation and its file name does not end with ".test" as the files needs to be picked up by a test which tests the default kotlin extension. 4 | -------------------------------------------------------------------------------- /ktlint-cli/src/test/resources/cli/too-many-empty-lines/Main.kt.test: -------------------------------------------------------------------------------- 1 | fun main() { 2 | 3 | 4 | 5 | println("Hello world!") 6 | } 7 | -------------------------------------------------------------------------------- /ktlint-logger/api/ktlint-logger.api: -------------------------------------------------------------------------------- 1 | public final class com/pinterest/ktlint/logger/api/KtLintKLoggerInitializerKt { 2 | public static final fun initKtLintKLogger (Lio/github/oshai/kotlinlogging/KLogger;)Lio/github/oshai/kotlinlogging/KLogger; 3 | public static final fun setDefaultLoggerModifier (Lio/github/oshai/kotlinlogging/KLogger;Lkotlin/jvm/functions/Function1;)Lio/github/oshai/kotlinlogging/KLogger; 4 | } 5 | 6 | -------------------------------------------------------------------------------- /ktlint-logger/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("ktlint-publication-library") 3 | } 4 | 5 | dependencies { 6 | api(libs.logging) 7 | } 8 | -------------------------------------------------------------------------------- /ktlint-logger/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=ktlint-logger 2 | POM_ARTIFACT_ID=ktlint-logger 3 | -------------------------------------------------------------------------------- /ktlint-logger/src/main/kotlin/com/pinterest/ktlint/logger/api/KtLintKLoggerInitializer.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.logger.api 2 | 3 | import io.github.oshai.kotlinlogging.KLogger 4 | 5 | /** 6 | * Default modifier for the KLogger. It can be set only once via [setDefaultLoggerModifier] but it should be set before the first invocation 7 | * of [initKtLintKLogger]. 8 | */ 9 | private var defaultLoggerModifier: ((KLogger) -> Unit)? = null 10 | 11 | /** 12 | * Set the [defaultLoggerModifier]. Note that it can only be set once. It should be set before the first invocation to [initKtLintKLogger]. 13 | * Also note that it depends on the actual logging framework what capabilities can be set at runtime. The Ktlint CLI uses 14 | * 'ch.qos.logback:logback-classic' as it allows the log level to be changed at run time. See the 'ktlint-api-consumer' module for an 15 | * example that uses 'org.slf4j:slf4j-simple' that is configured via a properties file. 16 | */ 17 | public fun KLogger.setDefaultLoggerModifier(loggerModifier: (KLogger) -> Unit): KLogger { 18 | if (defaultLoggerModifier != null) { 19 | warn { 20 | """ 21 | The defaultLoggerModifier has been set before and might already have been applied when initializing 22 | Loggers. Loggers which will be initialized after resetting the defaultLoggerModifier will be initialized 23 | with the new value. This might result in unpredictable behavior. Except for in unit tests, it is 24 | recommended to ensure to call this function only once. 25 | """.trimIndent() 26 | } 27 | } 28 | defaultLoggerModifier = loggerModifier 29 | return this 30 | } 31 | 32 | /** 33 | * Initializes the logger with the [defaultLoggerModifier]. 34 | */ 35 | public fun KLogger.initKtLintKLogger(): KLogger { 36 | if (defaultLoggerModifier == null) { 37 | // Initialize the defaultLoggerModifier on the first invocation of initKtlintLogger when it is not yet set. 38 | // In this way it can be ensured that all loggers are initialized with the exact same logger modifier. 39 | defaultLoggerModifier = { _ -> } 40 | } 41 | 42 | return apply { defaultLoggerModifier?.invoke(this) } 43 | } 44 | -------------------------------------------------------------------------------- /ktlint-rule-engine-core/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("ktlint-publication-library") 3 | } 4 | 5 | dependencies { 6 | implementation(projects.ktlintLogger) 7 | 8 | api(libs.kotlin.compiler) 9 | api(libs.ec4j) 10 | 11 | testImplementation(projects.ktlintTest) 12 | testImplementation(projects.ktlintRuleEngine) 13 | testRuntimeOnly(libs.slf4j) 14 | 15 | testImplementation(libs.junit5.jupiter) 16 | // Since Gradle 8 the platform launcher needs explicitly be defined as runtime dependency to avoid classpath problems 17 | // https://docs.gradle.org/8.12/userguide/upgrading_version_8.html#test_framework_implementation_dependencies 18 | testRuntimeOnly(libs.junit5.platform.launcher) 19 | } 20 | -------------------------------------------------------------------------------- /ktlint-rule-engine-core/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=ktlint-rule-engine-core 2 | POM_ARTIFACT_ID=ktlint-rule-engine-core 3 | -------------------------------------------------------------------------------- /ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/AutocorrectDecision.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.rule.engine.core.api 2 | 3 | import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision.ALLOW_AUTOCORRECT 4 | 5 | public enum class AutocorrectDecision { 6 | /** 7 | * Autocorrect lint violation if supported by the [Rule]. 8 | */ 9 | ALLOW_AUTOCORRECT, 10 | 11 | /** 12 | * Do not autocorrect lint violation even when this is supported by the [Rule]. 13 | */ 14 | NO_AUTOCORRECT, 15 | } 16 | 17 | public inline fun AutocorrectDecision.ifAutocorrectAllowed(function: () -> T): T? = 18 | takeIf { this == ALLOW_AUTOCORRECT } 19 | ?.let { function() } 20 | -------------------------------------------------------------------------------- /ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/IgnoreKtlintSuppressions.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.rule.engine.core.api 2 | 3 | /** 4 | * Marker interface to indicate that this [Rule] should ignore any rule suppression hints. This class is not intended to be used by Custom 5 | * RuleSetProviders. No backward compatibility is guaranteed. 6 | */ 7 | public interface IgnoreKtlintSuppressions 8 | -------------------------------------------------------------------------------- /ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/OptInFeatures.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.rule.engine.core.api 2 | 3 | @RequiresOptIn( 4 | message = "This Ktlint feature is highly experimental, and most probably will change in the future releases.", 5 | level = RequiresOptIn.Level.ERROR, 6 | ) 7 | @Retention(AnnotationRetention.BINARY) 8 | @Target( 9 | AnnotationTarget.CLASS, 10 | AnnotationTarget.FUNCTION, 11 | AnnotationTarget.TYPEALIAS, 12 | AnnotationTarget.PROPERTY, 13 | ) 14 | public annotation class FeatureInAlphaState 15 | 16 | @RequiresOptIn( 17 | message = "This Ktlint feature is experimental, and may change in the future releases.", 18 | ) 19 | @Retention(AnnotationRetention.BINARY) 20 | @Target( 21 | AnnotationTarget.CLASS, 22 | AnnotationTarget.FUNCTION, 23 | AnnotationTarget.TYPEALIAS, 24 | ) 25 | public annotation class FeatureInBetaState 26 | -------------------------------------------------------------------------------- /ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/SinceKtlint.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.rule.engine.core.api 2 | 3 | /** 4 | * The version in which the rule was introduced. 5 | */ 6 | @Repeatable 7 | @Target( 8 | AnnotationTarget.CLASS, 9 | AnnotationTarget.PROPERTY, 10 | AnnotationTarget.FIELD, 11 | AnnotationTarget.CONSTRUCTOR, 12 | AnnotationTarget.FUNCTION, 13 | AnnotationTarget.PROPERTY_GETTER, 14 | AnnotationTarget.PROPERTY_SETTER, 15 | AnnotationTarget.TYPEALIAS, 16 | ) 17 | @Retention(AnnotationRetention.BINARY) 18 | @MustBeDocumented 19 | public annotation class SinceKtlint( 20 | val version: String, 21 | val status: Status, 22 | ) { 23 | public enum class Status { STABLE, EXPERIMENTAL } 24 | } 25 | -------------------------------------------------------------------------------- /ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/TokenSets.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.rule.engine.core.api 2 | 3 | import com.pinterest.ktlint.rule.engine.core.api.ElementType.DO_KEYWORD 4 | import com.pinterest.ktlint.rule.engine.core.api.ElementType.FOR_KEYWORD 5 | import com.pinterest.ktlint.rule.engine.core.api.ElementType.IF_KEYWORD 6 | import com.pinterest.ktlint.rule.engine.core.api.ElementType.OBJECT_KEYWORD 7 | import com.pinterest.ktlint.rule.engine.core.api.ElementType.TRY_KEYWORD 8 | import com.pinterest.ktlint.rule.engine.core.api.ElementType.WHEN_KEYWORD 9 | import com.pinterest.ktlint.rule.engine.core.api.ElementType.WHILE_KEYWORD 10 | import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet 11 | import org.jetbrains.kotlin.lexer.KtTokens 12 | 13 | public object TokenSets { 14 | public val COMMENTS: TokenSet = KtTokens.COMMENTS 15 | 16 | /** 17 | * 18 | * Reference: This is a subset of [KotlinExpressionParsing.EXPRESSION_FIRST] 19 | */ 20 | public val CONTROL_FLOW_KEYWORDS: TokenSet = 21 | TokenSet.create( 22 | IF_KEYWORD, // if 23 | WHEN_KEYWORD, // when 24 | TRY_KEYWORD, // try 25 | OBJECT_KEYWORD, // object 26 | // loop 27 | FOR_KEYWORD, 28 | WHILE_KEYWORD, 29 | DO_KEYWORD, 30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/editorconfig/CommaSeparatedListValueParser.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.rule.engine.core.api.editorconfig 2 | 3 | import org.ec4j.core.model.PropertyType 4 | import org.ec4j.core.model.PropertyType.PropertyValueParser 5 | 6 | /** 7 | * A [PropertyValueParser] implementation that allows a comma separate list of strings. 8 | */ 9 | public class CommaSeparatedListValueParser : PropertyValueParser> { 10 | override fun parse( 11 | name: String?, 12 | value: String?, 13 | ): PropertyType.PropertyValue> = 14 | if (value == "unset") { 15 | PropertyType.PropertyValue.valid(value, emptySet()) 16 | } else { 17 | PropertyType.PropertyValue.valid( 18 | value, 19 | value 20 | .orEmpty() 21 | .split(",") 22 | .map { it.trim() } 23 | .toSet(), 24 | ) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/editorconfig/EndOfLineProperty.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.rule.engine.core.api.editorconfig 2 | 3 | import org.ec4j.core.model.PropertyType 4 | 5 | public val END_OF_LINE_PROPERTY: EditorConfigProperty = 6 | EditorConfigProperty( 7 | name = PropertyType.end_of_line.name, 8 | type = PropertyType.end_of_line, 9 | defaultValue = PropertyType.EndOfLineValue.lf, 10 | ) 11 | -------------------------------------------------------------------------------- /ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/editorconfig/IndentSizeEditorConfigProperty.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.rule.engine.core.api.editorconfig 2 | 3 | import org.ec4j.core.model.PropertyType 4 | 5 | private const val DEFAULT_INDENT_SIZE = 4 6 | 7 | public val INDENT_SIZE_PROPERTY: EditorConfigProperty = 8 | EditorConfigProperty( 9 | name = PropertyType.indent_size.name, 10 | type = PropertyType.indent_size, 11 | defaultValue = DEFAULT_INDENT_SIZE, 12 | propertyMapper = { property, _ -> 13 | if (property?.isUnset == true) { 14 | -1 15 | } else { 16 | property 17 | ?.getValueAs() 18 | .let { 19 | if (it == null || it <= 0) { 20 | DEFAULT_INDENT_SIZE 21 | } else { 22 | it 23 | } 24 | } 25 | } 26 | }, 27 | ) 28 | -------------------------------------------------------------------------------- /ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/editorconfig/IndentStyleEditorConfigProperty.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.rule.engine.core.api.editorconfig 2 | 3 | import org.ec4j.core.model.PropertyType 4 | 5 | public val INDENT_STYLE_PROPERTY: EditorConfigProperty = 6 | EditorConfigProperty( 7 | name = PropertyType.indent_style.name, 8 | type = PropertyType.indent_style, 9 | defaultValue = PropertyType.IndentStyleValue.space, 10 | ) 11 | -------------------------------------------------------------------------------- /ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/editorconfig/InsertFinalNewLineEditorConfigProperty.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.rule.engine.core.api.editorconfig 2 | 3 | import org.ec4j.core.model.PropertyType 4 | 5 | public val INSERT_FINAL_NEWLINE_PROPERTY: EditorConfigProperty = 6 | EditorConfigProperty( 7 | name = PropertyType.insert_final_newline.name, 8 | type = PropertyType.insert_final_newline, 9 | defaultValue = true, 10 | ) 11 | -------------------------------------------------------------------------------- /ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/editorconfig/SafeEnumValueParser.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.rule.engine.core.api.editorconfig 2 | 3 | import org.ec4j.core.model.PropertyType 4 | import org.ec4j.core.model.PropertyType.PropertyValueParser 5 | import java.util.Locale 6 | 7 | /** 8 | * A [PropertyValueParser] implementation that allows only members of a given [Enum] type. This class is almost identical to the original 9 | * [EnumValueParser] provided by ec4j. Difference is that values are trimmed before trying to match the enum values. 10 | * 11 | * As the ec4j project has not provided any new release since version 1.0 (2019-08-01) a custom implementation has been added. 12 | * 13 | * @param the type of the value 14 | * 15 | */ 16 | public class SafeEnumValueParser>( 17 | private val enumType: Class, 18 | ) : PropertyValueParser { 19 | override fun parse( 20 | name: String?, 21 | value: String?, 22 | ): PropertyType.PropertyValue = 23 | if (value == null) { 24 | PropertyType.PropertyValue.invalid(null, "Cannot make enum ${enumType.name} out of null") 25 | } else { 26 | try { 27 | PropertyType.PropertyValue.valid( 28 | value, 29 | java.lang.Enum.valueOf( 30 | enumType, 31 | value 32 | // In case an EOL comment (separated with a space) is appended after the value then the comment 33 | // itself is removed but not the space. This results in the enum value not being parsed 34 | // correctly. 35 | .trim() 36 | .lowercase(Locale.getDefault()), 37 | ) as T, 38 | ) 39 | } catch (e: IllegalArgumentException) { 40 | PropertyType.PropertyValue.invalid( 41 | value, 42 | "Unexpected parsed \"" + value + "\" for enum " + enumType.name, 43 | ) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/editorconfig/ec4j/EditorConfigProperty.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.rule.engine.core.api.editorconfig.ec4j 2 | 3 | import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfigProperty 4 | import org.ec4j.core.model.Property 5 | import org.ec4j.core.model.PropertyType 6 | 7 | /** 8 | * Creates an [org.ec4j.core.model.Property.Builder] for given [value]. 9 | */ 10 | public fun EditorConfigProperty.toPropertyBuilderWithValue(value: String): Property.Builder = 11 | Property 12 | .builder() 13 | .type(type) 14 | .name(name) 15 | .value(value) 16 | 17 | /** 18 | * Creates an [org.ec4j.core.model.Property] for given [value]. 19 | */ 20 | public fun EditorConfigProperty.toPropertyWithValue(value: String): Property = toPropertyBuilderWithValue(value).build() 21 | 22 | /** 23 | * Creates an [org.ec4j.core.model.Property.Builder] for given [value]. 24 | */ 25 | public fun EditorConfigProperty.toPropertyBuilderWithValue(value: PropertyType.PropertyValue<*>): Property.Builder = 26 | Property 27 | .builder() 28 | .type(type) 29 | .name(name) 30 | .value(value) 31 | 32 | /** 33 | * Creates an [org.ec4j.core.model.Property] for given [value]. 34 | */ 35 | public fun EditorConfigProperty.toPropertyWithValue(value: PropertyType.PropertyValue<*>): Property = 36 | toPropertyBuilderWithValue(value).build() 37 | -------------------------------------------------------------------------------- /ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/internal/IdNamingPolicy.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.rule.engine.core.internal 2 | 3 | /** 4 | * Provides policy to have consistent and restricted `id` field naming style. 5 | */ 6 | internal object IdNamingPolicy { 7 | private const val SIMPLE_ID_REGEX = "[a-z]+(-[a-z]+)*" 8 | private val RULE_ID_REGEX = "$SIMPLE_ID_REGEX:$SIMPLE_ID_REGEX".toRegex() 9 | private val RULE_SET_ID_REGEX = SIMPLE_ID_REGEX.toRegex() 10 | 11 | /** 12 | * Checks provided [ruleId] is valid. 13 | * 14 | * Will throw [IllegalArgumentException] on invalid [ruleId] name. 15 | */ 16 | internal fun enforceRuleIdNaming(ruleId: String) = 17 | require(isValidRuleId(ruleId)) { "Rule with id '$ruleId' must match regexp '${RULE_ID_REGEX.pattern}'" } 18 | 19 | internal fun isValidRuleId(ruleId: String) = ruleId.matches(RULE_ID_REGEX) 20 | 21 | /** 22 | * Checks provided [ruleSetId] is valid. 23 | * 24 | * Will throw [IllegalArgumentException] on invalid [ruleSetId] name. 25 | */ 26 | internal fun enforceRuleSetIdNaming(ruleSetId: String) = 27 | require(isValidRuleSetId(ruleSetId)) { "Rule set id '$ruleSetId' must match '${RULE_SET_ID_REGEX.pattern}'" } 28 | 29 | internal fun isValidRuleSetId(ruleSetId: String) = ruleSetId.matches(RULE_SET_ID_REGEX) 30 | } 31 | -------------------------------------------------------------------------------- /ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/util/Any.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.rule.engine.core.util 2 | 3 | /** 4 | * Safe cast, copied from 5 | * https://github.com/JetBrains/kotlin/blob/d3200b2c65b829b85244c4ec4cb19f6e479b06ba/core/util.runtime/src/org/jetbrains/kotlin/utils/addToStdlib.kt#L108 6 | */ 7 | public inline fun Any?.safeAs(): T? = this as? T 8 | 9 | /** 10 | * Unsafe cast, copied from 11 | * https://github.com/JetBrains/kotlin/blob/d3200b2c65b829b85244c4ec4cb19f6e479b06ba/core/util.runtime/src/org/jetbrains/kotlin/utils/addToStdlib.kt#L111 12 | */ 13 | public inline fun Any?.cast(): T = this as T 14 | -------------------------------------------------------------------------------- /ktlint-rule-engine-core/src/test/kotlin/com/pinterest/ktlint/rule/engine/core/api/RuleKtTest.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.rule.engine.core.api 2 | 3 | import org.assertj.core.api.Assertions.assertThat 4 | import org.assertj.core.api.Assertions.assertThatThrownBy 5 | import org.junit.jupiter.api.Test 6 | import org.junit.jupiter.params.ParameterizedTest 7 | import org.junit.jupiter.params.provider.CsvSource 8 | import org.junit.jupiter.params.provider.ValueSource 9 | 10 | class RuleKtTest { 11 | @Test 12 | fun `Given a rule with an unqualified rule id than the rule can not be instantiated`() { 13 | assertThatThrownBy { creatRule("some-unqualified-rule-id") } 14 | .isInstanceOf(IllegalArgumentException::class.java) 15 | .hasMessage("Rule with id 'some-unqualified-rule-id' must match regexp '[a-z]+(-[a-z]+)*:[a-z]+(-[a-z]+)*'") 16 | } 17 | 18 | @ParameterizedTest(name = "Qualified rule id: `{0}`, expected rule id: `{1}`") 19 | @ValueSource( 20 | strings = [ 21 | "standard:rule-id", 22 | "custom:rule-id", 23 | ], 24 | ) 25 | fun `Given a rule with a qualified rule id then return the rule id`(id: String) { 26 | val rule = creatRule(id) 27 | assertThat(rule.ruleId.value).isEqualTo(id) 28 | } 29 | 30 | @ParameterizedTest(name = "Qualified rule id: `{0}`, expected rule set id: `{1}`") 31 | @CsvSource( 32 | value = [ 33 | "standard:rule-id,standard", 34 | "custom:rule-id,custom", 35 | ], 36 | ) 37 | fun `Given a qualified rule id then return the rule set id`( 38 | id: String, 39 | ruleSetId: String, 40 | ) { 41 | val rule = creatRule(id) 42 | assertThat(rule.ruleId.ruleSetId.value).isEqualTo(ruleSetId) 43 | } 44 | 45 | private fun creatRule(ruleId: String) = 46 | object : Rule( 47 | ruleId = RuleId(ruleId), 48 | about = About(), 49 | ) {} 50 | } 51 | -------------------------------------------------------------------------------- /ktlint-rule-engine-core/src/test/kotlin/com/pinterest/ktlint/rule/engine/core/api/editorconfig/CodeStyleEditorConfigPropertyTest.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.rule.engine.core.api.editorconfig 2 | 3 | import org.assertj.core.api.Assertions.assertThat 4 | import org.junit.jupiter.params.ParameterizedTest 5 | import org.junit.jupiter.params.provider.CsvSource 6 | 7 | class CodeStyleEditorConfigPropertyTest { 8 | @ParameterizedTest(name = "Value: [{0}], result: [{1}]") 9 | @CsvSource( 10 | value = [ 11 | "ktlint_official,ktlint_official", 12 | " ktlint_official,ktlint_official", 13 | "ktlint_official ,ktlint_official", 14 | " ktlint_official ,ktlint_official", 15 | "intellij_idea,intellij_idea", 16 | " intellij_idea,intellij_idea", 17 | "intellij_idea ,intellij_idea", 18 | " intellij_idea ,intellij_idea", 19 | "android_studio,android_studio", 20 | " android_studio,android_studio", 21 | "android_studio ,android_studio", 22 | " android_studio ,android_studio", 23 | ], 24 | ignoreLeadingAndTrailingWhitespace = false, 25 | ) 26 | fun `Given a code style property`( 27 | value: String, 28 | expectedCodeStyleValue: CodeStyleValue, 29 | ) { 30 | val actual = CODE_STYLE_PROPERTY.type.parse(value) 31 | 32 | assertThat(actual.parsed).isEqualTo(expectedCodeStyleValue) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /ktlint-rule-engine-core/src/test/kotlin/com/pinterest/ktlint/rule/engine/core/api/editorconfig/CommaSeparatedListValueParserTest.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.rule.engine.core.api.editorconfig 2 | 3 | import org.assertj.core.api.Assertions.assertThat 4 | import org.ec4j.core.model.PropertyType 5 | import org.junit.jupiter.api.Test 6 | 7 | class CommaSeparatedListValueParserTest { 8 | private val propertyType = 9 | PropertyType.LowerCasingPropertyType( 10 | "some-property-type", 11 | null, 12 | CommaSeparatedListValueParser(), 13 | ) 14 | 15 | @Test 16 | fun `Given a comma separated list property with value unset`() { 17 | val actual = propertyType.parse("unset") 18 | 19 | assertThat(actual.isUnset).isTrue() 20 | } 21 | 22 | @Test 23 | fun `Given a comma separated list property with a single value`() { 24 | val actual = propertyType.parse(SOME_VALUE_1) 25 | 26 | assertThat(actual.parsed).containsExactlyInAnyOrder(SOME_VALUE_1) 27 | } 28 | 29 | @Test 30 | fun `Given a comma separated list property with a multiple values`() { 31 | val actual = propertyType.parse("$SOME_VALUE_1,$SOME_VALUE_2") 32 | 33 | assertThat(actual.parsed).containsExactlyInAnyOrder(SOME_VALUE_1, SOME_VALUE_2) 34 | } 35 | 36 | @Test 37 | fun `Given a comma separated list property with a multiple values and redundant space before or after value`() { 38 | val actual = propertyType.parse(" $SOME_VALUE_1 , $SOME_VALUE_2 ") 39 | 40 | assertThat(actual.parsed).containsExactlyInAnyOrder(SOME_VALUE_1, SOME_VALUE_2) 41 | } 42 | 43 | private companion object { 44 | const val SOME_VALUE_1 = "some-value-1" 45 | const val SOME_VALUE_2 = "some-value-2" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /ktlint-rule-engine-core/src/test/kotlin/com/pinterest/ktlint/rule/engine/core/api/editorconfig/RuleExecutionEditorConfigPropertyTest.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.rule.engine.core.api.editorconfig 2 | 3 | import org.assertj.core.api.Assertions.assertThat 4 | import org.junit.jupiter.params.ParameterizedTest 5 | import org.junit.jupiter.params.provider.CsvSource 6 | 7 | class RuleExecutionEditorConfigPropertyTest { 8 | @ParameterizedTest(name = "Value: [{0}], result: [{1}]") 9 | @CsvSource( 10 | value = [ 11 | "enabled,enabled", 12 | " enabled,enabled", 13 | "enabled ,enabled", 14 | " enabled ,enabled", 15 | "disabled,disabled", 16 | " disabled,disabled", 17 | "disabled ,disabled", 18 | " disabled ,disabled", 19 | ], 20 | ignoreLeadingAndTrailingWhitespace = false, 21 | ) 22 | fun `Given a rule execution property for which the value`( 23 | value: String, 24 | expectedRuleExecution: RuleExecution, 25 | ) { 26 | val actual = RULE_EXECUTION_PROPERTY_TYPE.parse(value) 27 | 28 | assertThat(actual.parsed).isEqualTo(expectedRuleExecution) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ktlint-rule-engine-core/src/test/kotlin/com/pinterest/ktlint/rule/engine/core/api/editorconfig/SafeEnumValueParserTest.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.rule.engine.core.api.editorconfig 2 | 3 | import org.assertj.core.api.Assertions.assertThat 4 | import org.ec4j.core.model.PropertyType 5 | import org.junit.jupiter.api.Test 6 | 7 | class SafeEnumValueParserTest { 8 | @Test 9 | fun `Given a rule execution property for which the value`() { 10 | val propertyType = 11 | PropertyType.LowerCasingPropertyType( 12 | "some-property-type", 13 | null, 14 | SafeEnumValueParser(SomePropertyType::class.java), 15 | SomePropertyType.entries.map { it.name }.toSet(), 16 | ) 17 | 18 | val actual = propertyType.parse(" value2 ") 19 | 20 | assertThat(actual.parsed).isEqualTo(SomePropertyType.value2) 21 | } 22 | 23 | @Suppress("EnumEntryName") 24 | private enum class SomePropertyType { 25 | value1, 26 | value2, 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ktlint-rule-engine/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("ktlint-publication-library") 3 | } 4 | 5 | dependencies { 6 | implementation(projects.ktlintLogger) 7 | 8 | api(projects.ktlintRuleEngineCore) 9 | api(libs.kotlin.compiler) 10 | api(libs.ec4j) 11 | 12 | testImplementation(projects.ktlintTest) 13 | testImplementation(projects.ktlintRulesetStandard) 14 | testRuntimeOnly(libs.logback) 15 | 16 | testImplementation(libs.junit5.jupiter) 17 | // Since Gradle 8 the platform launcher needs explicitly be defined as runtime dependency to avoid classpath problems 18 | // https://docs.gradle.org/8.12/userguide/upgrading_version_8.html#test_framework_implementation_dependencies 19 | testRuntimeOnly(libs.junit5.platform.launcher) 20 | } 21 | -------------------------------------------------------------------------------- /ktlint-rule-engine/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=ktlint-rule-engine 2 | POM_ARTIFACT_ID=ktlint-rule-engine 3 | -------------------------------------------------------------------------------- /ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/api/KtLintParseException.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.rule.engine.api 2 | 3 | /** 4 | * [KtLintParseException] is thrown whenever the kotlin code which is to be scanned contains a parsing error. Ensure 5 | * that the code which is to be scanned, does not contain compilation errors. 6 | * 7 | * @param line line number (one-based) 8 | * @param col column number (one-based) 9 | * @param message message 10 | */ 11 | public class KtLintParseException( 12 | public val line: Int, 13 | public val col: Int, 14 | message: String, 15 | ) : RuntimeException("$line:$col $message") 16 | -------------------------------------------------------------------------------- /ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/api/KtLintRuleException.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.rule.engine.api 2 | 3 | /** 4 | * [KtLintRuleException] is thrown whenever an error occurs during execution of a KtLint [Rule]. 5 | * 6 | * @param line line number (one-based) 7 | * @param col column number (one-based) 8 | * @param ruleId rule id (prepended with "<ruleSetId>:" in case of non-standard ruleset) 9 | * @param message description or error 10 | */ 11 | public class KtLintRuleException( 12 | public val line: Int, 13 | public val col: Int, 14 | public val ruleId: String, 15 | override val message: String, 16 | override val cause: Throwable, 17 | ) : RuntimeException(message, cause) 18 | -------------------------------------------------------------------------------- /ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/api/LintError.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.rule.engine.api 2 | 3 | import com.pinterest.ktlint.rule.engine.core.api.RuleId 4 | import dev.drewhamilton.poko.Poko 5 | 6 | /** 7 | * Lint error found by the [KtLintRuleEngine]. 8 | * 9 | * [line]: line number (one-based) 10 | * [col]: column number (one-based) 11 | * [ruleId]: rule id 12 | * [detail]: error message 13 | * [canBeAutoCorrected]: flag indicating whether the error can be corrected by the rule if "format" is run 14 | */ 15 | @Poko 16 | public class LintError( 17 | public val line: Int, 18 | public val col: Int, 19 | public val ruleId: RuleId, 20 | public val detail: String, 21 | public val canBeAutoCorrected: Boolean, 22 | ) 23 | -------------------------------------------------------------------------------- /ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/AutocorrectHandler.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.rule.engine.internal 2 | 3 | import com.pinterest.ktlint.rule.engine.api.LintError 4 | import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision 5 | import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision.ALLOW_AUTOCORRECT 6 | import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision.NO_AUTOCORRECT 7 | import com.pinterest.ktlint.rule.engine.core.api.RuleAutocorrectApproveHandler 8 | 9 | /** 10 | * Handler which determines whether autocorrect should be enabled or disabled for the given offset. 11 | */ 12 | internal sealed interface AutocorrectHandler { 13 | fun autocorrectDecision(lintError: LintError): AutocorrectDecision 14 | } 15 | 16 | /** 17 | * Autocorrect no [LintError]s. This handler is used for backward compatability with rules that have not implemented 18 | * [RuleAutocorrectApproveHandler]. 19 | */ 20 | internal data object NoneAutocorrectHandler : AutocorrectHandler { 21 | override fun autocorrectDecision(lintError: LintError) = NO_AUTOCORRECT 22 | } 23 | 24 | /** 25 | * Autocorrect all [LintError]s. This handler is used for backward compatability with rules that have not implemented 26 | * [RuleAutocorrectApproveHandler]. 27 | */ 28 | internal data object AllAutocorrectHandler : AutocorrectHandler { 29 | override fun autocorrectDecision(lintError: LintError) = ALLOW_AUTOCORRECT 30 | } 31 | 32 | /** 33 | * The [LintErrorAutocorrectHandler] only works for rules that implement [RuleAutocorrectApproveHandler]. For rules that do not implement 34 | * that interface, no autocorrections will be made even though the rule is capable doing so. Reason for this is that the API consumer should 35 | * be able to control whether a specific [LintError] is to be corrected or not. 36 | */ 37 | internal class LintErrorAutocorrectHandler( 38 | val autocorrectRuleWithoutAutocorrectApproveHandler: Boolean, 39 | private val callback: (LintError) -> AutocorrectDecision, 40 | ) : AutocorrectHandler { 41 | override fun autocorrectDecision(lintError: LintError) = callback(lintError) 42 | } 43 | -------------------------------------------------------------------------------- /ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/DefaultEditorConfigProperties.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.rule.engine.internal 2 | 3 | import com.pinterest.ktlint.rule.engine.core.api.editorconfig.CODE_STYLE_PROPERTY 4 | import com.pinterest.ktlint.rule.engine.core.api.editorconfig.END_OF_LINE_PROPERTY 5 | import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfigProperty 6 | import com.pinterest.ktlint.rule.engine.core.api.editorconfig.INDENT_SIZE_PROPERTY 7 | import com.pinterest.ktlint.rule.engine.core.api.editorconfig.INDENT_STYLE_PROPERTY 8 | import com.pinterest.ktlint.rule.engine.core.api.editorconfig.INSERT_FINAL_NEWLINE_PROPERTY 9 | import com.pinterest.ktlint.rule.engine.core.api.editorconfig.MAX_LINE_LENGTH_PROPERTY 10 | 11 | internal val DEFAULT_EDITOR_CONFIG_PROPERTIES: List> = 12 | listOf( 13 | CODE_STYLE_PROPERTY, 14 | END_OF_LINE_PROPERTY, 15 | INDENT_STYLE_PROPERTY, 16 | INDENT_SIZE_PROPERTY, 17 | INSERT_FINAL_NEWLINE_PROPERTY, 18 | MAX_LINE_LENGTH_PROPERTY, 19 | ) 20 | -------------------------------------------------------------------------------- /ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/VisitorProvider.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.rule.engine.internal 2 | 3 | import com.pinterest.ktlint.logger.api.initKtLintKLogger 4 | import com.pinterest.ktlint.rule.engine.core.api.Rule 5 | import com.pinterest.ktlint.rule.engine.core.api.RuleProvider 6 | import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfig 7 | import io.github.oshai.kotlinlogging.KotlinLogging 8 | 9 | private val LOGGER = KotlinLogging.logger {}.initKtLintKLogger() 10 | 11 | /** 12 | * The VisitorProvider is created for each file being scanned. As the [RuleProviderSorter] logs the order in which the 13 | * rules are executed, a singleton instance of the class is used to prevent that the logs are flooded with duplicate 14 | * log lines. 15 | */ 16 | private val RULE_PROVIDER_SORTER = RuleProviderSorter() 17 | 18 | internal class VisitorProvider( 19 | /** 20 | * The set of [RuleProvider]s to be executed. This set should not contain any [RuleProvider]s which are disabled via the [EditorConfig]. 21 | */ 22 | ruleProviders: Set, 23 | /** 24 | * Creates a new [RuleProviderSorter]. Only to be used in unit tests where the same set of rules are used with distinct 25 | * [Rule.VisitorModifier]s. 26 | */ 27 | recreateRuleSorter: Boolean = false, 28 | ) { 29 | /** 30 | * The list of [ruleProvidersSorted] is sorted based on the [Rule.VisitorModifier] of the rules. 31 | */ 32 | private val ruleProvidersSorted: List = 33 | if (recreateRuleSorter) { 34 | RuleProviderSorter() 35 | } else { 36 | RULE_PROVIDER_SORTER 37 | }.getSortedRuleProviders(ruleProviders) 38 | 39 | internal val rules: List 40 | get() { 41 | if (ruleProvidersSorted.isEmpty()) { 42 | LOGGER.debug { "Skipping file as no enabled rules are found to be executed" } 43 | return emptyList() 44 | } 45 | return ruleProvidersSorted.map { 46 | it.createNewRuleInstance() 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/rulefilter/InternalRuleProvidersFilter.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.rule.engine.internal.rulefilter 2 | 3 | import com.pinterest.ktlint.logger.api.initKtLintKLogger 4 | import com.pinterest.ktlint.rule.engine.api.KtLintRuleEngine 5 | import com.pinterest.ktlint.rule.engine.core.api.RuleProvider 6 | import com.pinterest.ktlint.rule.engine.internal.rules.KtlintSuppressionRule 7 | import io.github.oshai.kotlinlogging.KotlinLogging 8 | 9 | private val LOGGER = KotlinLogging.logger {}.initKtLintKLogger() 10 | 11 | /** 12 | * Add internal [RuleProvider]s. These rule providers always have to run regardless of the rules providers which are provided by the API 13 | * consumer. In case the API consumer tries to provide a rule with the same rule id as an internal rule provider than it will be ignored. 14 | */ 15 | internal class InternalRuleProvidersFilter( 16 | private val ktLintRuleEngine: KtLintRuleEngine, 17 | ) : RuleFilter { 18 | private val internalRuleProviders = 19 | setOf( 20 | RuleProvider { 21 | KtlintSuppressionRule( 22 | ktLintRuleEngine.ruleProviders.map { it.ruleId }, 23 | ) 24 | }, 25 | ) 26 | 27 | override fun filter(ruleProviders: Set): Set { 28 | val internalRuleIds = internalRuleProviders.map { it.ruleId } 29 | return ruleProviders 30 | .mapNotNullTo(mutableSetOf()) { 31 | if (it.ruleId in internalRuleIds) { 32 | LOGGER.error { "The provided rule with id '${it.ruleId}' is ignored in favour of Ktlint's rule with same id" } 33 | null 34 | } else { 35 | it 36 | } 37 | }.plus(internalRuleProviders) 38 | .toSet() 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/rulefilter/RuleFilter.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.rule.engine.internal.rulefilter 2 | 3 | import com.pinterest.ktlint.rule.engine.api.KtLintRuleEngine 4 | import com.pinterest.ktlint.rule.engine.core.api.RuleProvider 5 | 6 | /** 7 | * Gets the rule provider for the [KtLintRuleEngine] by applying the [ruleFilters] in the given order on the set of [RuleProvider]s provided 8 | * by the previous (or the initial list of [RuleProvider]s). 9 | */ 10 | internal fun KtLintRuleEngine.applyRuleFilters(vararg ruleFilters: RuleFilter): Set { 11 | var ruleProviders = initialRuleProviders() 12 | val ruleFilterIterator = ruleFilters.iterator() 13 | while (ruleFilterIterator.hasNext()) { 14 | val ruleFilter = ruleFilterIterator.next() 15 | ruleProviders = ruleFilter.filter(ruleProviders) 16 | } 17 | return ruleProviders 18 | } 19 | 20 | private fun KtLintRuleEngine.initialRuleProviders() = 21 | ruleProviders 22 | .distinctBy { it.ruleId } 23 | .toSet() 24 | 25 | internal interface RuleFilter { 26 | fun filter(ruleProviders: Set): Set 27 | } 28 | -------------------------------------------------------------------------------- /ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/rules/InternalRule.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.rule.engine.internal.rules 2 | 3 | import com.pinterest.ktlint.rule.engine.core.api.Rule 4 | import com.pinterest.ktlint.rule.engine.core.api.RuleAutocorrectApproveHandler 5 | import com.pinterest.ktlint.rule.engine.core.api.RuleId 6 | import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfigProperty 7 | 8 | internal val INTERNAL_RULE_ABOUT = 9 | Rule.About( 10 | maintainer = "KtLint", 11 | repositoryUrl = "https://github.com/pinterest/ktlint", 12 | issueTrackerUrl = "https://github.com/pinterest/ktlint/issues", 13 | ) 14 | 15 | /** 16 | * Internal rules can only be declared and instantiated in the 'ktlint-rule-engine'. 17 | */ 18 | public open class InternalRule internal constructor( 19 | id: String, 20 | override val visitorModifiers: Set = emptySet(), 21 | override val usesEditorConfigProperties: Set> = emptySet(), 22 | ) : Rule( 23 | ruleId = RuleId("internal:$id"), 24 | visitorModifiers = visitorModifiers, 25 | usesEditorConfigProperties = usesEditorConfigProperties, 26 | about = INTERNAL_RULE_ABOUT, 27 | ), 28 | RuleAutocorrectApproveHandler 29 | -------------------------------------------------------------------------------- /ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/internal/VisitorProviderTest.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.rule.engine.internal 2 | 3 | import org.assertj.core.api.Assertions.assertThat 4 | import org.assertj.core.api.Assertions.assertThatNoException 5 | import org.junit.jupiter.api.Test 6 | 7 | class VisitorProviderTest { 8 | @Test 9 | fun `When no runnable rules are found for the root node, the visit function on the root node is not executed`() { 10 | assertThatNoException() 11 | .isThrownBy { 12 | VisitorProvider( 13 | ruleProviders = emptySet(), 14 | recreateRuleSorter = true, 15 | ).rules.forEach { _ -> 16 | assertThat(false) 17 | .withFailMessage("The visitor provider should not have called this lambda in case it has no rule providers") 18 | .isTrue 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ktlint-rule-engine/src/test/resources/spec/format-unicode-bom.kt.spec: -------------------------------------------------------------------------------- 1 | // Although probably not visible in your editor, this file starts with a UTF8 BOM unicode character 2 | -------------------------------------------------------------------------------- /ktlint-ruleset-standard/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("ktlint-publication-library") 3 | } 4 | 5 | dependencies { 6 | implementation(projects.ktlintLogger) 7 | 8 | api(projects.ktlintCliRulesetCore) 9 | api(projects.ktlintRuleEngineCore) 10 | 11 | testImplementation(projects.ktlintTest) 12 | testRuntimeOnly(libs.logback) 13 | 14 | testImplementation(libs.junit5.jupiter) 15 | // Since Gradle 8 the platform launcher needs explicitly be defined as runtime dependency to avoid classpath problems 16 | // https://docs.gradle.org/8.12/userguide/upgrading_version_8.html#test_framework_implementation_dependencies 17 | testRuntimeOnly(libs.junit5.platform.launcher) 18 | } 19 | -------------------------------------------------------------------------------- /ktlint-ruleset-standard/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=ktlint-ruleset-standard 2 | POM_ARTIFACT_ID=ktlint-ruleset-standard 3 | -------------------------------------------------------------------------------- /ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/StandardRule.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.ruleset.standard 2 | 3 | import com.pinterest.ktlint.rule.engine.core.api.Rule 4 | import com.pinterest.ktlint.rule.engine.core.api.RuleAutocorrectApproveHandler 5 | import com.pinterest.ktlint.rule.engine.core.api.RuleId 6 | import com.pinterest.ktlint.rule.engine.core.api.RuleSetId 7 | import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfigProperty 8 | 9 | internal val STANDARD_RULE_ABOUT = 10 | Rule.About( 11 | maintainer = "KtLint", 12 | repositoryUrl = "https://github.com/pinterest/ktlint", 13 | issueTrackerUrl = "https://github.com/pinterest/ktlint/issues", 14 | ) 15 | 16 | /** 17 | * Standard rules can only be declared and instantiated in the 'ktlint-ruleset-standard'. Custom rule set providers or API consumers have 18 | * to extend the [Rule] class to define a custom rule. 19 | */ 20 | public open class StandardRule internal constructor( 21 | id: String, 22 | override val visitorModifiers: Set = emptySet(), 23 | override val usesEditorConfigProperties: Set> = emptySet(), 24 | ) : Rule( 25 | ruleId = RuleId("${RuleSetId.STANDARD.value}:$id"), 26 | visitorModifiers = visitorModifiers, 27 | usesEditorConfigProperties = usesEditorConfigProperties, 28 | about = STANDARD_RULE_ABOUT, 29 | ), 30 | RuleAutocorrectApproveHandler 31 | -------------------------------------------------------------------------------- /ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoBlankLineBeforeRbraceRule.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.ruleset.standard.rules 2 | 3 | import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision 4 | import com.pinterest.ktlint.rule.engine.core.api.ElementType.RBRACE 5 | import com.pinterest.ktlint.rule.engine.core.api.RuleId 6 | import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint 7 | import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.STABLE 8 | import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed 9 | import com.pinterest.ktlint.rule.engine.core.api.nextLeaf 10 | import com.pinterest.ktlint.ruleset.standard.StandardRule 11 | import org.jetbrains.kotlin.com.intellij.lang.ASTNode 12 | import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace 13 | import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement 14 | 15 | @SinceKtlint("0.10.2", STABLE) 16 | public class NoBlankLineBeforeRbraceRule : StandardRule("no-blank-line-before-rbrace") { 17 | override fun beforeVisitChildNodes( 18 | node: ASTNode, 19 | emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, 20 | ) { 21 | if (node is PsiWhiteSpace && 22 | node.textContains('\n') && 23 | node.nextLeaf()?.elementType == RBRACE 24 | ) { 25 | val split = node.getText().split("\n") 26 | if (split.size > 2) { 27 | emit( 28 | node.startOffset + split[0].length + split[1].length + 1, 29 | "Unexpected blank line(s) before \"}\"", 30 | true, 31 | ).ifAutocorrectAllowed { 32 | (node as LeafPsiElement).rawReplaceWithText("${split.first()}\n${split.last()}") 33 | } 34 | } 35 | } 36 | } 37 | } 38 | 39 | public val NO_BLANK_LINE_BEFORE_RBRACE_RULE_ID: RuleId = NoBlankLineBeforeRbraceRule().ruleId 40 | -------------------------------------------------------------------------------- /ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoBlankLinesInChainedMethodCallsRule.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.ruleset.standard.rules 2 | 3 | import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision 4 | import com.pinterest.ktlint.rule.engine.core.api.ElementType.DOT_QUALIFIED_EXPRESSION 5 | import com.pinterest.ktlint.rule.engine.core.api.RuleId 6 | import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint 7 | import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed 8 | import com.pinterest.ktlint.ruleset.standard.StandardRule 9 | import org.jetbrains.kotlin.com.intellij.lang.ASTNode 10 | import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace 11 | import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement 12 | 13 | @SinceKtlint("0.46", SinceKtlint.Status.STABLE) 14 | public class NoBlankLinesInChainedMethodCallsRule : StandardRule("no-blank-lines-in-chained-method-calls") { 15 | override fun beforeVisitChildNodes( 16 | node: ASTNode, 17 | emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, 18 | ) { 19 | val isBlankLine = node is PsiWhiteSpace && node.getText().contains("\n\n") 20 | if (isBlankLine && node.treeParent.elementType == DOT_QUALIFIED_EXPRESSION) { 21 | emit(node.startOffset + 1, "Needless blank line(s)", true) 22 | .ifAutocorrectAllowed { 23 | (node as LeafPsiElement).rawReplaceWithText("\n" + node.getText().split("\n\n")[1]) 24 | } 25 | } 26 | } 27 | } 28 | 29 | public val NO_BLANK_LINES_IN_CHAINED_METHOD_CALLS_RULE_ID: RuleId = NoBlankLinesInChainedMethodCallsRule().ruleId 30 | -------------------------------------------------------------------------------- /ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoLineBreakAfterElseRule.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.ruleset.standard.rules 2 | 3 | import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision 4 | import com.pinterest.ktlint.rule.engine.core.api.ElementType.ELSE_KEYWORD 5 | import com.pinterest.ktlint.rule.engine.core.api.ElementType.IF_KEYWORD 6 | import com.pinterest.ktlint.rule.engine.core.api.ElementType.LBRACE 7 | import com.pinterest.ktlint.rule.engine.core.api.RuleId 8 | import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint 9 | import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.STABLE 10 | import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed 11 | import com.pinterest.ktlint.rule.engine.core.api.nextLeaf 12 | import com.pinterest.ktlint.rule.engine.core.api.prevLeaf 13 | import com.pinterest.ktlint.ruleset.standard.StandardRule 14 | import org.jetbrains.kotlin.com.intellij.lang.ASTNode 15 | import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace 16 | import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement 17 | 18 | @SinceKtlint("0.15", STABLE) 19 | public class NoLineBreakAfterElseRule : StandardRule("no-line-break-after-else") { 20 | override fun beforeVisitChildNodes( 21 | node: ASTNode, 22 | emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, 23 | ) { 24 | if (node is PsiWhiteSpace && 25 | node.textContains('\n') 26 | ) { 27 | if (node.prevLeaf()?.elementType == ELSE_KEYWORD && 28 | node.nextLeaf()?.elementType.let { it == IF_KEYWORD || it == LBRACE } 29 | ) { 30 | emit(node.startOffset + 1, "Unexpected line break after \"else\"", true) 31 | .ifAutocorrectAllowed { 32 | (node as LeafPsiElement).rawReplaceWithText(" ") 33 | } 34 | } 35 | } 36 | } 37 | } 38 | 39 | public val NO_LINE_BREAK_AFTER_ELSE_RULE_ID: RuleId = NoLineBreakAfterElseRule().ruleId 40 | -------------------------------------------------------------------------------- /ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoUnitReturnRule.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.ruleset.standard.rules 2 | 3 | import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision 4 | import com.pinterest.ktlint.rule.engine.core.api.ElementType.FUN 5 | import com.pinterest.ktlint.rule.engine.core.api.ElementType.LBRACE 6 | import com.pinterest.ktlint.rule.engine.core.api.ElementType.TYPE_REFERENCE 7 | import com.pinterest.ktlint.rule.engine.core.api.ElementType.VALUE_PARAMETER_LIST 8 | import com.pinterest.ktlint.rule.engine.core.api.RuleId 9 | import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint 10 | import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.STABLE 11 | import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed 12 | import com.pinterest.ktlint.rule.engine.core.api.nextCodeLeaf 13 | import com.pinterest.ktlint.ruleset.standard.StandardRule 14 | import org.jetbrains.kotlin.com.intellij.lang.ASTNode 15 | 16 | @SinceKtlint("0.7", STABLE) 17 | public class NoUnitReturnRule : StandardRule("no-unit-return") { 18 | override fun beforeVisitChildNodes( 19 | node: ASTNode, 20 | emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, 21 | ) { 22 | if (node.elementType == TYPE_REFERENCE && 23 | node.treeParent.elementType == FUN && 24 | node.text == "Unit" && 25 | node.nextCodeLeaf(skipSubtree = true)?.elementType == LBRACE 26 | ) { 27 | emit(node.startOffset, "Unnecessary \"Unit\" return type", true) 28 | .ifAutocorrectAllowed { 29 | var prevNode = node 30 | while (prevNode.treePrev.elementType != VALUE_PARAMETER_LIST) { 31 | prevNode = prevNode.treePrev 32 | } 33 | node.treeParent.removeRange(prevNode, node.treeNext) 34 | } 35 | } 36 | } 37 | } 38 | 39 | public val NO_UNIT_RETURN_RULE_ID: RuleId = NoUnitReturnRule().ruleId 40 | -------------------------------------------------------------------------------- /ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NullableTypeSpacingRule.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.ruleset.standard.rules 2 | 3 | import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision 4 | import com.pinterest.ktlint.rule.engine.core.api.ElementType.QUEST 5 | import com.pinterest.ktlint.rule.engine.core.api.ElementType.WHITE_SPACE 6 | import com.pinterest.ktlint.rule.engine.core.api.RuleId 7 | import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint 8 | import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.EXPERIMENTAL 9 | import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.STABLE 10 | import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed 11 | import com.pinterest.ktlint.rule.engine.core.api.prevLeaf 12 | import com.pinterest.ktlint.ruleset.standard.StandardRule 13 | import org.jetbrains.kotlin.com.intellij.lang.ASTNode 14 | import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement 15 | 16 | @SinceKtlint("0.46", EXPERIMENTAL) 17 | @SinceKtlint("0.49", STABLE) 18 | public class NullableTypeSpacingRule : StandardRule("nullable-type-spacing") { 19 | override fun beforeVisitChildNodes( 20 | node: ASTNode, 21 | emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, 22 | ) { 23 | node 24 | .takeIf { node.elementType == QUEST } 25 | ?.prevLeaf() 26 | ?.takeIf { it.elementType == WHITE_SPACE } 27 | ?.let { whiteSpaceBeforeQuest -> 28 | emit(whiteSpaceBeforeQuest.startOffset, "Unexpected whitespace", true) 29 | .ifAutocorrectAllowed { 30 | (whiteSpaceBeforeQuest as LeafPsiElement).rawRemove() 31 | } 32 | } 33 | } 34 | } 35 | 36 | public val NULLABLE_TYPE_SPACING_RULE_ID: RuleId = NullableTypeSpacingRule().ruleId 37 | -------------------------------------------------------------------------------- /ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenFunctionNameAndOpeningParenthesisRule.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.ruleset.standard.rules 2 | 3 | import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision 4 | import com.pinterest.ktlint.rule.engine.core.api.ElementType 5 | import com.pinterest.ktlint.rule.engine.core.api.ElementType.WHITE_SPACE 6 | import com.pinterest.ktlint.rule.engine.core.api.RuleId 7 | import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint 8 | import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.EXPERIMENTAL 9 | import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.STABLE 10 | import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed 11 | import com.pinterest.ktlint.rule.engine.core.api.nextSibling 12 | import com.pinterest.ktlint.rule.engine.core.api.remove 13 | import com.pinterest.ktlint.ruleset.standard.StandardRule 14 | import org.jetbrains.kotlin.com.intellij.lang.ASTNode 15 | 16 | @SinceKtlint("0.46", EXPERIMENTAL) 17 | @SinceKtlint("0.49", STABLE) 18 | public class SpacingBetweenFunctionNameAndOpeningParenthesisRule : StandardRule("spacing-between-function-name-and-opening-parenthesis") { 19 | override fun beforeVisitChildNodes( 20 | node: ASTNode, 21 | emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, 22 | ) { 23 | node 24 | .takeIf { node.elementType == ElementType.FUN } 25 | ?.findChildByType(ElementType.IDENTIFIER) 26 | ?.nextSibling() 27 | ?.takeIf { it.elementType == WHITE_SPACE } 28 | ?.let { whiteSpace -> 29 | emit(whiteSpace.startOffset, "Unexpected whitespace", true) 30 | .ifAutocorrectAllowed { whiteSpace.remove() } 31 | } 32 | } 33 | } 34 | 35 | public val SPACING_BETWEEN_FUNCTION_NAME_AND_OPENING_PARENTHESIS_RULE_ID: RuleId = 36 | SpacingBetweenFunctionNameAndOpeningParenthesisRule().ruleId 37 | -------------------------------------------------------------------------------- /ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ValueArgumentCommentRule.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.ruleset.standard.rules 2 | 3 | import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision 4 | import com.pinterest.ktlint.rule.engine.core.api.ElementType.VALUE_ARGUMENT 5 | import com.pinterest.ktlint.rule.engine.core.api.RuleId 6 | import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint 7 | import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.STABLE 8 | import com.pinterest.ktlint.rule.engine.core.api.isPartOfComment 9 | import com.pinterest.ktlint.ruleset.standard.StandardRule 10 | import org.jetbrains.kotlin.com.intellij.lang.ASTNode 11 | 12 | /** 13 | * The AST allows comments to be placed anywhere. This however can lead to code which is unnecessarily hard to read. Or, it makes 14 | * development of rules unnecessarily complex. 15 | * 16 | * This rule is based on the DiscouragedCommentLocationRule which is split in several distinct rules. In this way it becomes more clear why 17 | * another rule depends on this rule. 18 | */ 19 | @SinceKtlint("1.1", STABLE) 20 | public class ValueArgumentCommentRule : StandardRule("value-argument-comment") { 21 | override fun beforeVisitChildNodes( 22 | node: ASTNode, 23 | emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, 24 | ) { 25 | if (node.isPartOfComment() && node.treeParent.elementType == VALUE_ARGUMENT) { 26 | // Disallow: 27 | // val foo = foo( 28 | // bar /* some comment */ = "bar" 29 | // ) 30 | // or 31 | // val foo = foo( 32 | // bar = 33 | // // some comment 34 | // "bar" 35 | // ) 36 | emit( 37 | node.startOffset, 38 | "A (block or EOL) comment inside or on same line after a 'value_argument' is not allowed. It may be placed " + 39 | "on a separate line above.", 40 | false, 41 | ) 42 | } 43 | } 44 | } 45 | 46 | public val VALUE_ARGUMENT_COMMENT_RULE_ID: RuleId = ValueArgumentCommentRule().ruleId 47 | -------------------------------------------------------------------------------- /ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/internal/RegExIgnoringDiacriticsAndStrokesOnLetters.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.ruleset.standard.rules.internal 2 | 3 | /** 4 | * Transforms a string containing regular expression ranges like "A-Z" and "a-z" to a RegEx which checks whether a 5 | * unicode character has an uppercase versus a lowercase mapping to a letter. This function intents to keep the original 6 | * expression more readable 7 | */ 8 | internal fun String.regExIgnoringDiacriticsAndStrokesOnLetters() = 9 | replace("A-Z", "\\p{Lu}") 10 | .replace("a-z", "\\p{Ll}") 11 | .toRegex() 12 | -------------------------------------------------------------------------------- /ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/internal/importordering/ImportLayoutParser.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.ruleset.standard.rules.internal.importordering 2 | 3 | internal const val BLANK_LINE_CHAR = "|" 4 | internal const val WILDCARD_CHAR = "*" 5 | internal const val ALIAS_CHAR = "^" 6 | 7 | /** 8 | * Adapted from https://github.com/JetBrains/intellij-kotlin/blob/73b5a484198f02518c9ece2fb453d27cead680fb/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinPackageEntryTableAccessor.kt#L27-L43 9 | */ 10 | internal fun parseImportsLayout(importsLayout: String): List { 11 | val importsList = importsLayout.split(",").map { it.trim() } 12 | 13 | if (importsList.first() == BLANK_LINE_CHAR || importsList.last() == BLANK_LINE_CHAR) { 14 | throw IllegalArgumentException("Blank lines are not supported in the beginning or end of import list") 15 | } 16 | 17 | if (WILDCARD_CHAR !in importsList) { 18 | throw IllegalArgumentException(" symbol (\"*\") must be present in the custom imports layout") 19 | } 20 | 21 | return importsList.map { 22 | var import = it 23 | if (import == BLANK_LINE_CHAR) { 24 | return@map PatternEntry.BLANK_LINE_ENTRY 25 | } else { 26 | var hasAlias = false 27 | var withSubpackages = false 28 | if (import.startsWith(ALIAS_CHAR)) { 29 | import = import.substring(1).trim() 30 | hasAlias = true 31 | } 32 | if (import.endsWith(WILDCARD_CHAR + WILDCARD_CHAR)) { // java.** 33 | import = import.substringBeforeLast(WILDCARD_CHAR) 34 | withSubpackages = true 35 | } 36 | return@map if (import == WILDCARD_CHAR) { // * 37 | PatternEntry.ALL_OTHER_IMPORTS_ENTRY 38 | } else if (import.isEmpty() && hasAlias) { // ^ 39 | PatternEntry.ALL_OTHER_ALIAS_IMPORTS_ENTRY 40 | } else { 41 | PatternEntry(import, withSubpackages, hasAlias) 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/internal/importordering/ImportSorter.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.ruleset.standard.rules.internal.importordering 2 | 3 | import org.jetbrains.kotlin.psi.KtImportDirective 4 | import org.jetbrains.kotlin.resolve.ImportPath 5 | 6 | /** 7 | * Sorts the imports according to the order specified in [patterns] + alphabetically. 8 | * 9 | * Adopted from https://github.com/JetBrains/kotlin/blob/a270ee094c4d7b9520e0898a242bb6ce4dfcad7b/idea/src/org/jetbrains/kotlin/idea/util/ImportPathComparator.kt#L15 10 | */ 11 | internal class ImportSorter( 12 | val patterns: List, 13 | ) : Comparator { 14 | override fun compare( 15 | import1: KtImportDirective, 16 | import2: KtImportDirective, 17 | ): Int { 18 | val importPath1 = import1.importPath!! 19 | val importPath2 = import2.importPath!! 20 | 21 | return compareValuesBy( 22 | importPath1, 23 | importPath2, 24 | { import -> findImportIndex(import) }, 25 | { import -> import.toString().replace("`", "") }, 26 | ) 27 | } 28 | 29 | fun findImportIndex(path: ImportPath): Int { 30 | var bestIndex: Int = -1 31 | var bestEntryMatch: PatternEntry? = null 32 | var allOtherAliasIndex = -1 33 | var allOtherIndex = -1 34 | 35 | for ((index, entry) in patterns.withIndex()) { 36 | if (entry == PatternEntry.ALL_OTHER_ALIAS_IMPORTS_ENTRY) { 37 | allOtherAliasIndex = index 38 | } 39 | if (entry == PatternEntry.ALL_OTHER_IMPORTS_ENTRY) { 40 | allOtherIndex = index 41 | } 42 | if (entry.isBetterMatchForPackageThan(bestEntryMatch, path)) { 43 | bestEntryMatch = entry 44 | bestIndex = index 45 | } 46 | } 47 | 48 | if (bestIndex == -1 && path.hasAlias() && allOtherAliasIndex == -1 && allOtherIndex != -1) { 49 | // if no layout for alias imports specified, put them among all others 50 | bestIndex = allOtherIndex 51 | } 52 | return bestIndex 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ktlint-ruleset-standard/src/main/resources/META-INF/services/com.pinterest.ktlint.cli.ruleset.core.api.RuleSetProviderV3: -------------------------------------------------------------------------------- 1 | com.pinterest.ktlint.ruleset.standard.StandardRuleSetProvider 2 | -------------------------------------------------------------------------------- /ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/StandardRuleSetProviderTest.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.ruleset.standard 2 | 3 | import com.pinterest.ktlint.test.RuleSetProviderTest 4 | 5 | class StandardRuleSetProviderTest : 6 | RuleSetProviderTest( 7 | rulesetClass = StandardRuleSetProvider::class.java, 8 | packageName = "com.pinterest.ktlint.ruleset.standard.rules", 9 | ) 10 | -------------------------------------------------------------------------------- /ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunKeywordSpacingRuleTest.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.ruleset.standard.rules 2 | 3 | import com.pinterest.ktlint.test.KtLintAssertThat.Companion.assertThatRule 4 | import org.junit.jupiter.api.Test 5 | 6 | class FunKeywordSpacingRuleTest { 7 | private val funKeywordSpacingRuleAssertThat = assertThatRule { FunKeywordSpacingRule() } 8 | 9 | @Test 10 | fun `Given a function signature with multiple spaces between the fun keyword and the function name then remove the redundant spaces`() { 11 | val code = 12 | """ 13 | fun foo() = "some-result" 14 | """.trimIndent() 15 | val formattedCode = 16 | """ 17 | fun foo() = "some-result" 18 | """.trimIndent() 19 | funKeywordSpacingRuleAssertThat(code) 20 | .hasLintViolation(1, 4, "Single space expected after the fun keyword") 21 | .isFormattedAs(formattedCode) 22 | } 23 | 24 | @Test 25 | fun `Given a function signature with a newline between the fun keyword and the function name then remove the redundant newline`() { 26 | val code = 27 | """ 28 | fun 29 | foo() = "some-result" 30 | """.trimIndent() 31 | val formattedCode = 32 | """ 33 | fun foo() = "some-result" 34 | """.trimIndent() 35 | funKeywordSpacingRuleAssertThat(code) 36 | .hasLintViolation(1, 4, "Single space expected after the fun keyword") 37 | .isFormattedAs(formattedCode) 38 | } 39 | 40 | @Test 41 | fun `Issue 2879 - Given a function with name between backticks then the fun keyword and name should be separated by a space`() { 42 | val code = 43 | """ 44 | fun`foo or bar`() = "foo" 45 | """.trimIndent() 46 | val formattedCode = 47 | """ 48 | fun `foo or bar`() = "foo" 49 | """.trimIndent() 50 | funKeywordSpacingRuleAssertThat(code) 51 | .hasLintViolation(1, 4, "Space expected between the fun keyword and backtick") 52 | .isFormattedAs(formattedCode) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoBlankLinesInChainedMethodCallsRuleTest.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.ruleset.standard.rules 2 | 3 | import com.pinterest.ktlint.test.KtLintAssertThat.Companion.assertThatRule 4 | import org.junit.jupiter.api.Test 5 | 6 | class NoBlankLinesInChainedMethodCallsRuleTest { 7 | private val noBlankLinesInChainedMethodCallsRuleAssertThat = 8 | assertThatRule { NoBlankLinesInChainedMethodCallsRule() } 9 | 10 | @Test 11 | fun `single blank line in dot qualified expression returns lint error`() { 12 | val code = 13 | """ 14 | fun foo(inputText: String) { 15 | inputText 16 | 17 | .lowercase(Locale.getDefault()) 18 | } 19 | """.trimIndent() 20 | val formattedCode = 21 | """ 22 | fun foo(inputText: String) { 23 | inputText 24 | .lowercase(Locale.getDefault()) 25 | } 26 | """.trimIndent() 27 | noBlankLinesInChainedMethodCallsRuleAssertThat(code) 28 | .hasLintViolation(3, 1, "Needless blank line(s)") 29 | .isFormattedAs(formattedCode) 30 | } 31 | 32 | @Test 33 | fun `single blank line between statements does not return lint error`() { 34 | val code = 35 | """ 36 | fun foo(inputText: String) { 37 | bar() 38 | 39 | bar() 40 | } 41 | """.trimIndent() 42 | noBlankLinesInChainedMethodCallsRuleAssertThat(code).hasNoLintViolations() 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenFunctionNameAndOpeningParenthesisRuleTest.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.ruleset.standard.rules 2 | 3 | import com.pinterest.ktlint.test.KtLintAssertThat 4 | import org.junit.jupiter.api.Test 5 | 6 | class SpacingBetweenFunctionNameAndOpeningParenthesisRuleTest { 7 | private val spacingBetweenFunctionNameAndOpeningParenthesisRuleAssertThat = 8 | KtLintAssertThat.assertThatRule { SpacingBetweenFunctionNameAndOpeningParenthesisRule() } 9 | 10 | @Test 11 | fun `Given a function signature without whitespace between function name and opening parenthesis then do not reformat`() { 12 | val code = 13 | """ 14 | fun foo() = "foo" 15 | """.trimIndent() 16 | spacingBetweenFunctionNameAndOpeningParenthesisRuleAssertThat(code).hasNoLintViolations() 17 | } 18 | 19 | @Test 20 | fun `Given a function signature with one or more spaces between function name and opening parenthesis then do not reformat`() { 21 | val code = 22 | """ 23 | fun foo () = "foo" 24 | """.trimIndent() 25 | val formattedCode = 26 | """ 27 | fun foo() = "foo" 28 | """.trimIndent() 29 | spacingBetweenFunctionNameAndOpeningParenthesisRuleAssertThat(code) 30 | .hasLintViolation(1, 8, "Unexpected whitespace") 31 | .isFormattedAs(formattedCode) 32 | } 33 | 34 | @Test 35 | fun `Given a function signature with one or more new lines between function name and opening parenthesis then do not reformat`() { 36 | val code = 37 | """ 38 | fun foo 39 | () = "foo" 40 | """.trimIndent() 41 | val formattedCode = 42 | """ 43 | fun foo() = "foo" 44 | """.trimIndent() 45 | spacingBetweenFunctionNameAndOpeningParenthesisRuleAssertThat(code) 46 | .hasLintViolation(1, 8, "Unexpected whitespace") 47 | .isFormattedAs(formattedCode) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/UnnecessaryParenthesesBeforeTrailingLambdaRuleTest.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.ruleset.standard.rules 2 | 3 | import com.pinterest.ktlint.test.KtLintAssertThat.Companion.assertThatRule 4 | import org.junit.jupiter.api.Test 5 | 6 | class UnnecessaryParenthesesBeforeTrailingLambdaRuleTest { 7 | private val unnecessaryParenthesesBeforeTrailingLambdaRuleAssertThat = 8 | assertThatRule { UnnecessaryParenthesesBeforeTrailingLambdaRule() } 9 | 10 | @Test 11 | fun `Remove unnecessary parentheses in function call followed by lambda`() { 12 | val code = 13 | """ 14 | fun countDash(input: String) = 15 | "some-string".count() { it == '-' } 16 | """.trimIndent() 17 | val formattedCode = 18 | """ 19 | fun countDash(input: String) = 20 | "some-string".count { it == '-' } 21 | """.trimIndent() 22 | unnecessaryParenthesesBeforeTrailingLambdaRuleAssertThat(code) 23 | .hasLintViolation(2, 24, "Empty parentheses in function call followed by lambda are unnecessary") 24 | .isFormattedAs(formattedCode) 25 | } 26 | 27 | @Test 28 | fun `Issue 2884 - Given some a call expression ending with a lambda argument, followed by an empty argument list followed by another lambda argument then do not remove empty parameter list`() { 29 | val code = 30 | """ 31 | fun fooBar(foo: () -> String): (() -> String) -> String = { bar -> foo().plus(" ").plus(bar()) } 32 | 33 | val foobar = fooBar { "Hello" }() { "world" } 34 | """.trimIndent() 35 | unnecessaryParenthesesBeforeTrailingLambdaRuleAssertThat(code).hasNoLintViolations() 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/importordering/ImportLayoutParserTest.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.ruleset.standard.rules.importordering 2 | 3 | import com.pinterest.ktlint.ruleset.standard.rules.internal.importordering.PatternEntry 4 | import com.pinterest.ktlint.ruleset.standard.rules.internal.importordering.parseImportsLayout 5 | import org.assertj.core.api.Assertions.assertThat 6 | import org.assertj.core.api.Assertions.assertThatThrownBy 7 | import org.junit.jupiter.api.Test 8 | import org.junit.jupiter.params.ParameterizedTest 9 | import org.junit.jupiter.params.provider.ValueSource 10 | 11 | class ImportLayoutParserTest { 12 | @Test 13 | fun `blank lines in the beginning and end of import list are not allowed`() { 14 | assertThatThrownBy { 15 | parseImportsLayout("|,*,|") 16 | }.isInstanceOf(IllegalArgumentException::class.java) 17 | } 18 | 19 | @Test 20 | fun `pattern without single wildcard is not allowed`() { 21 | assertThatThrownBy { 22 | parseImportsLayout("java.util.List.*") 23 | }.isInstanceOf(IllegalArgumentException::class.java) 24 | } 25 | 26 | @ParameterizedTest(name = "Imports layout: {0}") 27 | @ValueSource( 28 | strings = [ 29 | "android.**,|,org.junit.**,|,^android.**,*,kotlin.io.Closeable.*,^", 30 | "android.**, |, org.junit.**, |, ^android.**, *, kotlin.io.Closeable.*, ^", 31 | ], 32 | ) 33 | fun `Given some imports layout then parse it correctly`(importsLayout: String) { 34 | val actual = parseImportsLayout(importsLayout) 35 | 36 | assertThat(actual).containsExactly( 37 | PatternEntry("android.*", withSubpackages = true, hasAlias = false), 38 | PatternEntry.BLANK_LINE_ENTRY, 39 | PatternEntry("org.junit.*", withSubpackages = true, hasAlias = false), 40 | PatternEntry.BLANK_LINE_ENTRY, 41 | PatternEntry("android.*", withSubpackages = true, hasAlias = true), 42 | PatternEntry.ALL_OTHER_IMPORTS_ENTRY, 43 | PatternEntry("kotlin.io.Closeable.*", withSubpackages = false, hasAlias = false), 44 | PatternEntry.ALL_OTHER_ALIAS_IMPORTS_ENTRY, 45 | ) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/importordering/ImportOrderingRuleTest.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.ruleset.standard.rules.importordering 2 | 3 | import com.pinterest.ktlint.ruleset.standard.rules.ImportOrderingRule 4 | import com.pinterest.ktlint.ruleset.standard.rules.internal.importordering.PatternEntry 5 | import org.assertj.core.api.Assertions.assertThat 6 | import org.junit.jupiter.api.Test 7 | 8 | class ImportOrderingRuleTest { 9 | @Test 10 | fun `Given a list of pattern entries then write the patterns as comma separated string`() { 11 | val actual = 12 | ImportOrderingRule.IJ_KOTLIN_IMPORTS_LAYOUT_PROPERTY.propertyWriter( 13 | listOf( 14 | PatternEntry.ALL_OTHER_IMPORTS_ENTRY, 15 | PatternEntry(packageName = "java", withSubpackages = true, hasAlias = false), 16 | PatternEntry.ALL_OTHER_ALIAS_IMPORTS_ENTRY, 17 | ), 18 | ) 19 | assertThat(actual).isEqualTo("*,java.**,^") 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/internal/RemoveDiacriticsFromLettersTest.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.ruleset.standard.rules.internal 2 | 3 | import org.assertj.core.api.Assertions.assertThat 4 | import org.junit.jupiter.params.ParameterizedTest 5 | import org.junit.jupiter.params.provider.ValueSource 6 | 7 | class RemoveDiacriticsFromLettersTest { 8 | @ParameterizedTest(name = "Original character: {0}") 9 | @ValueSource( 10 | strings = [ 11 | "àáâäæãåā", 12 | "çćč", 13 | "èéêëēėę", 14 | "îïíīįì", 15 | "ł", 16 | "ñń", 17 | "ôöòóœøōõ", 18 | "ßśš", 19 | "ûüùúū", 20 | "ÿ", 21 | "žźż", 22 | "ÀÁÂÄÆÃÅĀ", 23 | "ÇĆČ", 24 | "ÈÉÊËĒĖĘ", 25 | "ÎÏÍĪĮÌ", 26 | "Ł", 27 | "ÑŃ", 28 | "ÔÖÒÓŒØŌÕ", 29 | "ŚŠ", 30 | "ÛÜÙÚŪ", 31 | "Ÿ", 32 | "ŽŹŻ", 33 | ], 34 | ) 35 | fun `Given a letter with a diacritic then remove it`(original: String) { 36 | assertThat(original.matches("[A-Za-z]*".regExIgnoringDiacriticsAndStrokesOnLetters())).isTrue 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ktlint-ruleset-template/src/main/kotlin/yourpkgname/CustomRuleSetProvider.kt: -------------------------------------------------------------------------------- 1 | package yourpkgname 2 | 3 | import com.pinterest.ktlint.cli.ruleset.core.api.RuleSetProviderV3 4 | import com.pinterest.ktlint.rule.engine.core.api.RuleProvider 5 | import com.pinterest.ktlint.rule.engine.core.api.RuleSetId 6 | 7 | internal val CUSTOM_RULE_SET_ID = "custom-rule-set-id" 8 | 9 | public class CustomRuleSetProvider : RuleSetProviderV3(RuleSetId(CUSTOM_RULE_SET_ID)) { 10 | override fun getRuleProviders(): Set = 11 | setOf( 12 | RuleProvider { NoVarRule() }, 13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /ktlint-ruleset-template/src/main/kotlin/yourpkgname/NoVarRule.kt: -------------------------------------------------------------------------------- 1 | package yourpkgname 2 | 3 | import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision 4 | import com.pinterest.ktlint.rule.engine.core.api.ElementType 5 | import com.pinterest.ktlint.rule.engine.core.api.Rule 6 | import com.pinterest.ktlint.rule.engine.core.api.RuleAutocorrectApproveHandler 7 | import com.pinterest.ktlint.rule.engine.core.api.RuleId 8 | import org.jetbrains.kotlin.com.intellij.lang.ASTNode 9 | 10 | public class NoVarRule : 11 | Rule( 12 | ruleId = RuleId("$CUSTOM_RULE_SET_ID:no-var"), 13 | about = 14 | About( 15 | maintainer = "Your name", 16 | repositoryUrl = "https://github.com/your/project/", 17 | issueTrackerUrl = "https://github.com/your/project/issues", 18 | ), 19 | ), 20 | RuleAutocorrectApproveHandler { 21 | override fun beforeVisitChildNodes( 22 | node: ASTNode, 23 | emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, 24 | ) { 25 | if (node.elementType == ElementType.VAR_KEYWORD) { 26 | emit(node.startOffset, "Unexpected var, use val instead", false) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ktlint-ruleset-template/src/main/resources/META-INF/services/com.pinterest.ktlint.cli.ruleset.core.api.RuleSetProviderV3: -------------------------------------------------------------------------------- 1 | yourpkgname.CustomRuleSetProvider 2 | -------------------------------------------------------------------------------- /ktlint-ruleset-template/src/test/kotlin/yourpkgname/NoVarRuleTest.kt: -------------------------------------------------------------------------------- 1 | package yourpkgname 2 | 3 | import com.pinterest.ktlint.test.KtLintAssertThat.Companion.assertThatRule 4 | import org.junit.jupiter.api.Test 5 | 6 | class NoVarRuleTest { 7 | private val wrappingRuleAssertThat = assertThatRule { NoVarRule() } 8 | 9 | @Test 10 | fun `No var rule`() { 11 | // whenever KTLINT_DEBUG env variable is set to "ast" or -DktlintDebug=ast is used 12 | // com.pinterest.ktlint.test.(lint|format) will print AST (along with other debug info) to the stderr. 13 | // this can be extremely helpful while writing and testing rules. 14 | // uncomment the line below to take a quick look at it 15 | // System.setProperty("ktlintDebug", "ast") 16 | val code = 17 | """ 18 | fun fn() { 19 | var v = "var" 20 | } 21 | """.trimIndent() 22 | wrappingRuleAssertThat(code) 23 | .hasLintViolationWithoutAutoCorrect(2, 5, "Unexpected var, use val instead") 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ktlint-test-ruleset-provider-v2-deprecated/README.md: -------------------------------------------------------------------------------- 1 | IMPORTANT: This module is kept around for future reference in case the current RuleSetProviderV3 is being deprecated. 2 | 3 | This module contains a rule set provider based on the deprecated `com.pinterest.ktlint.core.api.RuleSetProviderV2` which is already removed from the current code base. It was used to build a custom rule set JAR for testing the `ktlint-cli` module. 4 | 5 | As this module is not meant to be published, you need to build the jar explicitly and copy the jar to the `ktlint-cli` test resource folder: 6 | ```shell 7 | cd .. # run from root of ktlint project 8 | ./gradlew ktlint-test-ruleset-provider-v2-deprecated:jar && \ 9 | cp ktlint-test-ruleset-provider-v2-deprecated/build/libs/ktlint-test-ruleset-provider-v2-deprecated.jar ktlint-cli/src/test/resources/cli/custom-ruleset/rule-set-provider-v2/ 10 | ``` 11 | -------------------------------------------------------------------------------- /ktlint-test-ruleset-provider-v2-deprecated/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("ktlint-kotlin-common") 3 | } 4 | 5 | dependencies { 6 | implementation(projects.ktlintCore) 7 | } 8 | -------------------------------------------------------------------------------- /ktlint-test-ruleset-provider-v2-deprecated/src/main/kotlin/com/pinterest/ruleset/provider/deprecated/SomeRule.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ruleset.provider.deprecated 2 | 3 | import com.pinterest.ktlint.core.Rule 4 | 5 | public class SomeRule : Rule("dump") 6 | -------------------------------------------------------------------------------- /ktlint-test-ruleset-provider-v2-deprecated/src/main/kotlin/com/pinterest/ruleset/provider/deprecated/SomeRuleSetProvider.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ruleset.provider.deprecated 2 | 3 | import com.pinterest.ktlint.core.RuleProvider 4 | import com.pinterest.ktlint.core.RuleSetProviderV2 5 | 6 | // This class deliberately extends a deprecated RuleSetProvider as for testing purposes a custom ruleset jar is required that only contains 7 | // a deprecated ruleset provider. 8 | public class SomeRuleSetProvider : 9 | RuleSetProviderV2( 10 | id = "test", 11 | about = NO_ABOUT, 12 | ) { 13 | override fun getRuleProviders(): Set = 14 | setOf( 15 | RuleProvider { SomeRule() }, 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /ktlint-test-ruleset-provider-v2-deprecated/src/main/resources/META-INF/services/com.pinterest.ktlint.core.RuleSetProviderV2: -------------------------------------------------------------------------------- 1 | com.pinterest.ruleset.provider.deprecated.SomeRuleSetProvider 2 | -------------------------------------------------------------------------------- /ktlint-test/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("ktlint-publication-library") 3 | } 4 | 5 | dependencies { 6 | implementation(projects.ktlintLogger) 7 | implementation(projects.ktlintRuleEngine) 8 | implementation(projects.ktlintCliRulesetCore) 9 | api(libs.assertj) 10 | api(libs.junit5.jupiter) 11 | api(libs.janino) 12 | api(libs.jimfs) 13 | } 14 | -------------------------------------------------------------------------------- /ktlint-test/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=ktlint-test 2 | POM_ARTIFACT_ID=ktlint-test 3 | -------------------------------------------------------------------------------- /ktlint-test/src/main/kotlin/com/pinterest/ktlint/test/KtlintDocumentationTest.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.test 2 | 3 | import org.junit.jupiter.api.Test 4 | 5 | /** 6 | * Mark a unit test as a unit test for an example in the Ktlint documentation. For now the purpose is only to indicate that the code of the 7 | * test is also used in the documentation. A future possible use it that the code samples in the documentation are extracted from the unit 8 | * test directly which then will ensure that the code samples in the documentation actually provide the documented results. 9 | */ 10 | @Test 11 | public annotation class KtlintDocumentationTest 12 | -------------------------------------------------------------------------------- /ktlint-test/src/main/kotlin/com/pinterest/ktlint/test/ReplaceStringTemplatePlaceholder.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.test 2 | 3 | /** 4 | * Replace the [placeholder] with an escaped "$" (i.e. "${'$'}") so that the resulting raw string literal still contains a string template 5 | * which then can be processed by the KtlintRuleEngine. 6 | */ 7 | public fun String.replaceStringTemplatePlaceholder(placeholder: String = "$."): String = replace(placeholder, "${'$'}") 8 | -------------------------------------------------------------------------------- /ktlint-test/src/main/kotlin/com/pinterest/ktlint/test/TestLiterals.kt: -------------------------------------------------------------------------------- 1 | package com.pinterest.ktlint.test 2 | 3 | /** 4 | * Literal for a multiline string quote """. To be used in code samples for testing the KtLint rules. 5 | */ 6 | public const val MULTILINE_STRING_QUOTE: String = "${'"'}${'"'}${'"'}" 7 | 8 | /** 9 | * Literal for a TAB character. To be used in code samples for testing the KtLint rules. 10 | */ 11 | public const val TAB: String = "${'\t'}" 12 | 13 | /** 14 | * Literal for a SPACE. To be used in code samples for testing the KtLint rules. 15 | */ 16 | public const val SPACE: String = "${' '}" 17 | -------------------------------------------------------------------------------- /ktlint-test/src/main/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 13 | 14 | 15 | [%level] %logger{36} - %msg%n 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:base" 5 | ], 6 | "packageRules": [ 7 | { 8 | "matchPackageNames": ["ch.qos.logback:logback-classic"], 9 | "allowedVersions": "<1.4.0" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | gradlePluginPortal() 5 | } 6 | 7 | includeBuild("build-logic") 8 | } 9 | 10 | dependencyResolutionManagement { 11 | repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS 12 | repositories { 13 | mavenCentral() 14 | } 15 | } 16 | 17 | plugins { 18 | id("com.gradle.develocity") version "4.0.2" 19 | id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" 20 | } 21 | 22 | develocity { 23 | buildScan { 24 | termsOfUseUrl = "https://gradle.com/terms-of-service" 25 | termsOfUseAgree = "yes" 26 | // TODO: workaround for https://github.com/gradle/gradle/issues/22879. 27 | val isCI = providers.environmentVariable("CI").isPresent 28 | publishing.onlyIf { isCI } 29 | } 30 | } 31 | 32 | rootProject.name = "ktlint-root" 33 | 34 | enableFeaturePreview("STABLE_CONFIGURATION_CACHE") 35 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") 36 | 37 | include( 38 | ":ktlint-api-consumer", 39 | ":ktlint-bom", 40 | ":ktlint-cli", 41 | ":ktlint-cli-reporter-baseline", 42 | ":ktlint-cli-reporter-checkstyle", 43 | ":ktlint-cli-reporter-core", 44 | ":ktlint-cli-reporter-format", 45 | ":ktlint-cli-reporter-json", 46 | ":ktlint-cli-reporter-sarif", 47 | ":ktlint-cli-reporter-html", 48 | ":ktlint-cli-reporter-plain", 49 | ":ktlint-cli-reporter-plain-summary", 50 | ":ktlint-cli-ruleset-core", 51 | ":ktlint-logger", 52 | ":ktlint-rule-engine", 53 | ":ktlint-rule-engine-core", 54 | ":ktlint-ruleset-standard", 55 | ":ktlint-ruleset-template", 56 | ":ktlint-test", 57 | ) 58 | --------------------------------------------------------------------------------