From 5234fbdde24093147f141154529c271cccd7a72f Mon Sep 17 00:00:00 2001 From: Paul Dingemans Date: Sat, 12 Dec 2020 21:06:04 +0100 Subject: [PATCH 01/53] Ignore tokens between backticks in rule max line length (#1007) Tokens starting and ending with a backtick should be ignored entirely when validating whether a line exceeds the maximum length of a line. Such tokens can not be spit into multiple lines. For example when writing JUnit test it is a common pattern to write the function names between back ticks instead of using the DisplayName annotation. --- .../ruleset/standard/MaxLineLengthRule.kt | 59 +++++++++++++++---- .../ruleset/standard/MaxLineLengthRuleTest.kt | 40 +++++++++++++ 2 files changed, 88 insertions(+), 11 deletions(-) diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/MaxLineLengthRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/MaxLineLengthRule.kt index dd6a8d85df..587f3fcd19 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/MaxLineLengthRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/MaxLineLengthRule.kt @@ -5,10 +5,12 @@ import com.pinterest.ktlint.core.Rule import com.pinterest.ktlint.core.ast.ElementType import com.pinterest.ktlint.core.ast.isPartOf import com.pinterest.ktlint.core.ast.isRoot +import com.pinterest.ktlint.core.ast.nextLeaf import com.pinterest.ktlint.core.ast.parent import com.pinterest.ktlint.core.ast.prevCodeSibling import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.PsiComment +import org.jetbrains.kotlin.com.intellij.psi.PsiElement import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement import org.jetbrains.kotlin.kdoc.psi.api.KDoc import org.jetbrains.kotlin.psi.KtImportDirective @@ -31,12 +33,11 @@ class MaxLineLengthRule : Rule("max-line-length"), Rule.Modifier.Last { return } val errorOffset = arrayListOf() - val text = node.text - val lines = text.split("\n") - var offset = 0 - for (line in lines) { - if (line.length > maxLineLength) { - val el = node.psi.findElementAt(offset + line.length - 1)!!.node + node + .getElementsPerLine() + .filter { it.lengthExcludingTokensBetweenBackticks() > maxLineLength } + .forEach() { parsedLine -> + val el = parsedLine.elements.last() if (!el.isPartOf(KDoc::class) && !el.isPartOfRawMultiLineString()) { if (!el.isPartOf(PsiComment::class)) { if (!el.isPartOf(KtPackageDirective::class) && !el.isPartOf(KtImportDirective::class)) { @@ -45,27 +46,25 @@ class MaxLineLengthRule : Rule("max-line-length"), Rule.Modifier.Last { // node spanning the same offset is 'visit'ed // (for ktlint-disable directive to have effect (when applied)) // this will be rectified in the upcoming release(s) - errorOffset.add(offset) + errorOffset.add(parsedLine.offset) } } else { // Allow ktlint-disable comments to exceed max line length if (!el.text.startsWith("// ktlint-disable")) { // if comment is the only thing on the line - fine, otherwise emit an error val prevLeaf = el.prevCodeSibling() - if (prevLeaf != null && prevLeaf.startOffset >= offset) { + if (prevLeaf != null && prevLeaf.startOffset >= parsedLine.offset) { // fixme: // normally we would emit here but due to API limitations we need to hold off until // node spanning the same offset is 'visit'ed // (for ktlint-disable directive to have effect (when applied)) // this will be rectified in the upcoming release(s) - errorOffset.add(offset) + errorOffset.add(parsedLine.offset) } } } } } - offset += line.length + 1 - } rangeTree = RangeTree(errorOffset) } else if (!rangeTree.isEmpty() && node.psi is LeafPsiElement) { rangeTree @@ -81,6 +80,44 @@ class MaxLineLengthRule : Rule("max-line-length"), Rule.Modifier.Last { ?.let { it.firstChildNode.text == "\"\"\"" && it.textContains('\n') } == true } +private fun ASTNode.getElementsPerLine(): List { + val parsedLines = mutableListOf() + val lines = text.split("\n") + var offset = 0 + for (line in lines) { + val elements = mutableListOf() + var el = psi.findElementAt(offset)?.node + while (el != null && el.startOffset < offset + line.length) { + elements.add(el) + el = el.nextLeaf() + } + parsedLines.add(ParsedLine(line, offset, elements)) + offset += line.length + 1 // +1 for the newline which is stripped due to the splitting of the lines + } + return parsedLines +} + +private data class ParsedLine( + val line: String, + val offset: Int, + val elements: List +) { + fun lengthExcludingTokensBetweenBackticks(): Int { + return line.length - totalLengthBacktickedElements() + } + + private fun totalLengthBacktickedElements(): Int { + return elements + .filterIsInstance(PsiElement::class.java) + .filter { it.text.matches(isValueBetweenBackticks) } + .sumBy(PsiElement::getTextLength) + } + + private companion object { + val isValueBetweenBackticks = Regex("`.*`") + } +} + class RangeTree(seq: List = emptyList()) { private var emptyArrayView = ArrayView(0, 0) diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/MaxLineLengthRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/MaxLineLengthRuleTest.kt index fdf5666753..e6adb167fc 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/MaxLineLengthRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/MaxLineLengthRuleTest.kt @@ -38,6 +38,46 @@ class MaxLineLengthRuleTest { ) } + @Test + fun testErrorSuppressionOnTokensBetweenBackticks() { + assertThat( + MaxLineLengthRule().lint( + """ + @Test + fun `Some too long test description between backticks`() { + println("teeeeeeeeeeeeeeeeeeeeeeeext") + } + """.trimIndent(), + userData = mapOf("max_line_length" to "40") + ) + ).isEqualTo( + listOf( + // Note that no error was generated on line 2 with the long fun name but on another line + LintError(3, 1, "max-line-length", "Exceeded max line length (40)") + ) + ) + } + + @Test + fun testReportLongLinesAfterExcludingTokensBetweenBackticks() { + assertThat( + MaxLineLengthRule().lint( + """ + @ParameterizedTest + fun `Some too long test description between backticks`(looooooooongParameterName: String) { + println("teeeeeeeeext") + } + """.trimIndent(), + userData = mapOf("max_line_length" to "40") + ) + ).isEqualTo( + listOf( + // Note that no error was generated on line 2 with the long fun name + LintError(2, 1, "max-line-length", "Exceeded max line length (40)") + ) + ) + } + @Test fun testLintOff() { assertThat( From 80e62a6208a62b56511ca556a6d81cf8368b6b29 Mon Sep 17 00:00:00 2001 From: Paul Dingemans Date: Sat, 19 Dec 2020 15:30:26 +0100 Subject: [PATCH 02/53] Add option to ignore tokens between backticks in rule max line length (#1007) --- README.md | 14 ++++++++++++++ .../com/pinterest/ktlint/core/EditorConfig.kt | 3 +++ .../ktlint/ruleset/standard/MaxLineLengthRule.kt | 10 +++++++--- .../ruleset/standard/MaxLineLengthRuleTest.kt | 5 ++++- 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 7e9539e02e..6c58203c1d 100644 --- a/README.md +++ b/README.md @@ -117,6 +117,20 @@ disabled_rules=import-ordering disabled_rules=indent ``` +### Overriding Editorconfig properties for unit testing + +According to the [Kotlin coding conventions](https://kotlinlang.org/docs/reference/coding-conventions.html#names-for-test-methods) +it is acceptable to write method names as natural language. As such descriptions tend to be longer than normal method +names, it is possible to ignore such method names when validation the maximum line length. It is advised though, to +limit this behavior to tests only. +```ini +[**/test/**.kt] +# According to https://kotlinlang.org/docs/reference/coding-conventions.html#names-for-test-methods it is acceptable to write method names +# in natural language. When using natural language, the description tends to be longer. Allow lines containing an identifier between +# backticks to be longer than the maximum line length. +ignore_back_ticked_identifier=true +``` + ## Online demo You can try `ktlint` online [here](https://ktlint-demo.herokuapp.com/) using the standard or a custom ruleset without installing it to your PC. \ To contribute or get more info, please visit the [GitHub repository](https://github.com/akuleshov7/diKTat-demo). diff --git a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/EditorConfig.kt b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/EditorConfig.kt index 950514ccf0..0f1cfaea0a 100644 --- a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/EditorConfig.kt +++ b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/EditorConfig.kt @@ -13,6 +13,7 @@ interface EditorConfig { val indentSize: Int val tabWidth: Int val maxLineLength: Int + val ignoreBackTickedIdentifier: Boolean @Deprecated( message = "Not used anymore by rules, please use 'insert_final_newline' directly via get()" ) @@ -32,12 +33,14 @@ interface EditorConfig { val tabWidth = map["indent_size"]?.toIntOrNull() val maxLineLength = map["max_line_length"]?.toIntOrNull() ?: -1 val insertFinalNewline = map["insert_final_newline"]?.toBoolean() ?: true + val ignoreBackTickedIdentifier = map["ignore_back_ticked_identifier"]?.toBoolean() ?: false return object : EditorConfig { override val indentStyle = indentStyle override val indentSize = indentSize override val tabWidth = tabWidth ?: indentSize override val maxLineLength = maxLineLength override val insertFinalNewline = insertFinalNewline + override val ignoreBackTickedIdentifier = ignoreBackTickedIdentifier override fun get(key: String): String? = map[key] } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/MaxLineLengthRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/MaxLineLengthRule.kt index 587f3fcd19..dce2ee60cb 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/MaxLineLengthRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/MaxLineLengthRule.kt @@ -35,7 +35,7 @@ class MaxLineLengthRule : Rule("max-line-length"), Rule.Modifier.Last { val errorOffset = arrayListOf() node .getElementsPerLine() - .filter { it.lengthExcludingTokensBetweenBackticks() > maxLineLength } + .filter { it.lineLength(editorConfig.ignoreBackTickedIdentifier) > maxLineLength } .forEach() { parsedLine -> val el = parsedLine.elements.last() if (!el.isPartOf(KDoc::class) && !el.isPartOfRawMultiLineString()) { @@ -102,8 +102,12 @@ private data class ParsedLine( val offset: Int, val elements: List ) { - fun lengthExcludingTokensBetweenBackticks(): Int { - return line.length - totalLengthBacktickedElements() + fun lineLength(ignoreBackTickedIdentifier: Boolean): Int { + return if (ignoreBackTickedIdentifier) { + line.length - totalLengthBacktickedElements() + } else { + line.length + } } private fun totalLengthBacktickedElements(): Int { diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/MaxLineLengthRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/MaxLineLengthRuleTest.kt index e6adb167fc..0166a85291 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/MaxLineLengthRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/MaxLineLengthRuleTest.kt @@ -48,7 +48,10 @@ class MaxLineLengthRuleTest { println("teeeeeeeeeeeeeeeeeeeeeeeext") } """.trimIndent(), - userData = mapOf("max_line_length" to "40") + userData = mapOf( + "max_line_length" to "40", + "ignore_back_ticked_identifier" to "true" + ) ) ).isEqualTo( listOf( From a7fa11ad977629a4effdf5b9c1dd948699e04651 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Sun, 13 Dec 2020 17:29:44 +0100 Subject: [PATCH 03/53] Fix snapshot publishing (#1009) --- ktlint/build.gradle | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ktlint/build.gradle b/ktlint/build.gradle index ac82b621d7..eb48d5693b 100644 --- a/ktlint/build.gradle +++ b/ktlint/build.gradle @@ -19,10 +19,6 @@ shadowJar { mergeServiceFiles() } -publishing.publications.named("maven").configure { - artifact(shadowJar) -} - dependencies { implementation project(':ktlint-core') implementation project(':ktlint-reporter-baseline') From afd522ddff8752c2bba41c4120c30cb7ad6aaba9 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Mon, 14 Dec 2020 23:07:34 +0100 Subject: [PATCH 04/53] Fix import-ordering editorconifg generation --- .../ruleset/standard/ImportOrderingRule.kt | 47 +++++++------------ .../internal/importordering/ImportSorter.kt | 6 +-- .../internal/importordering/PatternEntry.kt | 8 +++- .../ImportOrderingEditorconfigTest.kt | 21 +++++++++ 4 files changed, 46 insertions(+), 36 deletions(-) create mode 100644 ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/importordering/ImportOrderingEditorconfigTest.kt diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/ImportOrderingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/ImportOrderingRule.kt index fcb058c711..69dd913389 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/ImportOrderingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/ImportOrderingRule.kt @@ -11,7 +11,6 @@ import com.pinterest.ktlint.ruleset.standard.ImportOrderingRule.Companion.ASCII_ import com.pinterest.ktlint.ruleset.standard.ImportOrderingRule.Companion.IDEA_PATTERN import com.pinterest.ktlint.ruleset.standard.internal.importordering.ImportSorter import com.pinterest.ktlint.ruleset.standard.internal.importordering.PatternEntry -import com.pinterest.ktlint.ruleset.standard.internal.importordering.parseImportsLayout import org.ec4j.core.model.PropertyType import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace @@ -40,7 +39,7 @@ public class ImportOrderingRule : Rule("import-ordering"), UsesEditorConfigProperties { - private lateinit var importsLayout: List + private lateinit var importsLayout: String private lateinit var importSorter: ImportSorter public companion object { @@ -55,7 +54,7 @@ public class ImportOrderingRule : * * https://developer.android.com/kotlin/style-guide#import_statements */ - private val ASCII_PATTERN = parseImportsLayout("*") + private const val ASCII_PATTERN = "*" /** * Default IntelliJ IDEA style: Alphabetical with capital letters before lower case letters (e.g. Z before a), @@ -65,7 +64,7 @@ public class ImportOrderingRule : * * https://github.com/JetBrains/kotlin/blob/ffdab473e28d0d872136b910eb2e0f4beea2e19c/idea/formatter/src/org/jetbrains/kotlin/idea/core/formatter/KotlinCodeStyleSettings.java#L87-L91 */ - private val IDEA_PATTERN = parseImportsLayout("*,java.**,javax.**,kotlin.**,^") + private const val IDEA_PATTERN = "*,java.**,javax.**,kotlin.**,^" private const val IDEA_ERROR_MESSAGE = "Imports must be ordered in lexicographic order without any empty lines in-between " + "with \"java\", \"javax\", \"kotlin\" and aliases in the end" @@ -77,16 +76,12 @@ public class ImportOrderingRule : ASCII_PATTERN to ASCII_ERROR_MESSAGE ) - private val editorConfigPropertyParser: (String, String?) -> PropertyType.PropertyValue> = + private val editorConfigPropertyParser: (String, String?) -> PropertyType.PropertyValue = { _, value -> when { - value == null -> PropertyType.PropertyValue.invalid( + value.isNullOrBlank() -> PropertyType.PropertyValue.invalid( value, - "Null is not supported for import layout" - ) - value.isBlank() -> PropertyType.PropertyValue.valid( - value, - emptyList() + "Import layout must contain at least one entry of a wildcard symbol (*)" ) value == "idea" -> PropertyType.PropertyValue.valid( value, @@ -96,22 +91,15 @@ public class ImportOrderingRule : value, ASCII_PATTERN ) - else -> try { - PropertyType.PropertyValue.valid( - value, - parseImportsLayout(value) - ) - } catch (e: IllegalArgumentException) { - PropertyType.PropertyValue.invalid( - value, - "Unexpected imports layout: $value" - ) - } + else -> PropertyType.PropertyValue.valid( + value, + value + ) } } internal val ktlintCustomImportsLayoutProperty = - UsesEditorConfigProperties.EditorConfigProperty>( + UsesEditorConfigProperties.EditorConfigProperty( type = PropertyType( KTLINT_CUSTOM_IMPORTS_LAYOUT_PROPERTY_NAME, PROPERTY_DESCRIPTION, @@ -122,7 +110,7 @@ public class ImportOrderingRule : ) internal val ideaImportsLayoutProperty = - UsesEditorConfigProperties.EditorConfigProperty>( + UsesEditorConfigProperties.EditorConfigProperty( type = PropertyType( IDEA_IMPORTS_LAYOUT_PROPERTY_NAME, PROPERTY_DESCRIPTION, @@ -220,13 +208,10 @@ public class ImportOrderingRule : private fun EditorConfigProperties.resolveImportsLayout( android: Boolean - ): List = when { - containsKey(KTLINT_CUSTOM_IMPORTS_LAYOUT_PROPERTY_NAME) -> - getValue(KTLINT_CUSTOM_IMPORTS_LAYOUT_PROPERTY_NAME).getValueAs() - containsKey(IDEA_IMPORTS_LAYOUT_PROPERTY_NAME) -> - getValue(IDEA_IMPORTS_LAYOUT_PROPERTY_NAME).getValueAs() - else -> - if (android) ideaImportsLayoutProperty.defaultAndroidValue else ideaImportsLayoutProperty.defaultValue + ): String = if (containsKey(KTLINT_CUSTOM_IMPORTS_LAYOUT_PROPERTY_NAME)) { + getEditorConfigValue(ktlintCustomImportsLayoutProperty, android) + } else { + getEditorConfigValue(ideaImportsLayoutProperty, android) } private fun importsAreEqual(actual: List, expected: List): Boolean { diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/internal/importordering/ImportSorter.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/internal/importordering/ImportSorter.kt index 64ed94f078..271961f3d7 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/internal/importordering/ImportSorter.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/internal/importordering/ImportSorter.kt @@ -8,9 +8,9 @@ import org.jetbrains.kotlin.resolve.ImportPath * * Adopted from https://github.com/JetBrains/kotlin/blob/a270ee094c4d7b9520e0898a242bb6ce4dfcad7b/idea/src/org/jetbrains/kotlin/idea/util/ImportPathComparator.kt#L15 */ -internal class ImportSorter( - val patterns: List -) : Comparator { +internal class ImportSorter(importsLayout: String) : Comparator { + + val patterns: List = parseImportsLayout(importsLayout) override fun compare(import1: KtImportDirective, import2: KtImportDirective): Int { val importPath1 = import1.importPath!! diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/internal/importordering/PatternEntry.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/internal/importordering/PatternEntry.kt index f6b201c0d4..427e57d91b 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/internal/importordering/PatternEntry.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/internal/importordering/PatternEntry.kt @@ -42,7 +42,11 @@ internal class PatternEntry( return entry.packageName.count { it == '.' } < packageName.count { it == '.' } } - override fun toString(): String = packageName + override fun toString(): String = when (this) { + ALL_OTHER_IMPORTS_ENTRY -> WILDCARD_CHAR + ALL_OTHER_ALIAS_IMPORTS_ENTRY -> ALIAS_CHAR + else -> "$packageName.$WILDCARD_CHAR" + if (withSubpackages) WILDCARD_CHAR else "" + } override fun equals(other: Any?): Boolean { if (this === other) return true @@ -67,6 +71,6 @@ internal class PatternEntry( companion object { val BLANK_LINE_ENTRY = PatternEntry(BLANK_LINE_CHAR, withSubpackages = true, hasAlias = false) val ALL_OTHER_IMPORTS_ENTRY = PatternEntry(WILDCARD_CHAR, withSubpackages = true, hasAlias = false) - val ALL_OTHER_ALIAS_IMPORTS_ENTRY = PatternEntry((ALIAS_CHAR + WILDCARD_CHAR), withSubpackages = true, hasAlias = true) + val ALL_OTHER_ALIAS_IMPORTS_ENTRY = PatternEntry(ALIAS_CHAR, withSubpackages = true, hasAlias = true) } } diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/importordering/ImportOrderingEditorconfigTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/importordering/ImportOrderingEditorconfigTest.kt new file mode 100644 index 0000000000..b94fab2a3f --- /dev/null +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/importordering/ImportOrderingEditorconfigTest.kt @@ -0,0 +1,21 @@ +package com.pinterest.ktlint.ruleset.standard.importordering + +import com.pinterest.ktlint.core.api.EditorConfigProperties +import com.pinterest.ktlint.core.api.FeatureInAlphaState +import com.pinterest.ktlint.ruleset.standard.ImportOrderingRule +import org.assertj.core.api.Assertions.assertThat +import org.junit.Test + +@OptIn(FeatureInAlphaState::class) +class ImportOrderingEditorconfigTest { + + @Test + fun `import ordering gets written correctly to editorconfig`() { + val properties: EditorConfigProperties = emptyMap() + val rule = ImportOrderingRule() + with(rule) { + val raw = properties.getEditorConfigValue(ImportOrderingRule.ideaImportsLayoutProperty).toString() + assertThat(raw).isEqualTo("*,java.**,javax.**,kotlin.**,^") + } + } +} From 2279d695b095896a9143f80d29a09e5b6ec2a3f2 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Tue, 15 Dec 2020 23:16:12 +0100 Subject: [PATCH 05/53] Revert to using list of PatternEntry --- .../ruleset/standard/ImportOrderingRule.kt | 30 ++++++++++++------- .../internal/importordering/ImportSorter.kt | 6 ++-- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/ImportOrderingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/ImportOrderingRule.kt index 69dd913389..87b43f579c 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/ImportOrderingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/ImportOrderingRule.kt @@ -11,6 +11,7 @@ import com.pinterest.ktlint.ruleset.standard.ImportOrderingRule.Companion.ASCII_ import com.pinterest.ktlint.ruleset.standard.ImportOrderingRule.Companion.IDEA_PATTERN import com.pinterest.ktlint.ruleset.standard.internal.importordering.ImportSorter import com.pinterest.ktlint.ruleset.standard.internal.importordering.PatternEntry +import com.pinterest.ktlint.ruleset.standard.internal.importordering.parseImportsLayout import org.ec4j.core.model.PropertyType import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace @@ -39,7 +40,7 @@ public class ImportOrderingRule : Rule("import-ordering"), UsesEditorConfigProperties { - private lateinit var importsLayout: String + private lateinit var importsLayout: List private lateinit var importSorter: ImportSorter public companion object { @@ -54,7 +55,7 @@ public class ImportOrderingRule : * * https://developer.android.com/kotlin/style-guide#import_statements */ - private const val ASCII_PATTERN = "*" + private val ASCII_PATTERN = parseImportsLayout("*") /** * Default IntelliJ IDEA style: Alphabetical with capital letters before lower case letters (e.g. Z before a), @@ -64,7 +65,7 @@ public class ImportOrderingRule : * * https://github.com/JetBrains/kotlin/blob/ffdab473e28d0d872136b910eb2e0f4beea2e19c/idea/formatter/src/org/jetbrains/kotlin/idea/core/formatter/KotlinCodeStyleSettings.java#L87-L91 */ - private const val IDEA_PATTERN = "*,java.**,javax.**,kotlin.**,^" + private val IDEA_PATTERN = parseImportsLayout("*,java.**,javax.**,kotlin.**,^") private const val IDEA_ERROR_MESSAGE = "Imports must be ordered in lexicographic order without any empty lines in-between " + "with \"java\", \"javax\", \"kotlin\" and aliases in the end" @@ -76,7 +77,7 @@ public class ImportOrderingRule : ASCII_PATTERN to ASCII_ERROR_MESSAGE ) - private val editorConfigPropertyParser: (String, String?) -> PropertyType.PropertyValue = + private val editorConfigPropertyParser: (String, String?) -> PropertyType.PropertyValue> = { _, value -> when { value.isNullOrBlank() -> PropertyType.PropertyValue.invalid( @@ -91,15 +92,22 @@ public class ImportOrderingRule : value, ASCII_PATTERN ) - else -> PropertyType.PropertyValue.valid( - value, - value - ) + else -> try { + PropertyType.PropertyValue.valid( + value, + parseImportsLayout(value) + ) + } catch (e: IllegalArgumentException) { + PropertyType.PropertyValue.invalid( + value, + "Unexpected imports layout: $value" + ) + } } } internal val ktlintCustomImportsLayoutProperty = - UsesEditorConfigProperties.EditorConfigProperty( + UsesEditorConfigProperties.EditorConfigProperty>( type = PropertyType( KTLINT_CUSTOM_IMPORTS_LAYOUT_PROPERTY_NAME, PROPERTY_DESCRIPTION, @@ -110,7 +118,7 @@ public class ImportOrderingRule : ) internal val ideaImportsLayoutProperty = - UsesEditorConfigProperties.EditorConfigProperty( + UsesEditorConfigProperties.EditorConfigProperty>( type = PropertyType( IDEA_IMPORTS_LAYOUT_PROPERTY_NAME, PROPERTY_DESCRIPTION, @@ -208,7 +216,7 @@ public class ImportOrderingRule : private fun EditorConfigProperties.resolveImportsLayout( android: Boolean - ): String = if (containsKey(KTLINT_CUSTOM_IMPORTS_LAYOUT_PROPERTY_NAME)) { + ): List = if (containsKey(KTLINT_CUSTOM_IMPORTS_LAYOUT_PROPERTY_NAME)) { getEditorConfigValue(ktlintCustomImportsLayoutProperty, android) } else { getEditorConfigValue(ideaImportsLayoutProperty, android) diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/internal/importordering/ImportSorter.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/internal/importordering/ImportSorter.kt index 271961f3d7..64ed94f078 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/internal/importordering/ImportSorter.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/internal/importordering/ImportSorter.kt @@ -8,9 +8,9 @@ import org.jetbrains.kotlin.resolve.ImportPath * * Adopted from https://github.com/JetBrains/kotlin/blob/a270ee094c4d7b9520e0898a242bb6ce4dfcad7b/idea/src/org/jetbrains/kotlin/idea/util/ImportPathComparator.kt#L15 */ -internal class ImportSorter(importsLayout: String) : Comparator { - - val patterns: List = parseImportsLayout(importsLayout) +internal class ImportSorter( + val patterns: List +) : Comparator { override fun compare(import1: KtImportDirective, import2: KtImportDirective): Int { val importPath1 = import1.importPath!! From 9c49a3068e494f629238554bf4791786d18263d5 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Wed, 16 Dec 2020 10:06:04 +0100 Subject: [PATCH 06/53] Introduce new writeEditorConfigProperty method and use it for import-ordering --- .../core/api/UsesEditorConfigProperties.kt | 16 +++++++++++++++- .../core/internal/EditorConfigGenerator.kt | 4 ++-- .../ruleset/standard/ImportOrderingRule.kt | 6 ++++-- .../ImportOrderingEditorconfigTest.kt | 2 +- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/api/UsesEditorConfigProperties.kt b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/api/UsesEditorConfigProperties.kt index 0b5691f972..990e820800 100644 --- a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/api/UsesEditorConfigProperties.kt +++ b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/api/UsesEditorConfigProperties.kt @@ -39,6 +39,16 @@ public interface UsesEditorConfigProperties { ?: if (isAndroidCodeStyle) property.defaultAndroidValue else property.defaultValue } + /** + * Write the string representation of [EditorConfigProperty] + */ + public fun EditorConfigProperties.writeEditorConfigProperty( + property: EditorConfigProperty, + isAndroidCodeStyle: Boolean + ): String { + return property.propertyWriter(getEditorConfigValue(property, isAndroidCodeStyle)) + } + /** * Supported `.editorconfig` property. * @@ -46,11 +56,15 @@ public interface UsesEditorConfigProperties { * @param defaultValue default value for property if it does not exist in loaded properties. * @param defaultAndroidValue default value for android codestyle. You should set different value only when it * differs from [defaultValue]. + * @param propertyWriter custom function that represents [T] as String. Defaults to the standard `toString()` call. + * You should override the default implementation in case you need a different behavior than the standard `toString()` + * (e.g. for collections joinToString() is more applicable). */ public data class EditorConfigProperty( public val type: PropertyType, public val defaultValue: T, - public val defaultAndroidValue: T = defaultValue + public val defaultAndroidValue: T = defaultValue, + public val propertyWriter: (T) -> String = { it.toString() } ) } diff --git a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/EditorConfigGenerator.kt b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/EditorConfigGenerator.kt index 67d2013551..df4059ad2c 100644 --- a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/EditorConfigGenerator.kt +++ b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/EditorConfigGenerator.kt @@ -45,10 +45,10 @@ internal class EditorConfigGenerator( rule.editorConfigProperties.forEach { prop -> if (debug) println("Setting '${prop.type.name}' property value") acc[prop.type.name] = with(rule) { - editorConfig.getEditorConfigValue( + editorConfig.writeEditorConfigProperty( prop, isAndroidCodeStyle - ).toString() + ) } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/ImportOrderingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/ImportOrderingRule.kt index 87b43f579c..29ff1999b0 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/ImportOrderingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/ImportOrderingRule.kt @@ -114,7 +114,8 @@ public class ImportOrderingRule : editorConfigPropertyParser ), defaultValue = IDEA_PATTERN, - defaultAndroidValue = ASCII_PATTERN + defaultAndroidValue = ASCII_PATTERN, + propertyWriter = { it.joinToString(separator = ",") } ) internal val ideaImportsLayoutProperty = @@ -125,7 +126,8 @@ public class ImportOrderingRule : editorConfigPropertyParser ), defaultValue = IDEA_PATTERN, - defaultAndroidValue = ASCII_PATTERN + defaultAndroidValue = ASCII_PATTERN, + propertyWriter = { it.joinToString(separator = ",") } ) } diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/importordering/ImportOrderingEditorconfigTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/importordering/ImportOrderingEditorconfigTest.kt index b94fab2a3f..e05814384b 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/importordering/ImportOrderingEditorconfigTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/importordering/ImportOrderingEditorconfigTest.kt @@ -14,7 +14,7 @@ class ImportOrderingEditorconfigTest { val properties: EditorConfigProperties = emptyMap() val rule = ImportOrderingRule() with(rule) { - val raw = properties.getEditorConfigValue(ImportOrderingRule.ideaImportsLayoutProperty).toString() + val raw = properties.writeEditorConfigProperty(ImportOrderingRule.ideaImportsLayoutProperty, false) assertThat(raw).isEqualTo("*,java.**,javax.**,kotlin.**,^") } } From 8ed0710a32a6515e343e143dd0c90a636a6048fa Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Wed, 16 Dec 2020 21:25:52 +0100 Subject: [PATCH 07/53] Always pass file path via user data properties. Previously, when ktlint failed to find '.editorconfig', file path was not passed to the rules. This lead for wrong "filename" rule behaviour. --- .../com/pinterest/ktlint/core/KtLint.kt | 16 +++++--- .../core/internal/EditorConfigLoader.kt | 27 +++--------- .../core/internal/EditorConfigLoaderTest.kt | 41 +++++++------------ .../ruleset/standard/FilenameRuleTest.kt | 18 ++++---- .../internal/IntellijIDEAIntegration.kt | 5 +-- 5 files changed, 41 insertions(+), 66 deletions(-) diff --git a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/KtLint.kt b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/KtLint.kt index 0d179a4765..9cb236d8fe 100644 --- a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/KtLint.kt +++ b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/KtLint.kt @@ -29,6 +29,7 @@ public object KtLint { public val EDITOR_CONFIG_USER_DATA_KEY: Key = Key("EDITOR_CONFIG") public val ANDROID_USER_DATA_KEY: Key = Key("ANDROID") public val FILE_PATH_USER_DATA_KEY: Key = Key("FILE_PATH") + private const val FILE_PATH_PROPERTY = "file_path" public val EDITOR_CONFIG_PROPERTIES_USER_DATA_KEY: Key = Key("EDITOR_CONFIG_PROPERTIES") public val DISABLED_RULES: Key> = Key>("DISABLED_RULES") @@ -145,10 +146,15 @@ public object KtLint { ) // Passed-in userData overrides .editorconfig - val mergedUserData = editorConfigProperties.convertToRawValues( - params.normalizedFilePath, - params.isStdIn - ) + params.userData + val mergedUserData = editorConfigProperties + .convertToRawValues() + params.userData + .run { + if (!params.isStdIn) { + plus(FILE_PATH_PROPERTY to params.normalizedFilePath.toString()) + } else { + this + } + } injectUserData(rootNode, editorConfigProperties, mergedUserData) @@ -188,7 +194,7 @@ public object KtLint { } else { userData } - node.putUserData(FILE_PATH_USER_DATA_KEY, userData["file_path"]) + node.putUserData(FILE_PATH_USER_DATA_KEY, userData[FILE_PATH_PROPERTY]) node.putUserData(EDITOR_CONFIG_USER_DATA_KEY, EditorConfig.fromMap(editorConfigMap - "android" - "file_path")) node.putUserData(EDITOR_CONFIG_PROPERTIES_USER_DATA_KEY, editorConfigProperties) node.putUserData(ANDROID_USER_DATA_KEY, android) diff --git a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/EditorConfigLoader.kt b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/EditorConfigLoader.kt index 82f5a59ca5..ab6ebb7153 100644 --- a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/EditorConfigLoader.kt +++ b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/EditorConfigLoader.kt @@ -121,7 +121,6 @@ class EditorConfigLoader( } companion object { - internal const val FILE_PATH_PROPERTY = "file_path" /** * List of file extensions, editorconfig lookup will be performed. */ @@ -131,31 +130,17 @@ class EditorConfigLoader( ) /** - * Converts loaded [editorConfigProperties] values to string representation. + * Converts loaded [EditorConfigProperties] values into string representation. * - * @param filePath added under [FILE_PATH_PROPERTY] key - * @param isStdIn indicate that input is from std-in and [filePath] should not be added to the map - * - * @return converted [editorConfigProperties] + * @return map of key as string and value as string property representation */ - fun EditorConfigProperties.convertToRawValues( - filePath: Path?, - isStdIn: Boolean = false - ): Map { + fun EditorConfigProperties.convertToRawValues(): Map { return if (isEmpty()) { emptyMap() } else { - this - .mapValues { - if (it.value.isUnset) "unset" else it.value.sourceValue - } - .run { - if (!isStdIn) { - plus(FILE_PATH_PROPERTY to filePath.toString()) - } else { - this - } - } + mapValues { + if (it.value.isUnset) "unset" else it.value.sourceValue + } } } } diff --git a/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigLoaderTest.kt b/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigLoaderTest.kt index 9808342d44..9c8cf569c9 100644 --- a/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigLoaderTest.kt +++ b/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigLoaderTest.kt @@ -74,7 +74,7 @@ internal class EditorConfigLoaderTest { val lintFile = tempFileSystem.normalizedPath(projectDir).resolve("test.kt") val editorConfig = editorConfigLoader.loadPropertiesForFile(lintFile, rules = rules) - val parsedEditorConfig = editorConfig.convertToRawValues(lintFile) + val parsedEditorConfig = editorConfig.convertToRawValues() assertThat(parsedEditorConfig).isNotEmpty assertThat(parsedEditorConfig) @@ -86,7 +86,6 @@ internal class EditorConfigLoaderTest { mapOf( "indent_size" to "2", "tab_width" to "2", - EditorConfigLoader.FILE_PATH_PROPERTY to lintFile.toString() ) ) } @@ -130,38 +129,35 @@ internal class EditorConfigLoaderTest { val lintFileSubdirectory = tempFileSystem.normalizedPath(project1Subdirectory).resolve("test.kt") var editorConfigProperties = editorConfigLoader.loadPropertiesForFile(lintFileSubdirectory, rules = rules) - var parsedEditorConfig = editorConfigProperties.convertToRawValues(lintFileSubdirectory) + var parsedEditorConfig = editorConfigProperties.convertToRawValues() assertThat(parsedEditorConfig).isEqualTo( mapOf( "indent_size" to "2", "tab_width" to "2", "indent_style" to "space", - EditorConfigLoader.FILE_PATH_PROPERTY to lintFileSubdirectory.toString() ) ) val lintFileMainDir = tempFileSystem.normalizedPath(project1Dir).resolve("test.kts") editorConfigProperties = editorConfigLoader.loadPropertiesForFile(lintFileMainDir, rules = rules) - parsedEditorConfig = editorConfigProperties.convertToRawValues(lintFileMainDir) + parsedEditorConfig = editorConfigProperties.convertToRawValues() assertThat(parsedEditorConfig).isEqualTo( mapOf( "indent_size" to "4", "tab_width" to "4", "indent_style" to "space", - EditorConfigLoader.FILE_PATH_PROPERTY to lintFileMainDir.toString() ) ) val lintFileRoot = tempFileSystem.normalizedPath(rootDir).resolve("test.kt") editorConfigProperties = editorConfigLoader.loadPropertiesForFile(lintFileRoot, rules = rules) - parsedEditorConfig = editorConfigProperties.convertToRawValues(lintFileRoot) + parsedEditorConfig = editorConfigProperties.convertToRawValues() assertThat(parsedEditorConfig).isEqualTo( mapOf( "end_of_line" to "lf", - EditorConfigLoader.FILE_PATH_PROPERTY to lintFileRoot.toString() ) ) } @@ -179,14 +175,13 @@ internal class EditorConfigLoaderTest { val lintFile = tempFileSystem.normalizedPath(projectDir).resolve("test.kt") val editorConfigProperties = editorConfigLoader.loadPropertiesForFile(lintFile, rules = rules) - val parsedEditorConfig = editorConfigProperties.convertToRawValues(lintFile) + val parsedEditorConfig = editorConfigProperties.convertToRawValues() assertThat(parsedEditorConfig).isNotEmpty assertThat(parsedEditorConfig).isEqualTo( mapOf( "insert_final_newline" to "true", "disabled_rules" to "import-ordering", - EditorConfigLoader.FILE_PATH_PROPERTY to lintFile.toString() ) ) } @@ -203,14 +198,13 @@ internal class EditorConfigLoaderTest { val lintFile = tempFileSystem.normalizedPath(projectDir).resolve("test.kt") val editorConfigProperties = editorConfigLoader.loadPropertiesForFile(lintFile, rules = rules) - val parsedEditorConfig = editorConfigProperties.convertToRawValues(lintFile) + val parsedEditorConfig = editorConfigProperties.convertToRawValues() assertThat(parsedEditorConfig).isNotEmpty assertThat(parsedEditorConfig).isEqualTo( mapOf( "indent_size" to "unset", "tab_width" to "unset", - EditorConfigLoader.FILE_PATH_PROPERTY to lintFile.toString() ) ) } @@ -227,13 +221,12 @@ internal class EditorConfigLoaderTest { val lintFile = tempFileSystem.normalizedPath(projectDir).resolve("test.kts") val editorConfigProperties = editorConfigLoader.loadPropertiesForFile(lintFile, rules = rules) - val parsedEditorConfig = editorConfigProperties.convertToRawValues(lintFile) + val parsedEditorConfig = editorConfigProperties.convertToRawValues() assertThat(parsedEditorConfig).isNotEmpty assertThat(parsedEditorConfig).isEqualTo( mapOf( "disabled_rules" to "import-ordering, no-wildcard-imports", - EditorConfigLoader.FILE_PATH_PROPERTY to lintFile.toString() ) ) } @@ -251,7 +244,7 @@ internal class EditorConfigLoaderTest { val lintFile = tempFileSystem.normalizedPath(projectDir).resolve("test.txt") val editorConfigProperties = editorConfigLoader.loadPropertiesForFile(lintFile, rules = rules) - val parsedEditorConfig = editorConfigProperties.convertToRawValues(lintFile) + val parsedEditorConfig = editorConfigProperties.convertToRawValues() assertThat(parsedEditorConfig).isEmpty() } @@ -272,13 +265,9 @@ internal class EditorConfigLoaderTest { rules = rules, debug = true, ) - val parsedEditorConfig = editorConfigProperties.convertToRawValues( - null, - true - ) + val parsedEditorConfig = editorConfigProperties.convertToRawValues() assertThat(parsedEditorConfig).isNotEmpty - assertThat(parsedEditorConfig).doesNotContainKey(EditorConfigLoader.FILE_PATH_PROPERTY) assertThat(parsedEditorConfig).isEqualTo( mapOf( "insert_final_newline" to "true", @@ -329,13 +318,12 @@ internal class EditorConfigLoaderTest { alternativeEditorConfig = tempFileSystem.normalizedPath(anotherDir).resolve(".editorconfig"), rules = rules ) - val parsedEditorConfig = editorConfigProperties.convertToRawValues(lintFile) + val parsedEditorConfig = editorConfigProperties.convertToRawValues() assertThat(parsedEditorConfig).isNotEmpty assertThat(parsedEditorConfig).isEqualTo( mapOf( "end_of_line" to "lf", - EditorConfigLoader.FILE_PATH_PROPERTY to lintFile.toString(), "indent_size" to "2", "tab_width" to "2" ) @@ -372,7 +360,7 @@ internal class EditorConfigLoaderTest { isStdIn = true, rules = rules ) - val parsedEditorConfig = editorConfigProperties.convertToRawValues(null, true) + val parsedEditorConfig = editorConfigProperties.convertToRawValues() assertThat(parsedEditorConfig).isNotEmpty assertThat(parsedEditorConfig).isEqualTo( @@ -402,14 +390,13 @@ internal class EditorConfigLoaderTest { Files.createDirectories(lintFile) val editorConfigProperties = editorConfigLoader.loadPropertiesForFile(lintFile, debug = true, rules = rules) - val parsedEditorConfig = editorConfigProperties.convertToRawValues(lintFile) + val parsedEditorConfig = editorConfigProperties.convertToRawValues() assertThat(parsedEditorConfig).isNotEmpty assertThat(parsedEditorConfig).isEqualTo( mapOf( "insert_final_newline" to "true", "disabled_rules" to "class-must-be-internal", - EditorConfigLoader.FILE_PATH_PROPERTY to lintFile.toString() ) ) } @@ -421,6 +408,8 @@ internal class EditorConfigLoaderTest { node: ASTNode, autoCorrect: Boolean, emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit - ) = TODO("Not yet implemented") + ) { + throw NotImplementedError() + } } } diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/FilenameRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/FilenameRuleTest.kt index 9372cc1aa3..c528168142 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/FilenameRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/FilenameRuleTest.kt @@ -25,6 +25,7 @@ class FilenameRuleTest { ) { assertThat( FilenameRule().lint( + "/some/path/A.kt", """ /* * license @@ -35,7 +36,6 @@ class FilenameRuleTest { $src // """.trimIndent(), - fileName("/some/path/A.kt") ) ).isEmpty() } @@ -56,6 +56,7 @@ class FilenameRuleTest { ) { assertThat( FilenameRule().lint( + "/some/path/B.kt", """ /* * license @@ -66,7 +67,6 @@ class FilenameRuleTest { ${src.key} // """.trimIndent(), - fileName("/some/path/B.kt") ) ).isEqualTo( listOf( @@ -80,12 +80,12 @@ class FilenameRuleTest { fun testFileWithoutTopLevelDeclarations() { assertThat( FilenameRule().lint( + "A.kt", """ /* * copyright */ """.trimIndent(), - fileName("A.kt") ) ).isEmpty() } @@ -94,11 +94,11 @@ class FilenameRuleTest { fun testMultipleTopLevelClasses() { assertThat( FilenameRule().lint( + "A.kt", """ class B class C """.trimIndent(), - fileName("A.kt") ) ).isEmpty() } @@ -107,13 +107,13 @@ class FilenameRuleTest { fun testMultipleNonTopLevelClasses() { assertThat( FilenameRule().lint( + "A.kt", """ class B { class C class D } """.trimIndent(), - fileName("A.kt") ) ).isEqualTo( listOf( @@ -126,10 +126,10 @@ class FilenameRuleTest { fun testCaseSensitiveMatching() { assertThat( FilenameRule().lint( + "woohoo.kt", """ interface Woohoo """.trimIndent(), - fileName("woohoo.kt") ) ).isEqualTo( listOf( @@ -142,10 +142,10 @@ class FilenameRuleTest { fun testCaseEscapedClassNames() { assertThat( FilenameRule().lint( + "B.kt", """ class `A` """.trimIndent(), - fileName("B.kt") ) ).isEqualTo( listOf( @@ -158,13 +158,11 @@ class FilenameRuleTest { fun testIgnoreKotlinScriptFiles() { assertThat( FilenameRule().lint( + "A.kts", """ class B """.trimIndent(), - fileName("A.kts") ) ).isEmpty() } - - private fun fileName(fileName: String) = mapOf("file_path" to fileName) } diff --git a/ktlint/src/main/kotlin/com/pinterest/ktlint/internal/IntellijIDEAIntegration.kt b/ktlint/src/main/kotlin/com/pinterest/ktlint/internal/IntellijIDEAIntegration.kt index f2e42936f1..3c28271aca 100644 --- a/ktlint/src/main/kotlin/com/pinterest/ktlint/internal/IntellijIDEAIntegration.kt +++ b/ktlint/src/main/kotlin/com/pinterest/ktlint/internal/IntellijIDEAIntegration.kt @@ -31,10 +31,7 @@ object IntellijIDEAIntegration { } val editorConfigProperties = EditorConfigLoader(FileSystems.getDefault()) .loadPropertiesForFile(null, isStdIn = true, rules = emptySet()) - val editorConfig: Map = editorConfigProperties.convertToRawValues( - null, - isStdIn = true - ) + val editorConfig: Map = editorConfigProperties.convertToRawValues() val indentSize = editorConfig["indent_size"]?.toIntOrNull() ?: 4 val continuationIndentSize = editorConfig["continuation_indent_size"]?.toIntOrNull() ?: 4 val updates = if (local) { From 316e4076e218b5289ff9839110b21484ee673e12 Mon Sep 17 00:00:00 2001 From: Richard Wise Date: Thu, 17 Dec 2020 16:41:50 +0800 Subject: [PATCH 08/53] Add clarification for IDEA setup in README (#1017) To clarify how the IntelliJ IDEA code style setting configuration process works, add an brief explanation about what each command does and clarify that the code samples are command line calls, not config file entries --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6c58203c1d..c71e9f8c62 100644 --- a/README.md +++ b/README.md @@ -368,6 +368,9 @@ val ktlintFormat by tasks.creating(JavaExec::class) { ##### Option #1 (recommended) +To change the code style config files in a single IDEA project + +Run ktlint executable with the appropriate flag: > (inside project's root directory) ```sh @@ -378,7 +381,9 @@ ktlint --android applyToIDEAProject ##### Option #2 -Apply to all IDEA projects: +To change the code style config files for all IDEA projects + +Run ktlint executable with the appropriate flag: ```sh ktlint applyToIDEA ``` From b7cb8f8c5d0e96aed81378f1440e094782855955 Mon Sep 17 00:00:00 2001 From: Toshiaki Kameyama Date: Thu, 17 Dec 2020 17:42:27 +0900 Subject: [PATCH 09/53] ArgumentListWrappingRule: fix false positive when max_line_length in EditorConfig is set (#1010) --- .../experimental/ArgumentListWrappingRule.kt | 2 +- .../ArgumentListWrappingRuleTest.kt | 26 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRule.kt b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRule.kt index 756933c1e7..ea0366b045 100644 --- a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRule.kt +++ b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRule.kt @@ -69,7 +69,7 @@ class ArgumentListWrappingRule : Rule("argument-list-wrapping") { val putArgumentsOnSeparateLines = node.textContainsIgnoringLambda('\n') || // max_line_length exceeded - maxLineLength > -1 && (node.column - 1 + node.textLength) > maxLineLength + maxLineLength > -1 && (node.column - 1 + node.textLength) > maxLineLength && !node.textContains('\n') if (putArgumentsOnSeparateLines) { // IDEA quirk: // generic< diff --git a/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRuleTest.kt b/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRuleTest.kt index b0252bff95..f6bcbbbf55 100644 --- a/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRuleTest.kt +++ b/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRuleTest.kt @@ -166,6 +166,32 @@ class ArgumentListWrappingRuleTest { ).isEmpty() } + @Test + fun testLambdaArgumentsAreIgnoredWithMaxLineLength() { + assertThat( + ArgumentListWrappingRule().lint( + """ + abstract class A(init: String.() -> Int) + class B : A({ + toInt() + toInt() + toInt() + toInt() + toInt() + toInt() + }) + + fun test(a: Any, b: (Any) -> Any) { + test(a = "1", b = { + it.toString() + }) + } + """.trimIndent(), + userData = mapOf("max_line_length" to "80") + ) + ).isEmpty() + } + @Test fun testFormatWithLambdaArguments() { assertThat( From 3f5b5586508a20f56bea0efd184ebf5b66c70646 Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Thu, 17 Dec 2020 22:17:16 +0100 Subject: [PATCH 10/53] Add kotlinlang slack #ktlint channel badge. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c71e9f8c62..f605eb266e 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@

+Join the chat at https://kotlinlang.slack.com Build status Maven Central ktlint From 90021a9b01164af391d8dbfb369fd676926e70fb Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Thu, 17 Dec 2020 22:29:22 +0100 Subject: [PATCH 11/53] Update changelog. --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b1e6f4f964..523f8361bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,9 +6,15 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Added ### Fixed +- Incorrect indentation with multiple interfaces ([#1003](https://github.com/pinterest/ktlint/issues/1003)) +- Empty line before primary constructor is not reported and formatted-out ([#1004](https://github.com/pinterest/ktlint/issues/1004)) +- Fix '.editorconfig' generation for "import-ordering" rule ([#1011](https://github.com/pinterest/ktlint/issues/1004)) +- Fix "filename" rule will not work when '.editorconfig' file is not found ([#997](https://github.com/pinterest/ktlint/issues/1004)) ### Changed - Update Gradle shadow plugin to `6.1.0` version +- Align with Kotlin plugin on how alias pattern is represented for imports layout rule ([#753](https://github.com/pinterest/ktlint/issues/753)) +- Align with Kotlin plugin on how subpackages are represented ([#753](https://github.com/pinterest/ktlint/issues/753)) ### Removed From dfe2dba0f98ae519ec44421a6ae956405ff40a7d Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Fri, 18 Dec 2020 17:21:00 +0100 Subject: [PATCH 12/53] Deprecate shorcuts for import-ordering (#1018) * Deprecate shorcuts for import-ordering * Update CHANGELOG.md --- .editorconfig | 2 +- CHANGELOG.md | 5 +++ README.md | 15 +++---- .../ruleset/standard/ImportOrderingRule.kt | 41 +++++++++++++------ .../ImportOrderingRuleAsciiTest.kt | 2 +- .../ImportOrderingRuleIdeaTest.kt | 2 +- 6 files changed, 42 insertions(+), 25 deletions(-) diff --git a/.editorconfig b/.editorconfig index eed8eb0a72..e9e3a7d861 100644 --- a/.editorconfig +++ b/.editorconfig @@ -14,7 +14,7 @@ insert_final_newline = true indent_size = 4 [*.{kt,kts}] -kotlin_imports_layout=ascii +ij_kotlin_imports_layout=* [{Makefile,*.go}] indent_style = tab diff --git a/CHANGELOG.md b/CHANGELOG.md index 523f8361bd..f1a9a8610d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,11 +10,16 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Empty line before primary constructor is not reported and formatted-out ([#1004](https://github.com/pinterest/ktlint/issues/1004)) - Fix '.editorconfig' generation for "import-ordering" rule ([#1011](https://github.com/pinterest/ktlint/issues/1004)) - Fix "filename" rule will not work when '.editorconfig' file is not found ([#997](https://github.com/pinterest/ktlint/issues/1004)) +- EditorConfig generation for `import-ordering` ([#1011](https://github.com/pinterest/ktlint/pull/1011)) ### Changed - Update Gradle shadow plugin to `6.1.0` version - Align with Kotlin plugin on how alias pattern is represented for imports layout rule ([#753](https://github.com/pinterest/ktlint/issues/753)) - Align with Kotlin plugin on how subpackages are represented ([#753](https://github.com/pinterest/ktlint/issues/753)) +- Deprecated custom `kotlin_imports_layout` EditorConfig property. Please use `ij_kotlin_imports_layout` to ensure + that the Kotlin IDE plugin and ktlint use same imports layout ([#753](https://github.com/pinterest/ktlint/issues/753)) +- Deprecated `idea` and `ascii` shortcuts as the `ij_kotlin_imports_layout` property does not support those. + Please check README on how to achieve those with patterns ([#753](https://github.com/pinterest/ktlint/issues/753)) ### Removed diff --git a/README.md b/README.md index f605eb266e..d8eea85eb6 100644 --- a/README.md +++ b/README.md @@ -90,20 +90,15 @@ max_line_length=off # by the ruleset identifier. disabled_rules=no-wildcard-imports,experimental:annotation,my-custom-ruleset:my-custom-rule -# Defines the imports layout. There are predefined layouts like "ascii" or "idea", as well as a custom layout. -# The predefined layouts are temporary and will be deprecated in the future, once Kotlin plugin supports EditorConfig property for imports layout. -# The custom layout can be composed by the following symbols: +# Defines the imports layout. The layout can be composed by the following symbols: # "*" - wildcard. There must be at least one entry of a single wildcard to match all other imports. Matches anything after a specified symbol/import as well. # "|" - blank line. Supports only single blank lines between imports. No blank line is allowed in the beginning or end of the layout. # "^" - alias import, e.g. "^android.*" will match all android alias imports, "^" will match all other alias imports. # import paths - these can be full paths, e.g. "java.util.List.*" as well as wildcard paths, e.g. "kotlin.**" -# Examples: -kotlin_imports_layout=ascii # alphabetical with capital letters before lower case letters (e.g. Z before a), no blank lines -kotlin_imports_layout=idea # default IntelliJ IDEA style, same as "ascii", but with "java", "javax", "kotlin" and alias imports in the end of the imports list -kotlin_imports_layout=android.**,|,^org.junit.**,kotlin.io.Closeable.*,|,**,^ # custom imports layout -# Alternatively ij_kotlin_imports_layout name can be used, in order to set an imports layout for both ktlint and IDEA via a single property -# Note: this is not yet implemented on IDEA side, so it only takes effect for ktlint -ij_kotlin_imports_layout=* +# Examples (we use ij_kotlin_imports_layout to set an imports layout for both ktlint and IDEA via a single property): +ij_kotlin_imports_layout=* # alphabetical with capital letters before lower case letters (e.g. Z before a), no blank lines +ij_kotlin_imports_layout=*,java.**,javax.**,kotlin.**,^ # default IntelliJ IDEA style, same as alphabetical, but with "java", "javax", "kotlin" and alias imports in the end of the imports list +ij_kotlin_imports_layout=android.**,|,^org.junit.**,kotlin.io.Closeable.*,|,*,^ # custom imports layout ``` ### Overriding Editorconfig properties for specific directories diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/ImportOrderingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/ImportOrderingRule.kt index 29ff1999b0..2cf507968a 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/ImportOrderingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/ImportOrderingRule.kt @@ -19,9 +19,9 @@ import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl import org.jetbrains.kotlin.psi.KtImportDirective /** - * Import ordering is configured via EditorConfig's custom property `kotlin_imports_layout`. Supported values: - * * "idea" - default IntelliJ IDEA's order, see [IDEA_PATTERN] - * * "ascii" - alphabetical order as recommended in Android's Kotlin style guide, see [ASCII_PATTERN] + * Import ordering is configured via EditorConfig's property `ij_kotlin_imports_layout`, so the Kotlin IDE plugin also recongizes it. Supported values: + * * "*,java.**,javax.**,kotlin.**,^" - default IntelliJ IDEA's order, see [IDEA_PATTERN] + * * "*" - alphabetical order as recommended in Android's Kotlin style guide, see [ASCII_PATTERN] * * custom - defined by the following set of tokens. Tokens can be combined together in a group, groups/tokens must be comma separated: * * "*" - wildcard symbol, can be used as follows: * 1. Single, meaning matching any import ( in IDEA) @@ -33,7 +33,7 @@ import org.jetbrains.kotlin.psi.KtImportDirective * 2. Alone, meaning matching any alias import - "^" ( in IDEA) * * import paths - these can be full paths, e.g. "java.util.List.*" as well as wildcard paths meaning "with subpackages", e.g. "kotlin.**" * - * In case the custom property is not provided, the rule defaults to "ascii" style in case of "android" flag supplied, or to "idea" otherwise. + * In case the custom property is not provided, the rule defaults to alphabetical order in case of "android" flag supplied, or to idea otherwise. */ @OptIn(FeatureInAlphaState::class) public class ImportOrderingRule : @@ -84,14 +84,26 @@ public class ImportOrderingRule : value, "Import layout must contain at least one entry of a wildcard symbol (*)" ) - value == "idea" -> PropertyType.PropertyValue.valid( - value, - IDEA_PATTERN - ) - value == "ascii" -> PropertyType.PropertyValue.valid( - value, - ASCII_PATTERN - ) + value == "idea" -> { + println( + "[WARNING] `idea` is deprecated! Please use `*,java.**,javax.**,kotlin.**,^` instead" + + " to ensure that the Kotlin IDE plugin recognizes the value" + ) + PropertyType.PropertyValue.valid( + value, + IDEA_PATTERN + ) + } + value == "ascii" -> { + println( + "[WARNING] `ascii` is deprecated! Please use `*` instead" + + " to ensure that the Kotlin IDE plugin recognizes the value" + ) + PropertyType.PropertyValue.valid( + value, + ASCII_PATTERN + ) + } else -> try { PropertyType.PropertyValue.valid( value, @@ -106,6 +118,7 @@ public class ImportOrderingRule : } } + @Deprecated("This custom property is deprecated in favor of IDEA's default ideaImportsLayoutProperty") internal val ktlintCustomImportsLayoutProperty = UsesEditorConfigProperties.EditorConfigProperty>( type = PropertyType( @@ -219,6 +232,10 @@ public class ImportOrderingRule : private fun EditorConfigProperties.resolveImportsLayout( android: Boolean ): List = if (containsKey(KTLINT_CUSTOM_IMPORTS_LAYOUT_PROPERTY_NAME)) { + println( + "[WARNING] `kotlin_imports_layout` is deprecated! Please use `ij_kotlin_imports_layout` to ensure" + + " that the Kotlin IDE plugin and ktlint use same imports layout" + ) getEditorConfigValue(ktlintCustomImportsLayoutProperty, android) } else { getEditorConfigValue(ideaImportsLayoutProperty, android) diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/importordering/ImportOrderingRuleAsciiTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/importordering/ImportOrderingRuleAsciiTest.kt index bc54401a34..6a05aecbb0 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/importordering/ImportOrderingRuleAsciiTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/importordering/ImportOrderingRuleAsciiTest.kt @@ -297,7 +297,7 @@ class ImportOrderingRuleAsciiTest { private fun writeAsciiImportsOrderingConfig() = editorConfigTestRule .writeToEditorConfig( mapOf( - ImportOrderingRule.ideaImportsLayoutProperty.type to "ascii" + ImportOrderingRule.ideaImportsLayoutProperty.type to "*" ) ) .absolutePath diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/importordering/ImportOrderingRuleIdeaTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/importordering/ImportOrderingRuleIdeaTest.kt index 3ed0808084..9c16d6f69a 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/importordering/ImportOrderingRuleIdeaTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/importordering/ImportOrderingRuleIdeaTest.kt @@ -353,7 +353,7 @@ class ImportOrderingRuleIdeaTest { private fun writeIdeaImportsOrderingConfig() = editorConfigTestRule .writeToEditorConfig( mapOf( - ImportOrderingRule.ideaImportsLayoutProperty.type to "idea" + ImportOrderingRule.ideaImportsLayoutProperty.type to "*,java.**,javax.**,kotlin.**,^" ) ) .absolutePath From 7f17da80f9f7064190e78897bd7d8fbf961fb911 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Fri, 18 Dec 2020 22:22:43 +0100 Subject: [PATCH 13/53] Add test for #877 (#1021) --- CHANGELOG.md | 2 +- .../ruleset/standard/IndentationRuleTest.kt | 26 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f1a9a8610d..7338cd0117 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,7 +48,7 @@ Special thanks to [t-kameyama](https://github.com/t-kameyama) for the huge numbe - Correctly indent primary constructor parameters when class has multiline type parameter (`parameter-list-wrapping`) ([#921](https://github.com/pinterest/ktlint/issues/921)) ([#938](https://github.com/pinterest/ktlint/issues/938)) - Correctly indent property delegates (`indent`) ([#939](https://github.com/pinterest/ktlint/issues/939)) - Fix false positive for semicolon between empty enum entry and member (`no-semicolons`) ([#957](https://github.com/pinterest/ktlint/issues/957)) -- Fix wrong indentation for class delegates (`indent`) ([#960](https://github.com/pinterest/ktlint/issues/960)) ([#963](https://github.com/pinterest/ktlint/issues/963)) +- Fix wrong indentation for class delegates (`indent`) ([#960](https://github.com/pinterest/ktlint/issues/960)) ([#963](https://github.com/pinterest/ktlint/issues/963)) ([#877](https://github.com/pinterest/ktlint/issues/877)) - Fix wrong indentation in named arguments (`indent`) ([#964](https://github.com/pinterest/ktlint/issues/964)) - Fix wrong indentation when a function has multiline type arguments (`parameter-list-wrapping`) ([#965](https://github.com/pinterest/ktlint/issues/965)) - Fix false positive for `spacing-between-declarations-with-annotations` ([#970](https://github.com/pinterest/ktlint/issues/970)) diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/IndentationRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/IndentationRuleTest.kt index 539b3e1bf6..6383546aa9 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/IndentationRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/IndentationRuleTest.kt @@ -727,6 +727,32 @@ internal class IndentationRuleTest { ).isEmpty() } + @Test + fun `lint delegation 6`() { + assertThat( + IndentationRule().lint( + """ + data class Shortcut(val id: String, val url: String) + + object Someclass : List by listOf( + Shortcut( + id = "1", + url = "url" + ), + Shortcut( + id = "2", + url = "asd" + ), + Shortcut( + id = "3", + url = "TV" + ) + ) + """.trimIndent() + ) + ).isEmpty() + } + @Test fun `lint named argument`() { assertThat( From 81481f1568ba210fea51470bc2fa6a61b1365e70 Mon Sep 17 00:00:00 2001 From: paul-dingemans Date: Sun, 20 Dec 2020 21:26:56 +0100 Subject: [PATCH 14/53] Retain the UTF8 BOM (Byte Order Mark) in case present in original code (#1027) Solves #1013 Co-authored-by: Paul Dingemans --- .../com/pinterest/ktlint/core/KtLint.kt | 14 +++++--- .../com/pinterest/ktlint/core/KtLintTest.kt | 33 +++++++++++++++++++ .../resources/spec/format-unicode-bom.kt.spec | 1 + 3 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 ktlint-core/src/test/resources/spec/format-unicode-bom.kt.spec diff --git a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/KtLint.kt b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/KtLint.kt index 9cb236d8fe..ea1781d409 100644 --- a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/KtLint.kt +++ b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/KtLint.kt @@ -357,11 +357,15 @@ public object KtLint { return params.text } - return if (hasUTF8BOM) UTF8_BOM else "" + // Restore UTF8 BOM if it was present - preparedCode - .rootNode - .text - .replace("\n", determineLineSeparator(params.text, params.userData)) + val code = preparedCode + .rootNode + .text + .replace("\n", determineLineSeparator(params.text, params.userData)) + return if (hasUTF8BOM) { + UTF8_BOM + code + } else { + code + } } /** diff --git a/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/KtLintTest.kt b/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/KtLintTest.kt index 46bc6f4698..1b24197d7a 100644 --- a/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/KtLintTest.kt +++ b/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/KtLintTest.kt @@ -42,4 +42,37 @@ class KtLintTest { ) assertThat(bus).isEqualTo(listOf("file:a", "file:b", "file:c", "b", "c", "file:d")) } + + @Test + fun testFormatUnicodeBom() { + val code = getResourceAsText("spec/format-unicode-bom.kt.spec") + + val actual = KtLint.format( + KtLint.Params( + text = code, + ruleSets = listOf( + RuleSet("standard", DummyRule()) + ), + cb = { _, _ -> } + ) + ) + + assertThat(actual).isEqualTo(code) + } } + +class DummyRule : Rule("dummy-rule") { + override fun visit( + node: ASTNode, + autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit + ) { + // The rule does not need to do anything except emit + emit(node.startOffset, "Dummy rule", true) + } +} + +private fun getResourceAsText(path: String) = + (ClassLoader.getSystemClassLoader().getResourceAsStream(path) ?: throw RuntimeException("$path not found")) + .bufferedReader() + .readText() diff --git a/ktlint-core/src/test/resources/spec/format-unicode-bom.kt.spec b/ktlint-core/src/test/resources/spec/format-unicode-bom.kt.spec new file mode 100644 index 0000000000..bee4031d29 --- /dev/null +++ b/ktlint-core/src/test/resources/spec/format-unicode-bom.kt.spec @@ -0,0 +1 @@ +// Although probably not visible in your editor, this file starts with a UTF8 BOM unicode character From ff49b9b5fd5a90553ad584df428e3fe8c93f2705 Mon Sep 17 00:00:00 2001 From: paul-dingemans Date: Mon, 21 Dec 2020 23:34:30 +0100 Subject: [PATCH 15/53] Fix string-template for dot-expression from which the toString() is removed (#1026) * Fix string-template for dot-expression of which the redundant toString is removed (#996) The no-unused-imports rule fails on the existence of the dot-expression node but for which the actual call to the toString method was removed. * Update changelog * Fix code style violation Co-authored-by: Paul Dingemans Co-authored-by: Roman Zavarnitsyn --- CHANGELOG.md | 1 + .../ruleset/standard/StringTemplateRule.kt | 19 +++++++++++++++--- .../standard/StringTemplateRuleTest.kt | 20 +++++++++++++++++++ .../string-template/format-expected.kt.spec | 1 + .../spec/string-template/format.kt.spec | 1 + .../spec/string-template/lint.kt.spec | 12 ++++++----- 6 files changed, 46 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7338cd0117..86173c6c50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Fix '.editorconfig' generation for "import-ordering" rule ([#1011](https://github.com/pinterest/ktlint/issues/1004)) - Fix "filename" rule will not work when '.editorconfig' file is not found ([#997](https://github.com/pinterest/ktlint/issues/1004)) - EditorConfig generation for `import-ordering` ([#1011](https://github.com/pinterest/ktlint/pull/1011)) +- Internal error (`no-unused-imports`) ([#996](https://github.com/pinterest/ktlint/issues/996)) ### Changed - Update Gradle shadow plugin to `6.1.0` version diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/StringTemplateRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/StringTemplateRule.kt index 27b779f99a..7577763f74 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/StringTemplateRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/StringTemplateRule.kt @@ -6,6 +6,9 @@ import com.pinterest.ktlint.core.ast.ElementType.DOT import com.pinterest.ktlint.core.ast.ElementType.DOT_QUALIFIED_EXPRESSION import com.pinterest.ktlint.core.ast.ElementType.LITERAL_STRING_TEMPLATE_ENTRY import com.pinterest.ktlint.core.ast.ElementType.LONG_STRING_TEMPLATE_ENTRY +import com.pinterest.ktlint.core.ast.ElementType.LONG_TEMPLATE_ENTRY_END +import com.pinterest.ktlint.core.ast.ElementType.LONG_TEMPLATE_ENTRY_START +import com.pinterest.ktlint.core.ast.ElementType.REGULAR_STRING_PART import com.pinterest.ktlint.core.ast.ElementType.SUPER_EXPRESSION import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement @@ -65,9 +68,19 @@ class StringTemplateRule : Rule("string-template") { ) { emit(node.treePrev.startOffset + 2, "Redundant curly braces", true) if (autoCorrect) { - // fixme: a proper way would be to downcast to SHORT_STRING_TEMPLATE_ENTRY - (node.firstChildNode as LeafPsiElement).rawReplaceWithText("$") // entry start - (node.lastChildNode as LeafPsiElement).rawReplaceWithText("") // entry end + val leftCurlyBraceNode = node.findChildByType(LONG_TEMPLATE_ENTRY_START) + val rightCurlyBraceNode = node.findChildByType(LONG_TEMPLATE_ENTRY_END) + if (leftCurlyBraceNode != null && rightCurlyBraceNode != null) { + node.removeChild(leftCurlyBraceNode) + node.removeChild(rightCurlyBraceNode) + val remainingNode = node.firstChildNode + val newNode = if (remainingNode.elementType == DOT_QUALIFIED_EXPRESSION) { + LeafPsiElement(REGULAR_STRING_PART, "\$${remainingNode.text}") + } else { + LeafPsiElement(remainingNode.elementType, "\$${remainingNode.text}") + } + node.replaceChild(node.firstChildNode, newNode) + } } } } diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/StringTemplateRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/StringTemplateRuleTest.kt index dee53082c8..2849be6a9c 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/StringTemplateRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/StringTemplateRuleTest.kt @@ -2,6 +2,7 @@ package com.pinterest.ktlint.ruleset.standard import com.pinterest.ktlint.test.diffFileFormat import com.pinterest.ktlint.test.diffFileLint +import com.pinterest.ktlint.test.format import org.assertj.core.api.Assertions.assertThat import org.junit.Test @@ -21,4 +22,23 @@ class StringTemplateRuleTest { ) ).isEmpty() } + + @Test + fun testFormatIssue996() { + assertThat( + StringTemplateRule().format( + """ + fun getDrafts(val draftsIds: List) { + println("draftIds=[${'$'}{draftsIds.toString()}]") + } + """.trimIndent() + ) + ).isEqualTo( + """ + fun getDrafts(val draftsIds: List) { + println("draftIds=[${'$'}draftsIds]") + } + """.trimIndent() + ) + } } diff --git a/ktlint-ruleset-standard/src/test/resources/spec/string-template/format-expected.kt.spec b/ktlint-ruleset-standard/src/test/resources/spec/string-template/format-expected.kt.spec index 9a706d1b8f..bb9884d30d 100644 --- a/ktlint-ruleset-standard/src/test/resources/spec/string-template/format-expected.kt.spec +++ b/ktlint-ruleset-standard/src/test/resources/spec/string-template/format-expected.kt.spec @@ -2,6 +2,7 @@ fun main() { val x = "${String::class}" println("$x.hello") println("$x.hello") + println("$x") println("${x}hello") println("${x.length}.hello") println("$x.hello") diff --git a/ktlint-ruleset-standard/src/test/resources/spec/string-template/format.kt.spec b/ktlint-ruleset-standard/src/test/resources/spec/string-template/format.kt.spec index 867175c316..02f75d55a7 100644 --- a/ktlint-ruleset-standard/src/test/resources/spec/string-template/format.kt.spec +++ b/ktlint-ruleset-standard/src/test/resources/spec/string-template/format.kt.spec @@ -2,6 +2,7 @@ fun main() { val x = "${String::class.toString()}" println("${x}.hello") println("${x.toString()}.hello") + println("${x.toString()}") println("${x}hello") println("${x.length}.hello") println("$x.hello") diff --git a/ktlint-ruleset-standard/src/test/resources/spec/string-template/lint.kt.spec b/ktlint-ruleset-standard/src/test/resources/spec/string-template/lint.kt.spec index 7e6cb5e352..a38f9780af 100644 --- a/ktlint-ruleset-standard/src/test/resources/spec/string-template/lint.kt.spec +++ b/ktlint-ruleset-standard/src/test/resources/spec/string-template/lint.kt.spec @@ -1,5 +1,6 @@ fun main() { println("${String::class.toString()}") + println("${hello.toString()}") println("""${Int::class.toString()}""") println("$s0") println("""$s1""") @@ -62,9 +63,10 @@ class F { // expect // 2:29:Redundant "toString()" call in string template -// 3:28:Redundant "toString()" call in string template -// 6:15:Redundant curly braces +// 3:21:Redundant "toString()" call in string template +// 4:28:Redundant "toString()" call in string template // 7:15:Redundant curly braces -// 28:79:Redundant "toString()" call in string template -// 45:20:Redundant curly braces -// 55:19:Redundant curly braces +// 8:15:Redundant curly braces +// 29:79:Redundant "toString()" call in string template +// 46:20:Redundant curly braces +// 56:19:Redundant curly braces From 7312732b2a30a9652cc1101f46330f97b8d25ce9 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Tue, 22 Dec 2020 23:31:48 +0100 Subject: [PATCH 16/53] Fix false positive when argument list is after multiline dot-qualified expression (#1025) --- CHANGELOG.md | 1 + .../experimental/ArgumentListWrappingRule.kt | 56 ++++++++++++------- .../ArgumentListWrappingRuleTest.kt | 43 ++++++++++++++ 3 files changed, 79 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86173c6c50..dcfbf5c3cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Fix "filename" rule will not work when '.editorconfig' file is not found ([#997](https://github.com/pinterest/ktlint/issues/1004)) - EditorConfig generation for `import-ordering` ([#1011](https://github.com/pinterest/ktlint/pull/1011)) - Internal error (`no-unused-imports`) ([#996](https://github.com/pinterest/ktlint/issues/996)) +- Fix false positive when argument list is after multiline dot-qualified expression (`argument-list-wrapping`) ([#893](https://github.com/pinterest/ktlint/issues/893)) ### Changed - Update Gradle shadow plugin to `6.1.0` version diff --git a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRule.kt b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRule.kt index ea0366b045..aa29ff9fb5 100644 --- a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRule.kt +++ b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRule.kt @@ -3,8 +3,11 @@ package com.pinterest.ktlint.ruleset.experimental import com.pinterest.ktlint.core.KtLint import com.pinterest.ktlint.core.Rule import com.pinterest.ktlint.core.ast.ElementType +import com.pinterest.ktlint.core.ast.ElementType.DOT_QUALIFIED_EXPRESSION +import com.pinterest.ktlint.core.ast.ElementType.TYPE_ARGUMENT_LIST import com.pinterest.ktlint.core.ast.children import com.pinterest.ktlint.core.ast.column +import com.pinterest.ktlint.core.ast.isPartOf import com.pinterest.ktlint.core.ast.isPartOfComment import com.pinterest.ktlint.core.ast.isRoot import com.pinterest.ktlint.core.ast.isWhiteSpace @@ -71,21 +74,38 @@ class ArgumentListWrappingRule : Rule("argument-list-wrapping") { // max_line_length exceeded maxLineLength > -1 && (node.column - 1 + node.textLength) > maxLineLength && !node.textContains('\n') if (putArgumentsOnSeparateLines) { - // IDEA quirk: - // generic< - // T, - // R>( - // 1, - // 2 - // ) - // instead of - // generic< - // T, - // R>( - // 1, - // 2 - // ) - val adjustedIndent = if (node.hasTypeParameterListInFront()) indentSize else 0 + val prevWhitespaceWithNewline = node.prevLeaf { it.isWhiteSpaceWithNewline() } + val adjustedIndent = when { + // IDEA quirk: + // generic< + // T, + // R>( + // 1, + // 2 + // ) + // instead of + // generic< + // T, + // R>( + // 1, + // 2 + // ) + prevWhitespaceWithNewline?.isPartOf(TYPE_ARGUMENT_LIST) == true -> indentSize + // IDEA quirk: + // foo + // .bar( + // 1, + // 2 + // ) + // instead of + // foo + // .bar( + // 1, + // 2 + // ) + prevWhitespaceWithNewline?.isPartOf(DOT_QUALIFIED_EXPRESSION) == true -> indentSize + else -> 0 + } // aiming for // ... LPAR @@ -199,12 +219,6 @@ class ArgumentListWrappingRule : Rule("argument-list-wrapping") { } } - private fun ASTNode.hasTypeParameterListInFront(): Boolean = - treeParent.children() - .firstOrNull { it.elementType == ElementType.TYPE_PARAMETER_LIST } - ?.children() - ?.any { it.isWhiteSpaceWithNewline() } == true - private fun ASTNode.prevWhiteSpaceWithNewLine(): ASTNode? { var prev = prevLeaf() while (prev != null && (prev.isWhiteSpace() || prev.isPartOfComment())) { diff --git a/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRuleTest.kt b/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRuleTest.kt index f6bcbbbf55..e4d1078bf4 100644 --- a/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRuleTest.kt +++ b/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRuleTest.kt @@ -528,4 +528,47 @@ class ArgumentListWrappingRuleTest { ) ).isEmpty() } + + @Test + fun `lint argument list after multiline dot qualified expression`() { + assertThat( + ArgumentListWrappingRule().lint( + """ + class Logging(mode: Any, appInstanceIdentity: String, org: String) + + class StateManager { + var firebaseLogger: Logging? = null + } + + private fun replaceLogger(deviceId: String, orgName: String) { + val stateManager: StateManager = StateManager() + stateManager + .firebaseLogger( + mode = 0, + appInstanceIdentity = deviceId, + org = orgName + ) + } + """.trimIndent() + ) + ).isEmpty() + } + + @Test + fun `lint argument list after multiline type argument list`() { + assertThat( + ArgumentListWrappingRule().lint( + """ + fun test() { + generic< + Int, + Int>( + 1, + 2 + ) + } + """.trimIndent() + ) + ).isEmpty() + } } From 477c64cd667e4bdc890317aab16034aa82d6e3b6 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Wed, 23 Dec 2020 09:39:51 +0100 Subject: [PATCH 17/53] Fix indentation for function types after a newline (#1022) --- CHANGELOG.md | 1 + .../ruleset/standard/IndentationRule.kt | 8 ++++++- .../ruleset/standard/IndentationRuleTest.kt | 21 +++++++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dcfbf5c3cd..749f747b7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - EditorConfig generation for `import-ordering` ([#1011](https://github.com/pinterest/ktlint/pull/1011)) - Internal error (`no-unused-imports`) ([#996](https://github.com/pinterest/ktlint/issues/996)) - Fix false positive when argument list is after multiline dot-qualified expression (`argument-list-wrapping`) ([#893](https://github.com/pinterest/ktlint/issues/893)) +- Fix indentation for function types after a newline (`indent`) ([#918](https://github.com/pinterest/ktlint/issues/918)) ### Changed - Update Gradle shadow plugin to `6.1.0` version diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/IndentationRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/IndentationRule.kt index ce531b0d50..20b409d6fd 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/IndentationRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/IndentationRule.kt @@ -23,6 +23,7 @@ import com.pinterest.ktlint.core.ast.ElementType.ELSE import com.pinterest.ktlint.core.ast.ElementType.ELVIS import com.pinterest.ktlint.core.ast.ElementType.EOL_COMMENT import com.pinterest.ktlint.core.ast.ElementType.EQ +import com.pinterest.ktlint.core.ast.ElementType.FUN import com.pinterest.ktlint.core.ast.ElementType.FUNCTION_LITERAL import com.pinterest.ktlint.core.ast.ElementType.GT import com.pinterest.ktlint.core.ast.ElementType.KDOC @@ -725,7 +726,12 @@ class IndentationRule : Rule("indent"), Rule.Modifier.RestrictToRootLast { private fun adjustExpectedIndentAfterColon(n: ASTNode, ctx: IndentContext) { expectedIndent++ debug { "++after(COLON) -> $expectedIndent" } - ctx.exitAdjBy(n.treeParent, -1) + if (n.isPartOf(FUN)) { + val returnType = n.nextCodeSibling() + ctx.exitAdjBy(returnType!!, -1) + } else { + ctx.exitAdjBy(n.treeParent, -1) + } } private fun adjustExpectedIndentAfterLparInsideCondition(n: ASTNode, ctx: IndentContext) { diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/IndentationRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/IndentationRuleTest.kt index 6383546aa9..70932a0746 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/IndentationRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/IndentationRuleTest.kt @@ -828,4 +828,25 @@ internal class IndentationRuleTest { ) ).isEmpty() } + + // https://github.com/pinterest/ktlint/issues/918 + @Test + fun `lint newline after type reference in functions`() { + assertThat( + IndentationRule().lint( + """ + override fun actionProcessor(): + ObservableTransformer = + ObservableTransformer { actions -> + // ... + } + + fun generateGooooooooooooooooogle(): + Gooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooogle { + return Gooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooogle() + } + """.trimIndent() + ) + ).isEmpty() + } } From a6c6d01d234c85535be9a4835e8325cab0f577c7 Mon Sep 17 00:00:00 2001 From: paul-dingemans Date: Fri, 25 Dec 2020 22:13:42 +0100 Subject: [PATCH 18/53] Fix indentation for lines at expected indentation level but with wrong indentation style (#425) (#1031) Co-authored-by: Paul Dingemans --- .../ruleset/standard/IndentationRule.kt | 4 ++- .../ruleset/standard/IndentationRuleTest.kt | 26 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/IndentationRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/IndentationRule.kt index 20b409d6fd..515588ea92 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/IndentationRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/IndentationRule.kt @@ -1054,7 +1054,9 @@ class IndentationRule : Rule("indent"), Rule.Modifier.RestrictToRootLast { (if (!autoCorrect) "would have " else "") + "changed indentation to $expectedIndentLength (from ${normalizedNodeIndent.length})" } - if (autoCorrect) { + } + if (autoCorrect) { + if (nodeIndent != normalizedNodeIndent || normalizedNodeIndent.length != expectedIndentLength) { val indent = when (editorConfig.indentStyle) { IndentStyle.SPACE -> " " IndentStyle.TAB -> "\t" diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/IndentationRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/IndentationRuleTest.kt index 70932a0746..9c0e36cf2e 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/IndentationRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/IndentationRuleTest.kt @@ -343,6 +343,32 @@ internal class IndentationRuleTest { .isEqualTo("fun main() {\n return 0\n}") } + @Test + fun testUnexpectedTabCharacterForLinesAtCorrectIndentationLevel() { + val ktScript = "" + + "class Foo {\n" + + "\tfun doBar() {\n" + + "\t\t\tprintln(\"test\")\n" + + "\t}\n" + + "}\n" + assertThat(IndentationRule().lint(ktScript)).isEqualTo( + listOf( + LintError(line = 2, col = 1, ruleId = "indent", detail = "Unexpected tab character(s)"), + LintError(line = 3, col = 1, ruleId = "indent", detail = "Unexpected tab character(s)"), + LintError(line = 3, col = 1, ruleId = "indent", detail = "Unexpected indentation (12) (should be 8)"), + LintError(line = 4, col = 1, ruleId = "indent", detail = "Unexpected tab character(s)") + ) + ) + assertThat(IndentationRule().format(ktScript)) + .isEqualTo( + "class Foo {\n" + + " fun doBar() {\n" + + " println(\"test\")\n" + + " }\n" + + "}\n" + ) + } + @Test fun testUnexpectedTabCharacterWithCustomIndentSize() { val ktScript = "fun main() {\n\t\treturn 0\n\t}" From d03a86a968efdb030899ca757c141095f38314d1 Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Wed, 23 Dec 2020 15:19:53 +0100 Subject: [PATCH 19/53] Add Github pull request template. --- .github/PULL_REQUEST_TEMPLATE.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..ac506edf1d --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,10 @@ +## Description + + + + +## Checklist + + +- [ ] tests are added +- [ ] `CHANGELOG.md` is updated From 8f0542e71bf847087aa9a69d2c7c2e65a2622c2d Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Wed, 23 Dec 2020 15:28:34 +0100 Subject: [PATCH 20/53] Add new "enhancement" issue type. --- .github/ISSUE_TEMPLATE/enhancement.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/enhancement.yml diff --git a/.github/ISSUE_TEMPLATE/enhancement.yml b/.github/ISSUE_TEMPLATE/enhancement.yml new file mode 100644 index 0000000000..6694c30553 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/enhancement.yml @@ -0,0 +1,17 @@ +--- +name: Enhancement +about: Propose new enhancement to the Ktlint API + +--- + + + +## Expected Behavior + + +## Current Behavior + + +## Additional information +* Current version of ktlint: From 2625576d1f15383f69296031aba73d404c6cb2f9 Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Wed, 23 Dec 2020 15:29:21 +0100 Subject: [PATCH 21/53] Move to comment statement that bug may already be reported. --- .github/ISSUE_TEMPLATE/bug_report.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 2f7da33def..12ae7a1a30 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -4,8 +4,8 @@ about: Report ktlint bug --- -The bug you're experiencing might have already be reported! -Please search in the [issues](https://github.com/pinterest/ktlint/issues) before creating one. + ## Expected Behavior From 80135eb1b112492dba74854d1b6a96748efbe7b9 Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Wed, 23 Dec 2020 15:33:32 +0100 Subject: [PATCH 22/53] Add "New rule" issue template. --- .github/ISSUE_TEMPLATE/new_rule_proposal.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/new_rule_proposal.yml diff --git a/.github/ISSUE_TEMPLATE/new_rule_proposal.yml b/.github/ISSUE_TEMPLATE/new_rule_proposal.yml new file mode 100644 index 0000000000..6cc66f965d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/new_rule_proposal.yml @@ -0,0 +1,15 @@ +--- +name: New Rule +about: Propose new rule + +--- + + + +## Expected Rule behavior + + +## Additional information +* Current version of ktlint: +* Styleguide section: From da6305d8998b106ac45dd6758aaf551a90faea76 Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Sun, 27 Dec 2020 14:49:31 +0100 Subject: [PATCH 23/53] Update Kotlin dev version to 1.4.30-M1. --- build.gradle | 2 +- gradle/verification-metadata.xml | 10 ++++++++++ settings.gradle | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 83a4354e1f..f42a0f43c2 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { } ext.versions = [ - 'kotlin': gradle.ext.isKotlinDev ? "1.4.20-M1" : "1.4.10", + 'kotlin': gradle.ext.isKotlinDev ? "1.4.30-M1" : "1.4.10", 'gradle': '6.6', 'gradle-sha256': 'e6f83508f0970452f56197f610d13c5f593baaf43c0e3c6a571e5967be754025' ] diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 132a97eb54..2a7dc95a2a 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -290,6 +290,11 @@ + + + + + @@ -955,6 +960,11 @@ + + + + + diff --git a/settings.gradle b/settings.gradle index 0406f7ad38..9216e1a6b7 100644 --- a/settings.gradle +++ b/settings.gradle @@ -9,7 +9,7 @@ pluginManagement { } plugins { - def kotlinVersion = settings.hasProperty("kotlinDev") ? "1.4.20-M1" : "1.4.10" + def kotlinVersion = settings.hasProperty("kotlinDev") ? "1.4.30-M1" : "1.4.10" id 'org.jetbrains.kotlin.jvm' version kotlinVersion id 'com.github.breadmoirai.github-release' version '2.2.12' id 'com.github.johnrengelman.shadow' version '6.1.0' From 60b1e6db783d82159beab5893b86a1bc0756019f Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Sun, 27 Dec 2020 15:23:55 +0100 Subject: [PATCH 24/53] Fix new issue templates has wrong file extension. --- .github/ISSUE_TEMPLATE/{enhancement.yml => enhancement.md} | 0 .../{new_rule_proposal.yml => new_rule_proposal.md} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename .github/ISSUE_TEMPLATE/{enhancement.yml => enhancement.md} (100%) rename .github/ISSUE_TEMPLATE/{new_rule_proposal.yml => new_rule_proposal.md} (100%) diff --git a/.github/ISSUE_TEMPLATE/enhancement.yml b/.github/ISSUE_TEMPLATE/enhancement.md similarity index 100% rename from .github/ISSUE_TEMPLATE/enhancement.yml rename to .github/ISSUE_TEMPLATE/enhancement.md diff --git a/.github/ISSUE_TEMPLATE/new_rule_proposal.yml b/.github/ISSUE_TEMPLATE/new_rule_proposal.md similarity index 100% rename from .github/ISSUE_TEMPLATE/new_rule_proposal.yml rename to .github/ISSUE_TEMPLATE/new_rule_proposal.md From 9f4dd89ca2f64d09f02c3ba2593e61d288958253 Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Sun, 27 Dec 2020 16:12:41 +0100 Subject: [PATCH 25/53] Add to README section about IDEA .editorconfig autoformat issue. --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index d8eea85eb6..f5792070d2 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,13 @@ insert_final_newline=true max_line_length=off ``` +### IntelliJ IDEA `.editorconfig` autoformat issue + +Unfortunately [IntelliJ IDEA](https://www.jetbrains.com/idea/) has `.editorconfig` [autoformat issue](https://youtrack.jetbrains.com/issue/IDEA-242506) +that adds additional space into glob statements. +For example, `[*{kt,kts}]` is formatted into `[*{kt, kts}]` ([original ktlint issue](https://github.com/pinterest/ktlint/issues/762)). +Such behaviour violates `.editorconfig` [specification](https://github.com/editorconfig/editorconfig/issues/148) and leads to ignoring this section when ktlint is parsing it. + ### Custom Ktlint specific EditorConfig properties ```ini From b1d7b8ae257e31d6d7bd41efc2f5a06fb6e5b672 Mon Sep 17 00:00:00 2001 From: Nicolas Vuillamy Date: Tue, 29 Dec 2020 11:26:58 +0100 Subject: [PATCH 26/53] Add CI & Mega-Linter link in README (#1028) * Add CI & Mega-Linter link in README * Add CI link to head menu --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f5792070d2..87142103e4 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ It's also [easy to create your own](#creating-a-reporter). - **A single executable jar with all dependencies included.**

-Installation | Usage | Integration with Maven / Gradle / IntelliJ IDEA / Emacs | Creating a ruleset | a reporter | Badge | FAQ +Installation | Usage | Integration with Maven / Gradle / IntelliJ IDEA / Emacs / Continuous Integration | Creating a ruleset | a reporter | Badge | FAQ

## Standard rules @@ -427,6 +427,10 @@ See [w0rp/ale](https://github.com/w0rp/ale). > Integrated with something else? Send a PR. +#### ... with Continuous Integration + +See [Mega-Linter](https://nvuillam.github.io/mega-linter/): 70+ linters aggregated in a single tool for CI, including **ktlint** activated out of the box + ## Creating a ruleset > See also [Writing your first ktlint rule](https://medium.com/@vanniktech/writing-your-first-ktlint-rule-5a1707f4ca5b) by [Niklas Baudy](https://github.com/vanniktech). From e1e0326c7cc8903ab92e308d7037543e7b75f622 Mon Sep 17 00:00:00 2001 From: Toshiaki Kameyama Date: Tue, 29 Dec 2020 22:59:16 +0900 Subject: [PATCH 27/53] Don't remove the equals sign for a default argument (`no-line-break-before-assignment`) (#1041) * Don't remove the equals sign for a default argument (`no-line-break-before-assignment`) * Remove unnecessary test * Remove unused import Co-authored-by: Roman Zavarnitsyn --- CHANGELOG.md | 1 + .../NoLineBreakBeforeAssignmentRule.kt | 14 ++++++++------ .../NoLineBreakBeforeAssignmentRuleTest.kt | 18 ++++++++++++++++++ 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 749f747b7d..acf27338a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Internal error (`no-unused-imports`) ([#996](https://github.com/pinterest/ktlint/issues/996)) - Fix false positive when argument list is after multiline dot-qualified expression (`argument-list-wrapping`) ([#893](https://github.com/pinterest/ktlint/issues/893)) - Fix indentation for function types after a newline (`indent`) ([#918](https://github.com/pinterest/ktlint/issues/918)) +- Don't remove the equals sign for a default argument (`no-line-break-before-assignment`) ([#1039](https://github.com/pinterest/ktlint/issues/1039)) ### Changed - Update Gradle shadow plugin to `6.1.0` version diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/NoLineBreakBeforeAssignmentRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/NoLineBreakBeforeAssignmentRule.kt index fa00dc4ef1..ee39ef54f5 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/NoLineBreakBeforeAssignmentRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/NoLineBreakBeforeAssignmentRule.kt @@ -9,6 +9,7 @@ import com.pinterest.ktlint.core.ast.prevCodeSibling import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement +import org.jetbrains.kotlin.psi.KtPsiFactory import org.jetbrains.kotlin.psi.psiUtil.siblings class NoLineBreakBeforeAssignmentRule : Rule("no-line-break-before-assignment") { @@ -23,13 +24,14 @@ class NoLineBreakBeforeAssignmentRule : Rule("no-line-break-before-assignment") if (hasLineBreakBeforeAssignment == true) { emit(node.startOffset, "Line break before assignment is not allowed", true) if (autoCorrect) { - val next = prevCodeSibling.treeNext - val newText = buildString { - append(" =") - if (next !is PsiWhiteSpace) append(" ") - append(next.text) + val prevPsi = prevCodeSibling.psi + val parentPsi = prevPsi.parent + val psiFactory = KtPsiFactory(prevPsi) + if (prevPsi.nextSibling !is PsiWhiteSpace) { + parentPsi.addAfter(psiFactory.createWhiteSpace(), prevPsi) } - (next as? LeafPsiElement)?.rawReplaceWithText(newText) + parentPsi.addAfter(psiFactory.createEQ(), prevPsi) + parentPsi.addAfter(psiFactory.createWhiteSpace(), prevPsi) (node as? LeafPsiElement)?.delete() } } diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/NoLineBreakBeforeAssignmentRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/NoLineBreakBeforeAssignmentRuleTest.kt index b54a71945f..e86b3b4318 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/NoLineBreakBeforeAssignmentRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/NoLineBreakBeforeAssignmentRuleTest.kt @@ -169,4 +169,22 @@ class NoLineBreakBeforeAssignmentRuleTest { """.trimIndent() ) } + + // https://github.com/pinterest/ktlint/issues/1039 + @Test + fun `test default arguments`() { + assertThat( + NoLineBreakBeforeAssignmentRule().format( + """ + fun test(b: Boolean? + = null): Int = 3 + """.trimIndent() + ) + ).isEqualTo( + """ + fun test(b: Boolean? = + null): Int = 3 + """.trimIndent() + ) + } } From fa38d7576069665a450c7c003e21295c4e2d4764 Mon Sep 17 00:00:00 2001 From: Toshiaki Kameyama Date: Wed, 30 Dec 2020 23:00:44 +0900 Subject: [PATCH 28/53] Fix internal error in `no-unused-imports` (#1044) * Fix internal error in `no-unused-imports` * Add integration test Co-authored-by: Roman Zavarnitsyn --- CHANGELOG.md | 1 + .../ruleset/standard/StringTemplateRule.kt | 26 +++++++-------- .../StandardRuleSetIntegrationTest.kt | 33 +++++++++++++++++++ 3 files changed, 45 insertions(+), 15 deletions(-) create mode 100644 ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/StandardRuleSetIntegrationTest.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index acf27338a9..3a919a26ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Fix false positive when argument list is after multiline dot-qualified expression (`argument-list-wrapping`) ([#893](https://github.com/pinterest/ktlint/issues/893)) - Fix indentation for function types after a newline (`indent`) ([#918](https://github.com/pinterest/ktlint/issues/918)) - Don't remove the equals sign for a default argument (`no-line-break-before-assignment`) ([#1039](https://github.com/pinterest/ktlint/issues/1039)) +- Fix internal error in `no-unused-imports` ([#1040](https://github.com/pinterest/ktlint/issues/1040)) ### Changed - Update Gradle shadow plugin to `6.1.0` version diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/StringTemplateRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/StringTemplateRule.kt index 7577763f74..7b1009d663 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/StringTemplateRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/StringTemplateRule.kt @@ -2,19 +2,18 @@ package com.pinterest.ktlint.ruleset.standard import com.pinterest.ktlint.core.Rule import com.pinterest.ktlint.core.ast.ElementType.CLOSING_QUOTE -import com.pinterest.ktlint.core.ast.ElementType.DOT import com.pinterest.ktlint.core.ast.ElementType.DOT_QUALIFIED_EXPRESSION import com.pinterest.ktlint.core.ast.ElementType.LITERAL_STRING_TEMPLATE_ENTRY import com.pinterest.ktlint.core.ast.ElementType.LONG_STRING_TEMPLATE_ENTRY import com.pinterest.ktlint.core.ast.ElementType.LONG_TEMPLATE_ENTRY_END import com.pinterest.ktlint.core.ast.ElementType.LONG_TEMPLATE_ENTRY_START import com.pinterest.ktlint.core.ast.ElementType.REGULAR_STRING_PART -import com.pinterest.ktlint.core.ast.ElementType.SUPER_EXPRESSION import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement import org.jetbrains.kotlin.psi.KtBlockStringTemplateEntry import org.jetbrains.kotlin.psi.KtDotQualifiedExpression import org.jetbrains.kotlin.psi.KtNameReferenceExpression +import org.jetbrains.kotlin.psi.KtSuperExpression import org.jetbrains.kotlin.psi.KtThisExpression class StringTemplateRule : Rule("string-template") { @@ -38,20 +37,17 @@ class StringTemplateRule : Rule("string-template") { */ if (elementType == LONG_STRING_TEMPLATE_ENTRY) { var entryExpression = (node.psi as? KtBlockStringTemplateEntry)?.expression - val entryStart = node.firstChildNode - val dotQualifiedExpression = entryStart.treeNext - if (dotQualifiedExpression?.elementType == DOT_QUALIFIED_EXPRESSION) { - val callExpression = dotQualifiedExpression.lastChildNode - val dot = callExpression.treePrev - if (dot?.elementType == DOT && - callExpression.text == "toString()" && - dotQualifiedExpression.firstChildNode?.elementType != SUPER_EXPRESSION - ) { - emit(dot.startOffset, "Redundant \"toString()\" call in string template", true) + if (entryExpression is KtDotQualifiedExpression) { + val receiver = entryExpression.receiverExpression + if (entryExpression.selectorExpression?.text == "toString()" && receiver !is KtSuperExpression) { + emit( + entryExpression.operationTokenNode.startOffset, + "Redundant \"toString()\" call in string template", + true + ) if (autoCorrect) { - entryExpression = (entryExpression as? KtDotQualifiedExpression)?.receiverExpression - node.removeChild(dot) - node.removeChild(callExpression) + entryExpression.replace(receiver) + entryExpression = receiver } } } diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/StandardRuleSetIntegrationTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/StandardRuleSetIntegrationTest.kt new file mode 100644 index 0000000000..d4267bee7b --- /dev/null +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/StandardRuleSetIntegrationTest.kt @@ -0,0 +1,33 @@ +package com.pinterest.ktlint.ruleset.standard + +import com.pinterest.ktlint.core.KtLint +import org.assertj.core.api.Assertions.assertThat +import org.junit.Test + +class StandardRuleSetIntegrationTest { + + @Test + fun `test string-template wit unused-imports integration`() { + val actual = KtLint.format( + KtLint.Params( + text = """ + fun foo() = 1 + fun test() { + println("${'$'}{foo().toString()}") + } + """.trimIndent(), + ruleSets = listOf(StandardRuleSetProvider().get()), + cb = { _, _ -> } + ) + ) + assertThat(actual).isEqualTo( + """ + fun foo() = 1 + fun test() { + println("${'$'}{foo()}") + } + + """.trimIndent() + ) + } +} From 05c513fdf5ce15a879551101ffbf5a4d5b446410 Mon Sep 17 00:00:00 2001 From: Jen Date: Sun, 10 Jan 2021 14:50:33 +0100 Subject: [PATCH 29/53] Upgrade ec4j to latest version (#1042) ec4j 0.3.0 removes the length limit of key, value and section name in editor config files. Fixes #739. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index f42a0f43c2..a7b89537ef 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,7 @@ ext.deps = [ 'compiler': "org.jetbrains.kotlin:kotlin-compiler-embeddable:${versions.kotlin}" ], 'klob' : 'com.github.shyiko.klob:klob:0.2.1', - ec4j : 'org.ec4j.core:ec4j-core:0.2.2', + ec4j : 'org.ec4j.core:ec4j-core:0.3.0', 'picocli' : 'info.picocli:picocli:3.9.6', // Testing libraries 'junit' : 'junit:junit:4.13.1', From 634a8a228f5206d3969952ce92cb3e2f9b9f8636 Mon Sep 17 00:00:00 2001 From: Toshiaki Kameyama Date: Tue, 12 Jan 2021 13:52:00 +0900 Subject: [PATCH 30/53] Fix false positives when declaration has tail comments (`spacing-between-declarations-with-comments`) --- CHANGELOG.md | 1 + ...cingBetweenDeclarationsWithCommentsRule.kt | 4 +- ...BetweenDeclarationsWithCommentsRuleTest.kt | 37 +++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a919a26ac..f079a387c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Fix indentation for function types after a newline (`indent`) ([#918](https://github.com/pinterest/ktlint/issues/918)) - Don't remove the equals sign for a default argument (`no-line-break-before-assignment`) ([#1039](https://github.com/pinterest/ktlint/issues/1039)) - Fix internal error in `no-unused-imports` ([#1040](https://github.com/pinterest/ktlint/issues/1040)) +- Fix false positives when declaration has tail comments (`spacing-between-declarations-with-comments`) ([#1053](https://github.com/pinterest/ktlint/issues/1053)) ### Changed - Update Gradle shadow plugin to `6.1.0` version diff --git a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/SpacingBetweenDeclarationsWithCommentsRule.kt b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/SpacingBetweenDeclarationsWithCommentsRule.kt index 079daf4141..0513223141 100644 --- a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/SpacingBetweenDeclarationsWithCommentsRule.kt +++ b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/SpacingBetweenDeclarationsWithCommentsRule.kt @@ -11,6 +11,7 @@ import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.psiUtil.getPrevSiblingIgnoringWhitespaceAndComments +import org.jetbrains.kotlin.psi.psiUtil.startOffset /** * @see https://youtrack.jetbrains.com/issue/KT-35088 @@ -24,7 +25,8 @@ class SpacingBetweenDeclarationsWithCommentsRule : Rule("spacing-between-declara ) { if (node is PsiComment) { val declaration = node.parent as? KtDeclaration ?: return - if (declaration.getPrevSiblingIgnoringWhitespaceAndComments() !is KtDeclaration) return + val isTailComment = node.startOffset > declaration.startOffset + if (isTailComment || declaration.getPrevSiblingIgnoringWhitespaceAndComments() !is KtDeclaration) return val prevSibling = declaration.node.prevSibling { it.elementType != WHITE_SPACE } if (prevSibling != null && diff --git a/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/SpacingBetweenDeclarationsWithCommentsRuleTest.kt b/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/SpacingBetweenDeclarationsWithCommentsRuleTest.kt index 6a0785c2bc..e4c2cb3019 100644 --- a/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/SpacingBetweenDeclarationsWithCommentsRuleTest.kt +++ b/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/SpacingBetweenDeclarationsWithCommentsRuleTest.kt @@ -221,4 +221,41 @@ class SpacingBetweenDeclarationsWithCommentsRuleTest { """.trimIndent() ) } + + // https://github.com/pinterest/ktlint/issues/1053 + @Test + fun `declaration has tail comments`() { + assertThat( + SpacingBetweenDeclarationsWithCommentsRule().lint( + """ + class SampleClass { + + private val public = "ok" // ok + private val private = "not_ok" // false positive + private val halfPublicHalfPrivate = "not_ok" // false positive + + /** + * Doc 1 + */ + fun one() = 1 + + /** + * Doc 2 + */ + fun two() = 2 + fun three() { + val public = "ok" // ok + val private = "not_ok" // false positive + } + } + + enum class SampleEnum { + One, // ok + Two, // false positive + Three, // false positive + } + """.trimIndent() + ) + ).isEmpty() + } } From 4e0a7624e6e097262e26a8bbba673bffe475b489 Mon Sep 17 00:00:00 2001 From: Toshiaki Kameyama Date: Fri, 15 Jan 2021 10:54:06 +0900 Subject: [PATCH 31/53] Fix formatting with comments (`colon-spacing`) --- CHANGELOG.md | 1 + .../standard/SpacingAroundColonRule.kt | 34 +++++++++------ .../standard/SpacingAroundColonRuleTest.kt | 42 +++++++++++++++++-- 3 files changed, 61 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f079a387c2..a391b0e7a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Don't remove the equals sign for a default argument (`no-line-break-before-assignment`) ([#1039](https://github.com/pinterest/ktlint/issues/1039)) - Fix internal error in `no-unused-imports` ([#1040](https://github.com/pinterest/ktlint/issues/1040)) - Fix false positives when declaration has tail comments (`spacing-between-declarations-with-comments`) ([#1053](https://github.com/pinterest/ktlint/issues/1053)) +- Fix formatting with comments (`colon-spacing`) ([#1057](https://github.com/pinterest/ktlint/issues/1057)) ### Changed - Update Gradle shadow plugin to `6.1.0` version diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/SpacingAroundColonRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/SpacingAroundColonRule.kt index 84f7b7ae88..aa8e841155 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/SpacingAroundColonRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/SpacingAroundColonRule.kt @@ -6,7 +6,9 @@ import com.pinterest.ktlint.core.ast.ElementType.ANNOTATION_ENTRY import com.pinterest.ktlint.core.ast.isPartOf import com.pinterest.ktlint.core.ast.isPartOfComment import com.pinterest.ktlint.core.ast.isPartOfString +import com.pinterest.ktlint.core.ast.isWhiteSpace import com.pinterest.ktlint.core.ast.isWhiteSpaceWithNewline +import com.pinterest.ktlint.core.ast.nextLeaf import com.pinterest.ktlint.core.ast.prevLeaf import com.pinterest.ktlint.core.ast.upsertWhitespaceAfterMe import com.pinterest.ktlint.core.ast.upsertWhitespaceBeforeMe @@ -17,6 +19,7 @@ import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtConstructor import org.jetbrains.kotlin.psi.KtTypeConstraint import org.jetbrains.kotlin.psi.KtTypeParameterList +import org.jetbrains.kotlin.psi.psiUtil.prevLeafs class SpacingAroundColonRule : Rule("colon-spacing") { @@ -36,24 +39,31 @@ class SpacingAroundColonRule : Rule("colon-spacing") { node.parent !is KtTypeConstraint && // where T : S node.parent?.parent !is KtTypeParameterList val prevLeaf = node.prevLeaf() - if (prevLeaf != null && - prevLeaf.isWhiteSpaceWithNewline() && - // FIXME: relocate : so that it would be in front of comment - // (see SpacingAroundColonRuleTest.testFormatEOF & ChainWrappingRule) - prevLeaf.prevLeaf()?.isPartOfComment() != true - ) { + if (prevLeaf != null && prevLeaf.isWhiteSpaceWithNewline()) { emit(prevLeaf.startOffset, "Unexpected newline before \":\"", true) - val text = prevLeaf.text if (autoCorrect) { - if (removeSpacingBefore) { - prevLeaf.treeParent.removeChild(prevLeaf) + if (prevLeaf.prevLeaf()?.isPartOfComment() == true) { + val nextLeaf = node.nextLeaf() + val prevNonCodeLeaves = + node.prevLeafs.takeWhile { it.node.isWhiteSpace() || it.node.isPartOfComment() }.toList() + prevNonCodeLeaves.reversed().forEach { + node.treeParent.addChild(it.node, nextLeaf) + } + if (nextLeaf != null && nextLeaf.isWhiteSpace()) { + node.treeParent.removeChild(nextLeaf) + } } else { - (prevLeaf as LeafPsiElement).rawReplaceWithText(" ") + val text = prevLeaf.text + if (removeSpacingBefore) { + prevLeaf.treeParent.removeChild(prevLeaf) + } else { + (prevLeaf as LeafPsiElement).rawReplaceWithText(" ") + } + node.upsertWhitespaceAfterMe(text) } - node.upsertWhitespaceAfterMe(text) } } - if (node.prevSibling is PsiWhiteSpace && removeSpacingBefore) { + if (node.prevSibling is PsiWhiteSpace && removeSpacingBefore && !prevLeaf.isWhiteSpaceWithNewline()) { emit(node.startOffset, "Unexpected spacing before \":\"", true) if (autoCorrect) { node.prevSibling.node.treeParent.removeChild(node.prevSibling.node) diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/SpacingAroundColonRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/SpacingAroundColonRuleTest.kt index 3961a2a348..921f270604 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/SpacingAroundColonRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/SpacingAroundColonRuleTest.kt @@ -248,15 +248,49 @@ class SpacingAroundColonRuleTest { class X : Y, Z - class A // comment - : B - class A /* + class A : // comment + B + class A : /* */ - : B + B val xmlFormatter: String = "" """.trimIndent() ) } + + // https://github.com/pinterest/ktlint/issues/1057 + @Test + fun testLintWithEolComment() { + assertThat( + SpacingAroundColonRule().lint( + """ + val x // comment + : Int = 1 + """.trimIndent() + ) + ).isEqualTo( + listOf( + LintError(1, 17, "colon-spacing", "Unexpected newline before \":\""), + ) + ) + } + + @Test + fun testFormatWithEolComment() { + assertThat( + SpacingAroundColonRule().format( + """ + val x // comment + : Int = 1 + """.trimIndent() + ) + ).isEqualTo( + """ + val x: // comment + Int = 1 + """.trimIndent() + ) + } } From 0022b7f4375afdea1421f8029fc6e14f9cbf6e0a Mon Sep 17 00:00:00 2001 From: Toshiaki Kameyama Date: Sat, 16 Jan 2021 09:33:02 +0900 Subject: [PATCH 32/53] Place type on the same line with declaration name --- .../standard/SpacingAroundColonRule.kt | 57 +++++--- .../standard/SpacingAroundColonRuleTest.kt | 132 ++++++++++++++++-- 2 files changed, 162 insertions(+), 27 deletions(-) diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/SpacingAroundColonRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/SpacingAroundColonRule.kt index aa8e841155..2a8510da4d 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/SpacingAroundColonRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/SpacingAroundColonRule.kt @@ -3,6 +3,7 @@ package com.pinterest.ktlint.ruleset.standard import com.pinterest.ktlint.core.Rule import com.pinterest.ktlint.core.ast.ElementType.ANNOTATION import com.pinterest.ktlint.core.ast.ElementType.ANNOTATION_ENTRY +import com.pinterest.ktlint.core.ast.ElementType.EQ import com.pinterest.ktlint.core.ast.isPartOf import com.pinterest.ktlint.core.ast.isPartOfComment import com.pinterest.ktlint.core.ast.isPartOfString @@ -15,11 +16,15 @@ import com.pinterest.ktlint.core.ast.upsertWhitespaceBeforeMe import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement +import org.jetbrains.kotlin.psi.KtBlockExpression import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtConstructor +import org.jetbrains.kotlin.psi.KtNamedFunction +import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.psi.KtTypeConstraint import org.jetbrains.kotlin.psi.KtTypeParameterList -import org.jetbrains.kotlin.psi.psiUtil.prevLeafs +import org.jetbrains.kotlin.psi.psiUtil.siblings +import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull class SpacingAroundColonRule : Rule("colon-spacing") { @@ -42,24 +47,44 @@ class SpacingAroundColonRule : Rule("colon-spacing") { if (prevLeaf != null && prevLeaf.isWhiteSpaceWithNewline()) { emit(prevLeaf.startOffset, "Unexpected newline before \":\"", true) if (autoCorrect) { - if (prevLeaf.prevLeaf()?.isPartOfComment() == true) { - val nextLeaf = node.nextLeaf() - val prevNonCodeLeaves = - node.prevLeafs.takeWhile { it.node.isWhiteSpace() || it.node.isPartOfComment() }.toList() - prevNonCodeLeaves.reversed().forEach { - node.treeParent.addChild(it.node, nextLeaf) + val parent = node.parent + val prevNonCodeElements = node.siblings(forward = false, withItself = false) + .takeWhile { it.node.isWhiteSpace() || it.node.isPartOfComment() }.toList() + when { + parent is KtProperty || parent is KtNamedFunction -> { + val equalsSignElement = node.siblings(forward = true, withItself = false) + .firstOrNull { it.node.elementType == EQ } + if (equalsSignElement != null) { + equalsSignElement.nextSibling?.takeIf { it.node.isWhiteSpace() }?.delete() + prevNonCodeElements.forEach { parent.addAfter(it, equalsSignElement) } + } + val blockElement = node.siblings(forward = true, withItself = false) + .firstIsInstanceOrNull() + if (blockElement != null) { + prevNonCodeElements + .let { if (it.first().node.isWhiteSpace()) it.drop(1) else it } + .forEach { blockElement.addAfter(it, blockElement.lBrace) } + } + parent.deleteChildRange(prevNonCodeElements.last(), prevNonCodeElements.first()) } - if (nextLeaf != null && nextLeaf.isWhiteSpace()) { - node.treeParent.removeChild(nextLeaf) + prevLeaf.prevLeaf()?.isPartOfComment() == true -> { + val nextLeaf = node.nextLeaf() + prevNonCodeElements.reversed().forEach { + node.treeParent.addChild(it.node, nextLeaf) + } + if (nextLeaf != null && nextLeaf.isWhiteSpace()) { + node.treeParent.removeChild(nextLeaf) + } } - } else { - val text = prevLeaf.text - if (removeSpacingBefore) { - prevLeaf.treeParent.removeChild(prevLeaf) - } else { - (prevLeaf as LeafPsiElement).rawReplaceWithText(" ") + else -> { + val text = prevLeaf.text + if (removeSpacingBefore) { + prevLeaf.treeParent.removeChild(prevLeaf) + } else { + (prevLeaf as LeafPsiElement).rawReplaceWithText(" ") + } + node.upsertWhitespaceAfterMe(text) } - node.upsertWhitespaceAfterMe(text) } } } diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/SpacingAroundColonRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/SpacingAroundColonRuleTest.kt index 921f270604..f7aed928bc 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/SpacingAroundColonRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/SpacingAroundColonRuleTest.kt @@ -254,42 +254,152 @@ class SpacingAroundColonRuleTest { */ B - val xmlFormatter: - String = "" + val xmlFormatter: String = + "" """.trimIndent() ) } // https://github.com/pinterest/ktlint/issues/1057 @Test - fun testLintWithEolComment() { + fun testLintNewLineBeforeColon() { assertThat( SpacingAroundColonRule().lint( """ - val x // comment - : Int = 1 + fun test() { + val v1 + : Int = 1 + + val v2 // comment + : Int = 1 + + val v3 + // comment + : Int = 1 + + fun f1() + : Int = 1 + + fun f2() // comment + : Int = 1 + + fun f3() + // comment + : Int = 1 + + fun g1() + : Int { + return 1 + } + + fun g2() // comment + : Int { + return 1 + } + + fun g3() + // comment + : Int { + return 1 + } + } """.trimIndent() ) ).isEqualTo( listOf( - LintError(1, 17, "colon-spacing", "Unexpected newline before \":\""), + LintError(2, 11, "colon-spacing", "Unexpected newline before \":\""), + LintError(5, 22, "colon-spacing", "Unexpected newline before \":\""), + LintError(9, 19, "colon-spacing", "Unexpected newline before \":\""), + LintError(12, 13, "colon-spacing", "Unexpected newline before \":\""), + LintError(15, 24, "colon-spacing", "Unexpected newline before \":\""), + LintError(19, 19, "colon-spacing", "Unexpected newline before \":\""), + LintError(22, 13, "colon-spacing", "Unexpected newline before \":\""), + LintError(27, 24, "colon-spacing", "Unexpected newline before \":\""), + LintError(33, 19, "colon-spacing", "Unexpected newline before \":\""), ) ) } @Test - fun testFormatWithEolComment() { + fun testFormatNewLineBeforeColon() { assertThat( SpacingAroundColonRule().format( """ - val x // comment - : Int = 1 + fun test() { + val v1 + : Int = 1 + + val v2 // comment + : Int = 1 + + val v3 + // comment + : Int = 1 + + fun f1() + : Int = 1 + + fun f2() // comment + : Int = 1 + + fun f3() + // comment + : Int = 1 + + fun g1() + : Int { + return 1 + } + + fun g2() // comment + : Int { + return 1 + } + + fun g3() + // comment + : Int { + return 1 + } + } """.trimIndent() ) ).isEqualTo( """ - val x: // comment - Int = 1 + fun test() { + val v1: Int = + 1 + + val v2: Int = // comment + 1 + + val v3: Int = + // comment + 1 + + fun f1(): Int = + 1 + + fun f2(): Int = // comment + 1 + + fun f3(): Int = + // comment + 1 + + fun g1(): Int { + return 1 + } + + fun g2(): Int { // comment + return 1 + } + + fun g3(): Int { + // comment + return 1 + } + } """.trimIndent() ) } From 83c35477f590fb116919b6e2b7a2e33eb77f0bd9 Mon Sep 17 00:00:00 2001 From: Toshiaki Kameyama Date: Fri, 15 Jan 2021 14:48:35 +0900 Subject: [PATCH 33/53] Fix false positive after `else` keyword (`argument-list-wrapping`) --- CHANGELOG.md | 1 + .../experimental/ArgumentListWrappingRule.kt | 6 ++++-- .../ArgumentListWrappingRuleTest.kt | 18 ++++++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a391b0e7a8..85aacb2877 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Don't remove the equals sign for a default argument (`no-line-break-before-assignment`) ([#1039](https://github.com/pinterest/ktlint/issues/1039)) - Fix internal error in `no-unused-imports` ([#1040](https://github.com/pinterest/ktlint/issues/1040)) - Fix false positives when declaration has tail comments (`spacing-between-declarations-with-comments`) ([#1053](https://github.com/pinterest/ktlint/issues/1053)) +- Fix false positive after `else` keyword (`argument-list-wrapping`) ([#1047](https://github.com/pinterest/ktlint/issues/1047)) - Fix formatting with comments (`colon-spacing`) ([#1057](https://github.com/pinterest/ktlint/issues/1057)) ### Changed diff --git a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRule.kt b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRule.kt index aa29ff9fb5..a688e97b6f 100644 --- a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRule.kt +++ b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRule.kt @@ -4,6 +4,7 @@ import com.pinterest.ktlint.core.KtLint import com.pinterest.ktlint.core.Rule import com.pinterest.ktlint.core.ast.ElementType import com.pinterest.ktlint.core.ast.ElementType.DOT_QUALIFIED_EXPRESSION +import com.pinterest.ktlint.core.ast.ElementType.ELSE import com.pinterest.ktlint.core.ast.ElementType.TYPE_ARGUMENT_LIST import com.pinterest.ktlint.core.ast.children import com.pinterest.ktlint.core.ast.column @@ -113,7 +114,7 @@ class ArgumentListWrappingRule : Rule("argument-list-wrapping") { // RPAR val lineIndent = node.lineIndent() val indent = ("\n" + lineIndent.substring(0, (lineIndent.length - adjustedIndent).coerceAtLeast(0))) - .let { if (node.isOnSameLineAsIfKeyword()) it + " ".repeat(indentSize) else it } + .let { if (node.isOnSameLineAsControlFlowKeyword()) it + " ".repeat(indentSize) else it } val paramIndent = indent + " ".repeat(indentSize) nextChild@ for (child in node.children()) { when (child.elementType) { @@ -230,8 +231,9 @@ class ArgumentListWrappingRule : Rule("argument-list-wrapping") { return null } - private fun ASTNode.isOnSameLineAsIfKeyword(): Boolean { + private fun ASTNode.isOnSameLineAsControlFlowKeyword(): Boolean { val containerNode = psi.getStrictParentOfType() ?: return false + if (containerNode.node.elementType == ELSE) return false return when (val parent = containerNode.parent) { is KtIfExpression, is KtWhileExpression -> parent.node is KtDoWhileExpression -> parent.whileKeyword?.node diff --git a/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRuleTest.kt b/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRuleTest.kt index e4d1078bf4..1e221e6b7b 100644 --- a/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRuleTest.kt +++ b/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRuleTest.kt @@ -420,6 +420,24 @@ class ArgumentListWrappingRuleTest { ).isEmpty() } + @Test + fun testLintAfterElse() { + assertThat( + ArgumentListWrappingRule().lint( + """ + fun foo(i: Int, j: Int) = 1 + + fun test() { + val x = if (true) 1 else foo( + 2, + 3 + ) + } + """.trimIndent() + ) + ).isEmpty() + } + @Test fun testLintInWhenCondition() { assertThat( From 0a3a1a626a228adcb0411879b77f13566ce6f620 Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Sun, 17 Jan 2021 09:51:11 +0100 Subject: [PATCH 34/53] Run CI checks addintionally on Windows OS. --- .github/workflows/gradle-pr-build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/gradle-pr-build.yml b/.github/workflows/gradle-pr-build.yml index 095fe00162..fffdb851f3 100644 --- a/.github/workflows/gradle-pr-build.yml +++ b/.github/workflows/gradle-pr-build.yml @@ -6,10 +6,11 @@ on: jobs: build: - runs-on: ubuntu-latest strategy: matrix: + os: [ubuntu-latest, windows-latest] java: [ 8, 11 ] + runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 - name: Set up Java ${{ matrix.java }} From 34461a22db6612e38109ec26c74bff7563b0f8bd Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Sun, 17 Jan 2021 10:19:40 +0100 Subject: [PATCH 35/53] Upload depedency verification report on build error. --- .github/workflows/gradle-pr-build.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/gradle-pr-build.yml b/.github/workflows/gradle-pr-build.yml index fffdb851f3..549e025780 100644 --- a/.github/workflows/gradle-pr-build.yml +++ b/.github/workflows/gradle-pr-build.yml @@ -21,5 +21,12 @@ jobs: run: chmod +x gradlew - name: Build with release Kotlin version run: ./gradlew clean build ktlint + - name: Upload artifacts + uses: actions/upload-artifact@v2 + if: failure() + with: + name: Build artifacts + path: '**/build/**/dependency-verification-report.html' + if-no-files-found: warn - name: Build with dev Kotlin version run: ./gradlew -PkotlinDev clean build ktlint From e94424e7a51541b86426ec8a3d9d7d0b4bfbd438 Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Wed, 20 Jan 2021 22:38:49 +0100 Subject: [PATCH 36/53] Update Kotlin dev version to 1.4.30-RC. --- build.gradle | 2 +- gradle/verification-metadata.xml | 12 ++++-------- settings.gradle | 2 +- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/build.gradle b/build.gradle index a7b89537ef..4431edb698 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { } ext.versions = [ - 'kotlin': gradle.ext.isKotlinDev ? "1.4.30-M1" : "1.4.10", + 'kotlin': gradle.ext.isKotlinDev ? "1.4.30-RC" : "1.4.10", 'gradle': '6.6', 'gradle-sha256': 'e6f83508f0970452f56197f610d13c5f593baaf43c0e3c6a571e5967be754025' ] diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 2a7dc95a2a..e3e27984d4 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -462,6 +462,7 @@ + @@ -955,14 +956,9 @@ - - - - - - - - + + + diff --git a/settings.gradle b/settings.gradle index 9216e1a6b7..50443064ba 100644 --- a/settings.gradle +++ b/settings.gradle @@ -9,7 +9,7 @@ pluginManagement { } plugins { - def kotlinVersion = settings.hasProperty("kotlinDev") ? "1.4.30-M1" : "1.4.10" + def kotlinVersion = settings.hasProperty("kotlinDev") ? "1.4.30-RC" : "1.4.10" id 'org.jetbrains.kotlin.jvm' version kotlinVersion id 'com.github.breadmoirai.github-release' version '2.2.12' id 'com.github.johnrengelman.shadow' version '6.1.0' From 4773646d47ce95863f5a6c749da127c316871b99 Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Mon, 25 Jan 2021 21:29:29 +0100 Subject: [PATCH 37/53] Update Gradle to 6.8.1 version. Changelog: https://docs.gradle.org/6.8.1/release-notes.html Has to slightly increase Gradle maximum allowed memory usage to prevent heap out of space sporadic errors. --- CHANGELOG.md | 1 + build.gradle | 4 ++-- gradle.properties | 1 + gradle/verification-metadata.xml | 14 ++++++++++++++ gradle/wrapper/gradle-wrapper.jar | Bin 58910 -> 59203 bytes gradle/wrapper/gradle-wrapper.properties | 4 ++-- gradlew | 2 +- gradlew.bat | 21 +++------------------ 8 files changed, 24 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 85aacb2877..65dd8d21a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). that the Kotlin IDE plugin and ktlint use same imports layout ([#753](https://github.com/pinterest/ktlint/issues/753)) - Deprecated `idea` and `ascii` shortcuts as the `ij_kotlin_imports_layout` property does not support those. Please check README on how to achieve those with patterns ([#753](https://github.com/pinterest/ktlint/issues/753)) +- Update Gradle to `6.8.1` version ### Removed diff --git a/build.gradle b/build.gradle index 4431edb698..fb446ecd70 100644 --- a/build.gradle +++ b/build.gradle @@ -5,8 +5,8 @@ plugins { ext.versions = [ 'kotlin': gradle.ext.isKotlinDev ? "1.4.30-RC" : "1.4.10", - 'gradle': '6.6', - 'gradle-sha256': 'e6f83508f0970452f56197f610d13c5f593baaf43c0e3c6a571e5967be754025' + 'gradle': '6.8.1', + 'gradle-sha256': 'fd591a34af7385730970399f473afabdb8b28d57fd97d6625c388d090039d6fd' ] ext.deps = [ diff --git a/gradle.properties b/gradle.properties index 3ad6519abb..4057ec66fe 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,6 +15,7 @@ POM_DEVELOPER_ID=pinterest POM_DEVELOPER_NAME=Pinterest, Inc. # Gradle properties +org.gradle.jvmargs=-Xmx768m org.gradle.parallel=true org.gradle.caching=true org.gradle.vfs.watch=true diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index e3e27984d4..b26836ee33 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -16,6 +16,7 @@ + @@ -693,6 +694,14 @@ + + + + + + + + @@ -706,6 +715,11 @@ + + + + + diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 62d4c053550b91381bbd28b1afc82d634bf73a8a..e708b1c023ec8b20f512888fe07c5bd3ff77bb8f 100644 GIT binary patch delta 6656 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 Yte!%o?r4GTrVtxD61Ym!|5fq-1K$0e!T1w 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 8174453a1a..f604ed1642 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=3239b5ed86c3838a37d983ac100573f64c1f3fd8e1eb6c89fa5f9529b5ec091d -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip +distributionSha256Sum=fd591a34af7385730970399f473afabdb8b28d57fd97d6625c388d090039d6fd +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index fbd7c51583..4f906e0c81 100755 --- a/gradlew +++ b/gradlew @@ -130,7 +130,7 @@ fi if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath diff --git a/gradlew.bat b/gradlew.bat index 5093609d51..107acd32c4 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -54,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -64,21 +64,6 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line @@ -86,7 +71,7 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell From 0bfe7e6e0a0d69890926daf6a1b7821302f5bd7b Mon Sep 17 00:00:00 2001 From: Aaron Madlon-Kay Date: Fri, 12 Feb 2021 02:18:07 +0900 Subject: [PATCH 38/53] Mention MacPorts in readme (#1069) MacPorts also works on Linux --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 87142103e4..e9258dff5a 100644 --- a/README.md +++ b/README.md @@ -155,7 +155,7 @@ curl -sSLO https://github.com/pinterest/ktlint/releases/download/0.40.0/ktlint & * (Releases up through 0.31.0) `curl -sS https://keybase.io/shyiko/pgp_keys.asc | gpg --import && gpg --verify ktlint.asc` * (Releases from 0.32.0 on) `curl -sS https://keybase.io/ktlint/pgp_keys.asc | gpg --import && gpg --verify ktlint.asc` -On macOS ([or Linux](http://linuxbrew.sh/)) you can also use [brew](https://brew.sh/) - `brew install ktlint`. +On macOS ([or Linux](http://linuxbrew.sh/)) you can also use [brew](https://brew.sh/) - `brew install ktlint` - or [MacPorts](https://www.macports.org/) - `port install ktlint`. > If you don't have curl installed - replace `curl -sL` with `wget -qO-`. From 22ff1a34e67174dd8f1ae1e58189dc90cfb734b1 Mon Sep 17 00:00:00 2001 From: Toshiaki Kameyama Date: Mon, 15 Feb 2021 04:34:01 +0900 Subject: [PATCH 39/53] Fix IndexOutOfBoundsException in `argument-list-wrapping-rule` formatting file with many corrections (#1083) --- CHANGELOG.md | 1 + .../experimental/ArgumentListWrappingRule.kt | 15 ++- .../ArgumentListWrappingRuleTest.kt | 95 +++++++++++++++++++ 3 files changed, 107 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65dd8d21a4..675f5ccab1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Fix false positives when declaration has tail comments (`spacing-between-declarations-with-comments`) ([#1053](https://github.com/pinterest/ktlint/issues/1053)) - Fix false positive after `else` keyword (`argument-list-wrapping`) ([#1047](https://github.com/pinterest/ktlint/issues/1047)) - Fix formatting with comments (`colon-spacing`) ([#1057](https://github.com/pinterest/ktlint/issues/1057)) +- Fix IndexOutOfBoundsException in `argument-list-wrapping-rule` formatting file with many corrections ([#1081](https://github.com/pinterest/ktlint/issues/1081)) ### Changed - Update Gradle shadow plugin to `6.1.0` version diff --git a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRule.kt b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRule.kt index a688e97b6f..bbe4eea0b6 100644 --- a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRule.kt +++ b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRule.kt @@ -14,7 +14,6 @@ import com.pinterest.ktlint.core.ast.isRoot import com.pinterest.ktlint.core.ast.isWhiteSpace import com.pinterest.ktlint.core.ast.isWhiteSpaceWithNewline import com.pinterest.ktlint.core.ast.lineIndent -import com.pinterest.ktlint.core.ast.lineNumber import com.pinterest.ktlint.core.ast.prevLeaf import com.pinterest.ktlint.core.ast.visit import kotlin.math.max @@ -234,10 +233,18 @@ class ArgumentListWrappingRule : Rule("argument-list-wrapping") { private fun ASTNode.isOnSameLineAsControlFlowKeyword(): Boolean { val containerNode = psi.getStrictParentOfType() ?: return false if (containerNode.node.elementType == ELSE) return false - return when (val parent = containerNode.parent) { - is KtIfExpression, is KtWhileExpression -> parent.node + val controlFlowKeyword = when (val parent = containerNode.parent) { + is KtIfExpression -> parent.ifKeyword.node + is KtWhileExpression -> parent.firstChild.node is KtDoWhileExpression -> parent.whileKeyword?.node else -> null - }?.lineNumber() == lineNumber() + } ?: return false + + var prevLeaf = prevLeaf() ?: return false + while (prevLeaf != controlFlowKeyword) { + if (prevLeaf.isWhiteSpaceWithNewline()) return false + prevLeaf = prevLeaf.prevLeaf() ?: return false + } + return true } } diff --git a/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRuleTest.kt b/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRuleTest.kt index 1e221e6b7b..1ddd57c364 100644 --- a/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRuleTest.kt +++ b/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRuleTest.kt @@ -589,4 +589,99 @@ class ArgumentListWrappingRuleTest { ) ).isEmpty() } + + // https://github.com/pinterest/ktlint/issues/1081 + @Test + fun testManyCorrections() { + assertThat( + ArgumentListWrappingRule().format( + """ + package com.foo + + class MyClass() { + private fun doSomething() { + if (d == 0 || e == 0f) { + c.ee(hh, d, d, yy) + } else { + foo.blah() + val dr = t - u + val xx = -gg(dr) * rr(2f * d, dr.hh) + foo.bar( + dd, + g - d + ) + foo.baz( + a.b, a.c - d + ) + foo.biz( + a.b, a.c - d, + a.b, a.c, + a.b + d, a.c + ) + foo.baz( + a.x - d, a.c + ) + foo.biz( + a.x - d, a.c, + a.x, a.c, + a.x, a.c - d + ) + foo.baz( + a.x, a.j + d + ) + } + } + } + """.trimIndent() + ) + ).isEqualTo( + """ + package com.foo + + class MyClass() { + private fun doSomething() { + if (d == 0 || e == 0f) { + c.ee(hh, d, d, yy) + } else { + foo.blah() + val dr = t - u + val xx = -gg(dr) * rr(2f * d, dr.hh) + foo.bar( + dd, + g - d + ) + foo.baz( + a.b, + a.c - d + ) + foo.biz( + a.b, + a.c - d, + a.b, + a.c, + a.b + d, + a.c + ) + foo.baz( + a.x - d, + a.c + ) + foo.biz( + a.x - d, + a.c, + a.x, + a.c, + a.x, + a.c - d + ) + foo.baz( + a.x, + a.j + d + ) + } + } + } + """.trimIndent() + ) + } } From 53fe9ab402fb7b793f4ebf7d4605ce326926b687 Mon Sep 17 00:00:00 2001 From: Toshiaki Kameyama Date: Tue, 16 Feb 2021 05:03:00 +0900 Subject: [PATCH 40/53] Fix formatting in arguments (`multiline-if-else`) (#1082) --- CHANGELOG.md | 1 + .../experimental/MultiLineIfElseRule.kt | 6 ++-- .../experimental/MultiLineIfElseRuleTest.kt | 32 +++++++++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 675f5ccab1..99a62d2e8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Fix false positive after `else` keyword (`argument-list-wrapping`) ([#1047](https://github.com/pinterest/ktlint/issues/1047)) - Fix formatting with comments (`colon-spacing`) ([#1057](https://github.com/pinterest/ktlint/issues/1057)) - Fix IndexOutOfBoundsException in `argument-list-wrapping-rule` formatting file with many corrections ([#1081](https://github.com/pinterest/ktlint/issues/1081)) +- Fix formatting in arguments (`multiline-if-else`) ([#1079](https://github.com/pinterest/ktlint/issues/1079)) ### Changed - Update Gradle shadow plugin to `6.1.0` version diff --git a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/MultiLineIfElseRule.kt b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/MultiLineIfElseRule.kt index 549c82e7b3..9e11d82dd1 100644 --- a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/MultiLineIfElseRule.kt +++ b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/MultiLineIfElseRule.kt @@ -7,7 +7,8 @@ import com.pinterest.ktlint.core.ast.ElementType.LBRACE import com.pinterest.ktlint.core.ast.ElementType.RBRACE import com.pinterest.ktlint.core.ast.ElementType.RPAR import com.pinterest.ktlint.core.ast.ElementType.THEN -import com.pinterest.ktlint.core.ast.isWhiteSpaceWithNewline +import com.pinterest.ktlint.core.ast.isPartOfComment +import com.pinterest.ktlint.core.ast.isWhiteSpaceWithoutNewline import com.pinterest.ktlint.core.ast.prevLeaf import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace @@ -45,7 +46,8 @@ class MultiLineIfElseRule : Rule("multiline-if-else") { private fun autocorrect(node: ASTNode) { val prevLeaves = node.leaves(forward = false).takeWhile { it.elementType !in listOf(RPAR, ELSE_KEYWORD) }.toList().reversed() - val nextLeaves = node.leaves(forward = true).takeWhile { !it.isWhiteSpaceWithNewline() }.toList() + val nextLeaves = + node.leaves(forward = true).takeWhile { it.isWhiteSpaceWithoutNewline() || it.isPartOfComment() }.toList() val rightBraceIndent = node.treeParent .prevLeaf { it is PsiWhiteSpace && it.textContains('\n') }?.text.orEmpty() .let { "\n${it.substringAfterLast("\n")}" } diff --git a/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/MultiLineIfElseRuleTest.kt b/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/MultiLineIfElseRuleTest.kt index 1203490fb2..6ab67b4313 100644 --- a/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/MultiLineIfElseRuleTest.kt +++ b/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/MultiLineIfElseRuleTest.kt @@ -416,6 +416,38 @@ class MultiLineIfElseRuleTest { ) } + // https://github.com/pinterest/ktlint/issues/1079 + @Test + fun testInArgument() { + val actual = format( + """ + fun foo(x: Int, y: Int, z: Int) {} + fun test(a: Int, b: Int, c: Int, d: Int, bar: Boolean) { + foo( + a, + if (bar) b else + c, + d + ) + } + """.trimIndent() + ) + assertThat(actual).isEqualTo( + """ + fun foo(x: Int, y: Int, z: Int) {} + fun test(a: Int, b: Int, c: Int, d: Int, bar: Boolean) { + foo( + a, + if (bar) b else { + c + }, + d + ) + } + """.trimIndent() + ) + } + private fun assertOK(kotlinScript: String) { assertThat(format(kotlinScript)).isEqualTo(kotlinScript) assertThat(lint(kotlinScript)).isEqualTo(emptyList()) From 61a5a3832bfcaeac9cb737a793dde98d43845421 Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Sun, 21 Feb 2021 15:11:42 +0100 Subject: [PATCH 41/53] Fix Kotlin compiler fails to initialize. Initialization error happens only when using from KtLint CLI with Kotlin versions 1.4.20+. Via `java jar ktlint-cli.jar` approach Kotlin compiler initializes without failures. For now added workaround, but, in the future, CLI approach should revisited. --- .../com/pinterest/ktlint/core/KtLint.kt | 21 ++- .../core/internal/KotlinPsiFileFactory.kt | 178 ++++++++++++------ .../pinterest/ktlint/internal/FileUtils.kt | 6 +- 3 files changed, 143 insertions(+), 62 deletions(-) diff --git a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/KtLint.kt b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/KtLint.kt index ea1781d409..c44029cd80 100644 --- a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/KtLint.kt +++ b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/KtLint.kt @@ -6,11 +6,11 @@ import com.pinterest.ktlint.core.ast.visit import com.pinterest.ktlint.core.internal.EditorConfigGenerator import com.pinterest.ktlint.core.internal.EditorConfigLoader import com.pinterest.ktlint.core.internal.EditorConfigLoader.Companion.convertToRawValues +import com.pinterest.ktlint.core.internal.KotlinPsiFileFactory import com.pinterest.ktlint.core.internal.LineAndColumn import com.pinterest.ktlint.core.internal.SuppressionLocator import com.pinterest.ktlint.core.internal.buildPositionInTextLocator import com.pinterest.ktlint.core.internal.buildSuppressedRegionsLocator -import com.pinterest.ktlint.core.internal.initPsiFileFactory import com.pinterest.ktlint.core.internal.noSuppression import java.nio.file.FileSystems import java.nio.file.Path @@ -36,7 +36,7 @@ public object KtLint { private const val UTF8_BOM = "\uFEFF" public const val STDIN_FILE: String = "" - private val psiFileFactory: PsiFileFactory = initPsiFileFactory() + private val kotlinPsiFileFactory = KotlinPsiFileFactory() private val editorConfigLoader = EditorConfigLoader(FileSystems.getDefault()) @OptIn(FeatureInAlphaState::class) @@ -51,6 +51,9 @@ public object KtLint { * @param script true if this is a Kotlin script file * @param editorConfigPath optional path of the .editorconfig file (otherwise will use working directory) * @param debug True if invoked with the --debug flag + * @param isInvokedFromCli **For internal use only**: indicates that linting was invoked from KtLint CLI tool. + * Enables some internals workarounds for Kotlin Compiler initialization. + * Usually you don't need to use it and most probably it will be removed in one of next versions. */ public data class Params( val fileName: String? = null, @@ -60,7 +63,8 @@ public object KtLint { val cb: (e: LintError, corrected: Boolean) -> Unit, val script: Boolean = false, val editorConfigPath: String? = null, - val debug: Boolean = false + val debug: Boolean = false, + val isInvokedFromCli: Boolean = false, ) { internal val normalizedFilePath: Path? get() = if (fileName == STDIN_FILE || fileName == null) { null @@ -84,7 +88,8 @@ public object KtLint { * @throws RuleExecutionException in case of internal failure caused by a bug in rule implementation */ public fun lint(params: Params) { - val preparedCode = prepareCodeForLinting(params) + val psiFileFactory = kotlinPsiFileFactory.acquirePsiFileFactory(params.isInvokedFromCli) + val preparedCode = prepareCodeForLinting(psiFileFactory, params) val errors = mutableListOf() visitor(preparedCode.rootNode, params.ruleSets).invoke { node, rule, fqRuleId -> @@ -114,9 +119,12 @@ public object KtLint { errors .sortedWith { l, r -> if (l.line != r.line) l.line - r.line else l.col - r.col } .forEach { e -> params.cb(e, false) } + + kotlinPsiFileFactory.releasePsiFileFactory() } private fun prepareCodeForLinting( + psiFileFactory: PsiFileFactory, params: Params ): PreparedCode { val normalizedText = normalizeText(params.text) @@ -291,7 +299,8 @@ public object KtLint { */ public fun format(params: Params): String { val hasUTF8BOM = params.text.startsWith(UTF8_BOM) - val preparedCode = prepareCodeForLinting(params) + val psiFileFactory = kotlinPsiFileFactory.acquirePsiFileFactory(params.isInvokedFromCli) + val preparedCode = prepareCodeForLinting(psiFileFactory, params) var tripped = false var mutated = false @@ -361,6 +370,8 @@ public object KtLint { .rootNode .text .replace("\n", determineLineSeparator(params.text, params.userData)) + + kotlinPsiFileFactory.releasePsiFileFactory() return if (hasUTF8BOM) { UTF8_BOM + code } else { diff --git a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/KotlinPsiFileFactory.kt b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/KotlinPsiFileFactory.kt index b227a230c9..5fc64da889 100644 --- a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/KotlinPsiFileFactory.kt +++ b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/KotlinPsiFileFactory.kt @@ -1,5 +1,9 @@ package com.pinterest.ktlint.core.internal +import java.nio.file.Files +import java.nio.file.Path +import java.util.concurrent.atomic.AtomicInteger +import java.util.zip.ZipFile import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles @@ -18,79 +22,143 @@ import org.jetbrains.kotlin.com.intellij.pom.tree.TreeAspect import org.jetbrains.kotlin.com.intellij.psi.PsiFileFactory import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.TreeCopyHandler import org.jetbrains.kotlin.config.CompilerConfiguration +import org.jetbrains.kotlin.utils.PathUtil import sun.reflect.ReflectionFactory /** - * Initialize Kotlin Lexer. + * Initializes Kotlin compiler and disposes it when it is not needed anymore. */ -internal fun initPsiFileFactory(): PsiFileFactory { - DiagnosticLogger.setFactory(LoggerFactory::class.java) +internal class KotlinPsiFileFactory { + private var psiFileFactory: PsiFileFactory? = null + private var tempFiles: Path? = null + private val acquireCounter = AtomicInteger(0) + private val initializerLock = Any() - val compilerConfiguration = CompilerConfiguration() - compilerConfiguration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, MessageCollector.NONE) + internal fun acquirePsiFileFactory(isFromCli: Boolean): PsiFileFactory { + acquireCounter.incrementAndGet() + return psiFileFactory ?: synchronized(initializerLock) { + initializePsiFileFactory(isFromCli).also { + psiFileFactory = it + } + } + } - val project = KotlinCoreEnvironment.createForProduction( - {}, - compilerConfiguration, - EnvironmentConfigFiles.JVM_CONFIG_FILES - ).project as MockProject + internal fun releasePsiFileFactory() { + val acquiredCount = acquireCounter.decrementAndGet() + if (acquiredCount == 0) synchronized(initializerLock) { + tempFiles?.toFile()?.deleteRecursively() + tempFiles = null + psiFileFactory = null + } + } - project.enableASTMutations() + /** + * Initialize Kotlin Lexer. + */ + private fun initializePsiFileFactory(isFromCli: Boolean): PsiFileFactory { + DiagnosticLogger.setFactory(LoggerFactory::class.java) - return PsiFileFactory.getInstance(project) -} + val compilerConfiguration = CompilerConfiguration() + compilerConfiguration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, MessageCollector.NONE) + tempFiles = if (isFromCli) { + // Special workaround for KtLint CLI + // See https://github.com/pinterest/ktlint/issues/1063 for details + validateCompilerExtensionsFilesPath().also { + compilerConfiguration.put( + CLIConfigurationKeys.INTELLIJ_PLUGIN_ROOT, + it.toAbsolutePath().toString() + ) + } + } else { + null + } -/** - * Do not print anything to the stderr when lexer is unable to match input. - */ -private class LoggerFactory : DiagnosticLogger.Factory { - override fun getLoggerInstance( - p: String - ): DiagnosticLogger = object : DefaultLogger(null) { - override fun warn(message: String?, t: Throwable?) {} - override fun error(message: String?, vararg details: String?) {} + val project = KotlinCoreEnvironment.createForProduction( + {}, + compilerConfiguration, + EnvironmentConfigFiles.JVM_CONFIG_FILES + ).project as MockProject + + project.enableASTMutations() + + return PsiFileFactory.getInstance(project) } -} -/** - * Enables AST mutations (`ktlint -F ...`). - */ -private fun MockProject.enableASTMutations() { - val extensionPoint = "org.jetbrains.kotlin.com.intellij.treeCopyHandler" - val extensionClassName = TreeCopyHandler::class.java.name - for (area in arrayOf(extensionArea, getRootArea())) { - if (!area.hasExtensionPoint(extensionPoint)) { - area.registerExtensionPoint(extensionPoint, extensionClassName, ExtensionPoint.Kind.INTERFACE) + private fun validateCompilerExtensionsFilesPath(): Path { + val tempDir = Files.createTempDirectory("ktlint") + val extensionsDir = tempDir.resolve("META-INF").resolve("extensions") + if (!Files.exists(extensionsDir)) { + Files.createDirectories(extensionsDir) + val resourcesPath = PathUtil.getResourcePathForClass(DiagnosticLogger::class.java) + ZipFile(resourcesPath).use { zipFile -> + zipFile.getInputStream( + zipFile.getEntry("META-INF/extensions/core.xml") + ).copyTo( + Files.newOutputStream(extensionsDir.resolve("core.xml")) + ) + zipFile.getInputStream( + zipFile.getEntry("META-INF/extensions/compiler.xml") + ).copyTo( + Files.newOutputStream(extensionsDir.resolve("compiler.xml")) + ) + } } + + return tempDir } - registerService(PomModel::class.java, FormatPomModel()) -} + /** + * Enables AST mutations (`ktlint -F ...`). + */ + private fun MockProject.enableASTMutations() { + val extensionPoint = "org.jetbrains.kotlin.com.intellij.treeCopyHandler" + val extensionClassName = TreeCopyHandler::class.java.name + for (area in arrayOf(extensionArea, getRootArea())) { + if (!area.hasExtensionPoint(extensionPoint)) { + area.registerExtensionPoint(extensionPoint, extensionClassName, ExtensionPoint.Kind.INTERFACE) + } + } -private class FormatPomModel : UserDataHolderBase(), PomModel { + registerService(PomModel::class.java, FormatPomModel()) + } - override fun runTransaction( - transaction: PomTransaction - ) { - (transaction as PomTransactionBase).run() + /** + * Do not print anything to the stderr when lexer is unable to match input. + */ + private class LoggerFactory : DiagnosticLogger.Factory { + override fun getLoggerInstance( + p: String + ): DiagnosticLogger = object : DefaultLogger(null) { + override fun warn(message: String?, t: Throwable?) {} + override fun error(message: String?, vararg details: String?) {} + } } - @Suppress("UNCHECKED_CAST") - override fun getModelAspect( - aspect: Class - ): T? { - if (aspect == TreeAspect::class.java) { - // using approach described in https://git.io/vKQTo due to the magical bytecode of TreeAspect - // (check constructor signature and compare it to the source) - // (org.jetbrains.kotlin:kotlin-compiler-embeddable:1.0.3) - val constructor = ReflectionFactory - .getReflectionFactory() - .newConstructorForSerialization( - aspect, - Any::class.java.getDeclaredConstructor(*arrayOfNulls>(0)) - ) - return constructor.newInstance() as T + private class FormatPomModel : UserDataHolderBase(), PomModel { + + override fun runTransaction( + transaction: PomTransaction + ) { + (transaction as PomTransactionBase).run() + } + + @Suppress("UNCHECKED_CAST") + override fun getModelAspect( + aspect: Class + ): T? { + if (aspect == TreeAspect::class.java) { + // using approach described in https://git.io/vKQTo due to the magical bytecode of TreeAspect + // (check constructor signature and compare it to the source) + // (org.jetbrains.kotlin:kotlin-compiler-embeddable:1.0.3) + val constructor = ReflectionFactory + .getReflectionFactory() + .newConstructorForSerialization( + aspect, + Any::class.java.getDeclaredConstructor(*arrayOfNulls>(0)) + ) + return constructor.newInstance() as T + } + return null } - return null } } diff --git a/ktlint/src/main/kotlin/com/pinterest/ktlint/internal/FileUtils.kt b/ktlint/src/main/kotlin/com/pinterest/ktlint/internal/FileUtils.kt index f57fbda851..18e91e37a5 100644 --- a/ktlint/src/main/kotlin/com/pinterest/ktlint/internal/FileUtils.kt +++ b/ktlint/src/main/kotlin/com/pinterest/ktlint/internal/FileUtils.kt @@ -79,7 +79,8 @@ internal fun lintFile( cb = { e, _ -> lintErrorCallback(e) }, - debug = debug + debug = debug, + isInvokedFromCli = true ) ) } @@ -105,6 +106,7 @@ internal fun formatFile( script = !fileName.endsWith(".kt", ignoreCase = true), editorConfigPath = editorConfigPath, cb = cb, - debug = debug + debug = debug, + isInvokedFromCli = true ) ) From 545857dbc04e54675b9defb423bae30efbe1d508 Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Sun, 21 Feb 2021 15:32:45 +0100 Subject: [PATCH 42/53] Update Kotlin to 1.4.30 version. Enable new JVM IR backend. --- CHANGELOG.md | 1 + build.gradle | 4 +- gradle/verification-metadata.xml | 174 +---------------------- ktlint-core/build.gradle | 2 - ktlint-reporter-baseline/build.gradle | 1 - ktlint-reporter-checkstyle/build.gradle | 1 - ktlint-reporter-html/build.gradle | 1 - ktlint-reporter-json/build.gradle | 1 - ktlint-reporter-plain/build.gradle | 1 - ktlint-ruleset-experimental/build.gradle | 1 - ktlint-ruleset-standard/build.gradle | 1 - ktlint-ruleset-template/build.gradle | 2 - ktlint-ruleset-test/build.gradle | 1 - ktlint-test/build.gradle | 1 - ktlint/build.gradle | 1 - settings.gradle | 2 +- 16 files changed, 7 insertions(+), 188 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99a62d2e8a..1965256fbe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Deprecated `idea` and `ascii` shortcuts as the `ij_kotlin_imports_layout` property does not support those. Please check README on how to achieve those with patterns ([#753](https://github.com/pinterest/ktlint/issues/753)) - Update Gradle to `6.8.1` version +- Update Kotlin to `1.4.30` version. Fixes [#1063](https://github.com/pinterest/ktlint/issues/1063). ### Removed diff --git a/build.gradle b/build.gradle index fb446ecd70..e45a257713 100644 --- a/build.gradle +++ b/build.gradle @@ -4,14 +4,13 @@ plugins { } ext.versions = [ - 'kotlin': gradle.ext.isKotlinDev ? "1.4.30-RC" : "1.4.10", + 'kotlin': gradle.ext.isKotlinDev ? "1.4.30-RC" : "1.4.30", 'gradle': '6.8.1', 'gradle-sha256': 'fd591a34af7385730970399f473afabdb8b28d57fd97d6625c388d090039d6fd' ] ext.deps = [ 'kotlin' : [ - 'stdlib' : "org.jetbrains.kotlin:kotlin-stdlib:${versions.kotlin}", 'compiler': "org.jetbrains.kotlin:kotlin-compiler-embeddable:${versions.kotlin}" ], 'klob' : 'com.github.shyiko.klob:klob:0.2.1', @@ -68,6 +67,7 @@ allprojects { kotlinOptions { jvmTarget = '1.8' apiVersion = '1.3' + useIR = true freeCompilerArgs = ["-Xopt-in=kotlin.RequiresOptIn"] } diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index b26836ee33..cf885b83c2 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -797,177 +797,9 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + diff --git a/ktlint-core/build.gradle b/ktlint-core/build.gradle index 9ae087970a..11e4ddf021 100644 --- a/ktlint-core/build.gradle +++ b/ktlint-core/build.gradle @@ -5,8 +5,6 @@ plugins { dependencies { api deps.kotlin.compiler - - implementation deps.kotlin.stdlib api deps.ec4j testImplementation deps.junit diff --git a/ktlint-reporter-baseline/build.gradle b/ktlint-reporter-baseline/build.gradle index 437ed77722..45e27eb4c3 100644 --- a/ktlint-reporter-baseline/build.gradle +++ b/ktlint-reporter-baseline/build.gradle @@ -5,7 +5,6 @@ plugins { dependencies { implementation project(':ktlint-core') - implementation deps.kotlin.stdlib testImplementation deps.junit testImplementation deps.assertj diff --git a/ktlint-reporter-checkstyle/build.gradle b/ktlint-reporter-checkstyle/build.gradle index 437ed77722..45e27eb4c3 100644 --- a/ktlint-reporter-checkstyle/build.gradle +++ b/ktlint-reporter-checkstyle/build.gradle @@ -5,7 +5,6 @@ plugins { dependencies { implementation project(':ktlint-core') - implementation deps.kotlin.stdlib testImplementation deps.junit testImplementation deps.assertj diff --git a/ktlint-reporter-html/build.gradle b/ktlint-reporter-html/build.gradle index 437ed77722..45e27eb4c3 100644 --- a/ktlint-reporter-html/build.gradle +++ b/ktlint-reporter-html/build.gradle @@ -5,7 +5,6 @@ plugins { dependencies { implementation project(':ktlint-core') - implementation deps.kotlin.stdlib testImplementation deps.junit testImplementation deps.assertj diff --git a/ktlint-reporter-json/build.gradle b/ktlint-reporter-json/build.gradle index 437ed77722..45e27eb4c3 100644 --- a/ktlint-reporter-json/build.gradle +++ b/ktlint-reporter-json/build.gradle @@ -5,7 +5,6 @@ plugins { dependencies { implementation project(':ktlint-core') - implementation deps.kotlin.stdlib testImplementation deps.junit testImplementation deps.assertj diff --git a/ktlint-reporter-plain/build.gradle b/ktlint-reporter-plain/build.gradle index 437ed77722..45e27eb4c3 100644 --- a/ktlint-reporter-plain/build.gradle +++ b/ktlint-reporter-plain/build.gradle @@ -5,7 +5,6 @@ plugins { dependencies { implementation project(':ktlint-core') - implementation deps.kotlin.stdlib testImplementation deps.junit testImplementation deps.assertj diff --git a/ktlint-ruleset-experimental/build.gradle b/ktlint-ruleset-experimental/build.gradle index 6e7acaa783..e6969dd511 100644 --- a/ktlint-ruleset-experimental/build.gradle +++ b/ktlint-ruleset-experimental/build.gradle @@ -5,7 +5,6 @@ plugins { dependencies { implementation project(':ktlint-core') - implementation deps.kotlin.stdlib testImplementation project(':ktlint-test') testImplementation deps.junit diff --git a/ktlint-ruleset-standard/build.gradle b/ktlint-ruleset-standard/build.gradle index 6e7acaa783..e6969dd511 100644 --- a/ktlint-ruleset-standard/build.gradle +++ b/ktlint-ruleset-standard/build.gradle @@ -5,7 +5,6 @@ plugins { dependencies { implementation project(':ktlint-core') - implementation deps.kotlin.stdlib testImplementation project(':ktlint-test') testImplementation deps.junit diff --git a/ktlint-ruleset-template/build.gradle b/ktlint-ruleset-template/build.gradle index 53ebbb4814..fa72e6d692 100644 --- a/ktlint-ruleset-template/build.gradle +++ b/ktlint-ruleset-template/build.gradle @@ -27,11 +27,9 @@ configurations { dependencies { compileOnly project(':ktlint-core') - compileOnly deps.kotlin.stdlib testImplementation project(':ktlint-core') testImplementation project(':ktlint-test') - testImplementation deps.kotlin.stdlib testImplementation deps.assertj testImplementation('org.jetbrains.spek:spek-junit-platform-engine:1.1.5') { exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib' diff --git a/ktlint-ruleset-test/build.gradle b/ktlint-ruleset-test/build.gradle index 30e2c5cb30..cc8404a1d9 100644 --- a/ktlint-ruleset-test/build.gradle +++ b/ktlint-ruleset-test/build.gradle @@ -5,5 +5,4 @@ plugins { dependencies { api project(':ktlint-core') - implementation deps.kotlin.stdlib } diff --git a/ktlint-test/build.gradle b/ktlint-test/build.gradle index 033f70e6b9..007fdd434d 100644 --- a/ktlint-test/build.gradle +++ b/ktlint-test/build.gradle @@ -6,7 +6,6 @@ plugins { dependencies { api project(':ktlint-core') api project(':ktlint-ruleset-test') - implementation deps.kotlin.stdlib implementation deps.assertj implementation deps.junit diff --git a/ktlint/build.gradle b/ktlint/build.gradle index eb48d5693b..0580f6d3e3 100644 --- a/ktlint/build.gradle +++ b/ktlint/build.gradle @@ -29,7 +29,6 @@ dependencies { implementation project(':ktlint-ruleset-experimental') implementation project(':ktlint-ruleset-standard') implementation project(':ktlint-ruleset-test') - implementation deps.kotlin.stdlib implementation deps.kotlin.compiler implementation deps.klob implementation deps.picocli diff --git a/settings.gradle b/settings.gradle index 50443064ba..370109ab47 100644 --- a/settings.gradle +++ b/settings.gradle @@ -9,7 +9,7 @@ pluginManagement { } plugins { - def kotlinVersion = settings.hasProperty("kotlinDev") ? "1.4.30-RC" : "1.4.10" + def kotlinVersion = settings.hasProperty("kotlinDev") ? "1.4.30-RC" : "1.4.30" id 'org.jetbrains.kotlin.jvm' version kotlinVersion id 'com.github.breadmoirai.github-release' version '2.2.12' id 'com.github.johnrengelman.shadow' version '6.1.0' From 5d7f43078b5cc25887ba6652f12b6bfdbae985ae Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Sun, 21 Feb 2021 16:30:57 +0100 Subject: [PATCH 43/53] Update Kotlin dev version to 1.5.0-M2-187. --- build.gradle | 5 ++--- gradle/verification-metadata.xml | 10 ++++------ settings.gradle | 5 ++--- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/build.gradle b/build.gradle index e45a257713..e70997c14e 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { } ext.versions = [ - 'kotlin': gradle.ext.isKotlinDev ? "1.4.30-RC" : "1.4.30", + 'kotlin': gradle.ext.isKotlinDev ? "1.5.0-M2-187" : "1.4.30", 'gradle': '6.8.1', 'gradle-sha256': 'fd591a34af7385730970399f473afabdb8b28d57fd97d6625c388d090039d6fd' ] @@ -50,8 +50,7 @@ allprojects { gradlePluginPortal() if (gradle.ext.isKotlinDev) { - maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' } - maven { url 'https://dl.bintray.com/kotlin/kotlin-dev' } + maven { url 'https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev/' } } } diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index cf885b83c2..b0c2bf5d40 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -30,7 +30,10 @@ - + + + + @@ -802,11 +805,6 @@ - - - - - diff --git a/settings.gradle b/settings.gradle index 370109ab47..64648cdaf6 100644 --- a/settings.gradle +++ b/settings.gradle @@ -3,13 +3,12 @@ pluginManagement { mavenCentral() gradlePluginPortal() if (settings.hasProperty('kotlinDev')) { - maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' } - maven { url 'https://dl.bintray.com/kotlin/kotlin-dev' } + maven { url 'https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev/' } } } plugins { - def kotlinVersion = settings.hasProperty("kotlinDev") ? "1.4.30-RC" : "1.4.30" + def kotlinVersion = settings.hasProperty("kotlinDev") ? "1.5.0-M2-187" : "1.4.30" id 'org.jetbrains.kotlin.jvm' version kotlinVersion id 'com.github.breadmoirai.github-release' version '2.2.12' id 'com.github.johnrengelman.shadow' version '6.1.0' From 250a3e467d2624a123175f6a392c8742e6dd81e0 Mon Sep 17 00:00:00 2001 From: Sha Sha Chu Date: Tue, 23 Feb 2021 10:02:29 -0800 Subject: [PATCH 44/53] Annotation spacing fix (#1087) * Fixing bug with experiment:annotation-spacing-rule with comments * updating changelog * deleting unused function * fix imports * fix build --- CHANGELOG.md | 1 + .../experimental/AnnotationSpacingRule.kt | 35 ++++-- .../experimental/AnnotationSpacingRuleTest.kt | 101 +++++++++++++++++- 3 files changed, 125 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1965256fbe..ab9741b94e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Fix formatting with comments (`colon-spacing`) ([#1057](https://github.com/pinterest/ktlint/issues/1057)) - Fix IndexOutOfBoundsException in `argument-list-wrapping-rule` formatting file with many corrections ([#1081](https://github.com/pinterest/ktlint/issues/1081)) - Fix formatting in arguments (`multiline-if-else`) ([#1079](https://github.com/pinterest/ktlint/issues/1079)) +- Fix experimental:annotation-spacing-rule autocorrection with comments ### Changed - Update Gradle shadow plugin to `6.1.0` version diff --git a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/AnnotationSpacingRule.kt b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/AnnotationSpacingRule.kt index 8a3ecebfbd..41f061f12e 100644 --- a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/AnnotationSpacingRule.kt +++ b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/AnnotationSpacingRule.kt @@ -6,6 +6,7 @@ import com.pinterest.ktlint.core.ast.isPartOf import com.pinterest.ktlint.core.ast.isPartOfComment import com.pinterest.ktlint.core.ast.isWhiteSpace import com.pinterest.ktlint.core.ast.isWhiteSpaceWithNewline +import com.pinterest.ktlint.core.ast.nextCodeSibling import com.pinterest.ktlint.core.ast.nextLeaf import com.pinterest.ktlint.core.ast.nextSibling import org.jetbrains.kotlin.com.intellij.lang.ASTNode @@ -17,15 +18,14 @@ import org.jetbrains.kotlin.psi.psiUtil.children import org.jetbrains.kotlin.psi.psiUtil.endOffset /** - * Ensures there are not multiple line breaks between annotations and declarations. + * Ensures annotations occur immediately prior to the annotated construct * * https://kotlinlang.org/docs/reference/coding-conventions.html#annotation-formatting */ class AnnotationSpacingRule : Rule("annotation-spacing") { companion object { - const val fileAnnotationsLineBreaks = - "There should not be empty lines between an annotation and the object that it's annotating" + const val ERROR_MESSAGE = "Annotations should occur immediately before the annotated construct" } override fun visit( @@ -62,21 +62,36 @@ class AnnotationSpacingRule : Rule("annotation-spacing") { { !it.isWhiteSpace() && it.textLength > 0 && - !(it.isPartOfComment() /* && it.lineNumber() == lineNumber*/) && !it.isPartOf(ElementType.FILE_ANNOTATION_LIST) }, { - val s = it.text - // Ensure at least one occurrence of two line breaks - s.indexOf("\n") != s.lastIndexOf("\n") + // Disallow multiple white spaces as well as comments + if (it.psi is PsiWhiteSpace) { + val s = it.text + // Ensure at least one occurrence of two line breaks + s.indexOf("\n") != s.lastIndexOf("\n") + } else it.isPartOfComment() } ) if (next != null) { if (node.elementType != ElementType.FILE_ANNOTATION_LIST) { val psi = node.psi - emit(psi.endOffset - 1, fileAnnotationsLineBreaks, true) + emit(psi.endOffset - 1, ERROR_MESSAGE, true) if (autoCorrect) { - removeExtraLineBreaks(node) + // Special-case autocorrection when the annotation is separated from the annotated construct + // by a comment: we need to swap the order of the comment and the annotation + if (next.isPartOfComment()) { + // Remove the annotation and the following whitespace + val nextSibling = node.nextSibling { it.isWhiteSpace() } + node.treeParent.removeChild(node) + nextSibling?.treeParent?.removeChild(nextSibling) + // Insert the annotation prior to the annotated construct + val space = PsiWhiteSpaceImpl("\n") + next.treeParent.addChild(space, next.nextCodeSibling()) + next.treeParent.addChild(node, space) + } else { + removeExtraLineBreaks(node) + } } } } @@ -84,7 +99,7 @@ class AnnotationSpacingRule : Rule("annotation-spacing") { // Check to make sure there are multi breaks between annotations if (whiteSpaces.any { psi -> psi.textToCharArray().filter { it == '\n' }.count() > 1 }) { val psi = node.psi - emit(psi.endOffset - 1, fileAnnotationsLineBreaks, true) + emit(psi.endOffset - 1, ERROR_MESSAGE, true) if (autoCorrect) { removeIntraLineBreaks(node, annotations.last()) } diff --git a/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/AnnotationSpacingRuleTest.kt b/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/AnnotationSpacingRuleTest.kt index e259a9dc59..82e438d006 100644 --- a/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/AnnotationSpacingRuleTest.kt +++ b/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/AnnotationSpacingRuleTest.kt @@ -2,7 +2,6 @@ package com.pinterest.ktlint.ruleset.experimental import com.pinterest.ktlint.core.KtLint import com.pinterest.ktlint.core.LintError -import com.pinterest.ktlint.ruleset.experimental.AnnotationSpacingRule.Companion.fileAnnotationsLineBreaks import com.pinterest.ktlint.test.format import com.pinterest.ktlint.test.lint import java.util.ArrayList @@ -37,7 +36,7 @@ class AnnotationSpacingRuleTest { ) ).isEqualTo( listOf( - LintError(1, 9, "annotation-spacing", fileAnnotationsLineBreaks) + LintError(1, 9, "annotation-spacing", AnnotationSpacingRule.ERROR_MESSAGE) ) ) } @@ -328,4 +327,102 @@ class AnnotationSpacingRuleTest { it.ruleId == "experimental:argument-list-wrapping" } } + + @Test + fun `annotations should not be separated by comments from the annotated construct`() { + val code = + """ + @Suppress("DEPRECATION") @Hello + /** + * block comment + */ + class Foo { + } + """.trimIndent() + assertThat( + AnnotationSpacingRule().lint(code) + ).isEqualTo( + listOf( + LintError(1, 31, "annotation-spacing", AnnotationSpacingRule.ERROR_MESSAGE) + ) + ) + } + + @Test + fun `annotations should be moved after comments`() { + val code = + """ + @Suppress("DEPRECATION") @Hello + /** + * block comment + */ + class Foo { + } + """.trimIndent() + assertThat( + AnnotationSpacingRule().format(code) + ).isEqualTo( + """ + /** + * block comment + */ + @Suppress("DEPRECATION") @Hello + class Foo { + } + """.trimIndent() + ) + + val codeEOL = + """ + @Suppress("DEPRECATION") @Hello + // hello + class Foo { + } + """.trimIndent() + assertThat( + AnnotationSpacingRule().format(codeEOL) + ).isEqualTo( + """ + // hello + @Suppress("DEPRECATION") @Hello + class Foo { + } + """.trimIndent() + ) + } + + @Test + fun `preceding whitespaces are preserved`() { + val code = + """ + package a.b.c + + val hello = 5 + + + @Suppress("DEPRECATION") @Hello + /** + * block comment + */ + class Foo { + } + """.trimIndent() + assertThat( + AnnotationSpacingRule().format(code) + ).isEqualTo( + """ + package a.b.c + + val hello = 5 + + + /** + * block comment + */ + @Suppress("DEPRECATION") @Hello + class Foo { + } + """.trimIndent() + ) + } } From d90ef21a9e7aa7530fa3f563f3082e72e4d60ba6 Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Thu, 25 Feb 2021 21:49:42 +0100 Subject: [PATCH 45/53] Fix Kotlin compiler may not in all cases be released. --- .../main/kotlin/com/pinterest/ktlint/core/KtLint.kt | 7 +++++-- .../ktlint/core/internal/KotlinPsiFileFactory.kt | 12 +++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/KtLint.kt b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/KtLint.kt index c44029cd80..45b7bfae4b 100644 --- a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/KtLint.kt +++ b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/KtLint.kt @@ -111,16 +111,16 @@ public object KtLint { } } catch (e: Exception) { val (line, col) = preparedCode.positionInTextLocator(node.startOffset) + kotlinPsiFileFactory.releasePsiFileFactory() throw RuleExecutionException(line, col, fqRuleId, e) } } } + kotlinPsiFileFactory.releasePsiFileFactory() errors .sortedWith { l, r -> if (l.line != r.line) l.line - r.line else l.col - r.col } .forEach { e -> params.cb(e, false) } - - kotlinPsiFileFactory.releasePsiFileFactory() } private fun prepareCodeForLinting( @@ -324,6 +324,7 @@ public object KtLint { } } } catch (e: Exception) { + kotlinPsiFileFactory.releasePsiFileFactory() // line/col cannot be reliably mapped as exception might originate from a node not present // in the original AST throw RuleExecutionException(0, 0, fqRuleId, e) @@ -352,6 +353,7 @@ public object KtLint { } } catch (e: Exception) { val (line, col) = preparedCode.positionInTextLocator(node.startOffset) + kotlinPsiFileFactory.releasePsiFileFactory() throw RuleExecutionException(line, col, fqRuleId, e) } } @@ -363,6 +365,7 @@ public object KtLint { } if (!mutated) { + kotlinPsiFileFactory.releasePsiFileFactory() return params.text } diff --git a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/KotlinPsiFileFactory.kt b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/KotlinPsiFileFactory.kt index 5fc64da889..e4d5edf477 100644 --- a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/KotlinPsiFileFactory.kt +++ b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/KotlinPsiFileFactory.kt @@ -34,18 +34,16 @@ internal class KotlinPsiFileFactory { private val acquireCounter = AtomicInteger(0) private val initializerLock = Any() - internal fun acquirePsiFileFactory(isFromCli: Boolean): PsiFileFactory { + internal fun acquirePsiFileFactory(isFromCli: Boolean): PsiFileFactory = synchronized(initializerLock) { acquireCounter.incrementAndGet() - return psiFileFactory ?: synchronized(initializerLock) { - initializePsiFileFactory(isFromCli).also { - psiFileFactory = it - } + return psiFileFactory ?: initializePsiFileFactory(isFromCli).also { + psiFileFactory = it } } - internal fun releasePsiFileFactory() { + internal fun releasePsiFileFactory() = synchronized(initializerLock) { val acquiredCount = acquireCounter.decrementAndGet() - if (acquiredCount == 0) synchronized(initializerLock) { + if (acquiredCount == 0) { tempFiles?.toFile()?.deleteRecursively() tempFiles = null psiFileFactory = null From 649abe358f0aea5f86590e4ba22b418f1b8c6861 Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Mon, 1 Mar 2021 21:17:14 +0100 Subject: [PATCH 46/53] Remove usages of jcenter(). --- README.md | 2 +- buildSrc/build.gradle.kts | 1 - gradle/verification-metadata.xml | 26 +------------------------- 3 files changed, 2 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index e9258dff5a..830727e665 100644 --- a/README.md +++ b/README.md @@ -287,7 +287,7 @@ You might also want to take a look at [diffplug/spotless](https://github.com/dif apply plugin: 'java' repositories { - jcenter() + mavenCentral() } configurations { diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 78cbfdb382..8319cf9623 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -4,7 +4,6 @@ plugins { repositories { mavenCentral() - jcenter() gradlePluginPortal() } diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index b0c2bf5d40..fd96fc2f5a 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -752,14 +752,6 @@ - - - - - - - - @@ -768,28 +760,12 @@ - - - - - - - - - - - - - - - - - + From daa2229e2a1bd33a545c260d6d50e921d299415c Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Sat, 16 Jan 2021 20:50:43 +0100 Subject: [PATCH 47/53] Replace Klob dependency with 'walkFileTree' and 'PathMatcher'. Migrate method to get all mathing globs files to use internally 'PathMatcher' and 'FileVisitor' instead of using klob dependency. Fix negated globs passed to CLI are no longer worked. --- CHANGELOG.md | 2 + README.md | 42 +++-- build.gradle | 2 +- ktlint/build.gradle | 1 + .../main/kotlin/com/pinterest/ktlint/Main.kt | 44 +++-- .../pinterest/ktlint/internal/FileUtils.kt | 119 ++++++++++--- .../ktlint/internal/PrintASTSubCommand.kt | 10 +- .../internal/FileUtilsFileSequenceTest.kt | 161 ++++++++++++++++++ 8 files changed, 323 insertions(+), 58 deletions(-) create mode 100644 ktlint/src/test/kotlin/com/pinterest/ktlint/internal/FileUtilsFileSequenceTest.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index ab9741b94e..9e1e3c3787 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Fix IndexOutOfBoundsException in `argument-list-wrapping-rule` formatting file with many corrections ([#1081](https://github.com/pinterest/ktlint/issues/1081)) - Fix formatting in arguments (`multiline-if-else`) ([#1079](https://github.com/pinterest/ktlint/issues/1079)) - Fix experimental:annotation-spacing-rule autocorrection with comments +- Migrate from klob dependency and fix negated globs passed to CLI are no longer worked ([#999](https://github.com/pinterest/ktlint/issues/999)) + **Breaking**: absolute paths globs will no longer work, check updated README ### Changed - Update Gradle shadow plugin to `6.1.0` version diff --git a/README.md b/README.md index 830727e665..bd894fa5cc 100644 --- a/README.md +++ b/README.md @@ -164,32 +164,42 @@ On macOS ([or Linux](http://linuxbrew.sh/)) you can also use [brew](https://brew [wget](https://www.gnu.org/software/wget/manual/wget.html#Proxies) manpage. Usually simple `http_proxy=http://proxy-server:port https_proxy=http://proxy-server:port curl -sL ...` is enough. -## Usage +## Command line usage ```bash -# check the style of all Kotlin files inside the current dir (recursively) -# (hidden folders will be skipped) -$ ktlint --color [--color-name="RED"] - src/main/kotlin/Main.kt:10:10: Unused import - -# check only certain locations (prepend ! to negate the pattern, -# Ktlint uses .gitignore pattern style syntax) -$ ktlint "src/**/*.kt" "!src/**/*Test.kt" +# Get help about all available commands +$ ktlint --help -# auto-correct style violations -# (if some errors cannot be fixed automatically they will be printed to stderr) +# Check the style of all Kotlin files (ending with '.kt' or '.kts') inside the current dir (recursively). +# Hidden folders will be skipped. +$ ktlint + +# Check only certain locations starting from the current directory. +# +# Prepend ! to negate the pattern, KtLint uses .gitignore pattern style syntax. +# Globs are applied starting from the last one. +# +# Hidden folders will be skipped. +# Check all '.kt' files in 'src/' directory, but ignore files ending with 'Test.kt': +ktlint "src/**/*.kt" "!src/**/*Test.kt" +# Check all '.kt' files in 'src/' directory, but ignore 'generated' directory and its subdirectories: +ktlint "src/**/*.kt" "!src/**/generated/**" + +# Auto-correct style violations. +# If some errors cannot be fixed automatically they will be printed to stderr. $ ktlint -F "src/**/*.kt" -# print style violations grouped by file +# Print style violations grouped by file. $ ktlint --reporter=plain?group_by_file -# print style violations as usual + create report in checkstyle format + +# Print style violations as usual + create report in checkstyle format, specifying report location. $ ktlint --reporter=plain --reporter=checkstyle,output=ktlint-report-in-checkstyle-format.xml -# check against a baseline file +# Check against a baseline file. $ ktlint --baseline=ktlint-baseline.xml -# install git hook to automatically check files for style violations on commit -# Run "ktlint installGitPrePushHook" if you wish to run ktlint on push instead +# Install git hook to automatically check files for style violations on commit. +# Run "ktlint installGitPrePushHook" if you wish to run ktlint on push instead. $ ktlint installGitPreCommitHook ``` diff --git a/build.gradle b/build.gradle index e70997c14e..e6a5bce0d8 100644 --- a/build.gradle +++ b/build.gradle @@ -41,7 +41,7 @@ task ktlint(type: JavaExec, group: LifecycleBasePlugin.VERIFICATION_GROUP) { description = "Check Kotlin code style." classpath = configurations.ktlint main = 'com.pinterest.ktlint.Main' - args '*/src/**/*.kt', '--baseline=ktlint-baseline.xml' + args '**/src/**/*.kt', '--baseline=ktlint-baseline.xml' } allprojects { diff --git a/ktlint/build.gradle b/ktlint/build.gradle index 0580f6d3e3..f5bf0f0ce6 100644 --- a/ktlint/build.gradle +++ b/ktlint/build.gradle @@ -35,6 +35,7 @@ dependencies { testImplementation deps.junit testImplementation deps.assertj + testImplementation deps.jimfs } // Implements https://github.com/brianm/really-executable-jars-maven-plugin maven plugin behaviour. diff --git a/ktlint/src/main/kotlin/com/pinterest/ktlint/Main.kt b/ktlint/src/main/kotlin/com/pinterest/ktlint/Main.kt index c2d58342ec..76c39e38e9 100644 --- a/ktlint/src/main/kotlin/com/pinterest/ktlint/Main.kt +++ b/ktlint/src/main/kotlin/com/pinterest/ktlint/Main.kt @@ -33,6 +33,7 @@ import java.io.IOException import java.io.PrintStream import java.net.URLClassLoader import java.net.URLDecoder +import java.nio.file.FileSystems import java.util.ArrayList import java.util.LinkedHashMap import java.util.ServiceLoader @@ -87,34 +88,38 @@ fun handleSubCommand( @Command( headerHeading = -"""An anti-bikeshedding Kotlin linter with built-in formatter +""" +An anti-bikeshedding Kotlin linter with built-in formatter. (https://github.com/pinterest/ktlint). Usage: ktlint [patterns] - java -jar ktlint [patterns] + java -jar ktlint.jar [patterns] Examples: - # check the style of all Kotlin files inside the current dir (recursively) - # (hidden folders will be skipped) + # Check the style of all Kotlin files (ending with '.kt' or '.kts') inside the current dir (recursively). + # + # Hidden folders will be skipped. ktlint - # check only certain locations (prepend ! to negate the pattern, - # Ktlint uses .gitignore pattern style syntax) + # Check only certain locations starting from the current directory. + # + # Prepend ! to negate the pattern, KtLint uses .gitignore pattern style syntax. + # Globs are applied starting from the last one. + # + # Hidden folders will be skipped. + # Check all '.kt' files in 'src/' directory, but ignore files ending with 'Test.kt': ktlint "src/**/*.kt" "!src/**/*Test.kt" + # Check all '.kt' files in 'src/' directory, but ignore 'generated' directory and its subdirectories: + ktlint "src/**/*.kt" "!src/**/generated/**" - # auto-correct style violations + # Auto-correct style violations. ktlint -F "src/**/*.kt" - # custom reporter - ktlint --reporter=plain?group_by_file - # multiple reporters can be specified like this - ktlint --reporter=plain \ - --reporter=checkstyle,output=ktlint-checkstyle-report.xml - # 3rd-party reporter - ktlint --reporter=csv,artifact=com.github.user:repo:master-SNAPSHOT - -Flags:""", + # Using custom reporter jar and overriding report location + ktlint --reporter=csv,artifact=/path/to/reporter/csv.jar,output=my-custom-report.csv +Flags: +""", synopsisHeading = "", customSynopsis = [""], sortOptions = false, @@ -183,7 +188,8 @@ class KtlintCommandLine { names = ["--reporter"], description = [ "A reporter to use (built-in: plain (default), plain?group_by_file, json, checkstyle, html). " + - "To use a third-party reporter specify a path to a JAR file on the filesystem." + "To use a third-party reporter specify a path to a JAR file on the filesystem via ',artifact=' option. " + + "To override reporter output, use ',output=' option." ] ) private var reporters: JarFiles = ArrayList() @@ -274,7 +280,9 @@ class KtlintCommandLine { baseline: Map>?, reporter: Reporter ) { - patterns.fileSequence() + FileSystems.getDefault() + .fileSequence(patterns) + .map { it.toFile() } .takeWhile { errorNumber.get() < limit } .map { file -> Callable { diff --git a/ktlint/src/main/kotlin/com/pinterest/ktlint/internal/FileUtils.kt b/ktlint/src/main/kotlin/com/pinterest/ktlint/internal/FileUtils.kt index 18e91e37a5..6aedfec564 100644 --- a/ktlint/src/main/kotlin/com/pinterest/ktlint/internal/FileUtils.kt +++ b/ktlint/src/main/kotlin/com/pinterest/ktlint/internal/FileUtils.kt @@ -1,37 +1,116 @@ package com.pinterest.ktlint.internal -import com.github.shyiko.klob.Glob import com.pinterest.ktlint.core.KtLint import com.pinterest.ktlint.core.LintError import com.pinterest.ktlint.core.RuleSet import java.io.File +import java.nio.file.FileSystem +import java.nio.file.FileVisitResult +import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths +import java.nio.file.SimpleFileVisitor +import java.nio.file.attribute.BasicFileAttributes import kotlin.system.exitProcess internal val workDir: String = File(".").canonicalPath +private val tildeRegex = Regex("^(!)?~") -internal fun List.fileSequence(): Sequence { - val kotlinFiles: Sequence = if (isEmpty()) { - Glob.from("**/*.kt", "**/*.kts") - .iterate( - Paths.get(workDir), - Glob.IterationOption.SKIP_HIDDEN - ) - .asSequence() +internal fun FileSystem.fileSequence( + globs: List, + rootDir: Path = Paths.get(".").toAbsolutePath().normalize() +): Sequence { + checkGlobsContainAbsolutePath(globs) + + val pathMatchers = if (globs.isEmpty()) { + setOf( + getPathMatcher("glob:**$globSeparator*.kt"), + getPathMatcher("glob:**$globSeparator*.kts") + ) } else { - // Converting List to Array and passing it to Glob.from(patterns) skips some files - // See https://github.com/pinterest/ktlint/issues/942 - map { - Glob.from(expandTilde(it)) - .iterate(Paths.get(workDir)) - } - .asSequence() - .flatMap { it.asSequence() } + globs + .filterNot { it.startsWith("!") } + .map { + getPathMatcher(it.toGlob(rootDir)) + } + } + + val negatedPathMatchers = if (globs.isEmpty()) { + emptySet() + } else { + globs + .filter { it.startsWith("!") } + .map { getPathMatcher(it.removePrefix("!").toGlob(rootDir)) } } - return kotlinFiles - .map(Path::toFile) + val result = mutableListOf() + Files.walkFileTree( + rootDir, + object : SimpleFileVisitor() { + override fun visitFile( + filePath: Path, + fileAttrs: BasicFileAttributes + ): FileVisitResult { + if (negatedPathMatchers.none { it.matches(filePath) } && + pathMatchers.any { it.matches(filePath) } + ) { + result.add(filePath) + } + return FileVisitResult.CONTINUE + } + + override fun preVisitDirectory( + dirPath: Path, + dirAttr: BasicFileAttributes + ): FileVisitResult { + return if (Files.isHidden(dirPath)) { + FileVisitResult.SKIP_SUBTREE + } else { + FileVisitResult.CONTINUE + } + } + } + ) + + return result.asSequence() +} + +private fun FileSystem.checkGlobsContainAbsolutePath(globs: List) { + val rootDirs = rootDirectories.map { it.toString() } + globs + .map { it.removePrefix("!") } + .filter { glob -> + rootDirs.any { glob.startsWith(it) } + } + .run { + if (isNotEmpty()) { + throw IllegalArgumentException( + "KtLint does not support absolute path in globs:\n${joinToString(separator = "\n")}" + ) + } + } +} + +private fun String.toGlob( + rootDir: Path +): String { + val expandedPath = expandTilde(this) + val rootDirPath = rootDir + .toAbsolutePath() + .toString() + .run { + val normalizedPath = if (!endsWith(File.separator)) "$this${File.separator}" else this + normalizedPath.replace(File.separator, globSeparator) + } + return "glob:$rootDirPath$expandedPath" +} + +internal val globSeparator: String get() { + val os = System.getProperty("os.name") + return when { + os.startsWith("windows", ignoreCase = true) -> "\\\\" + else -> "/" + } } /** @@ -50,7 +129,7 @@ internal fun JarFiles.toFilesURIList() = map { // a complete solution would be to implement https://www.gnu.org/software/bash/manual/html_node/Tilde-Expansion.html // this implementation takes care only of the most commonly used case (~/) -private fun expandTilde(path: String): String = path.replaceFirst(Regex("^~"), System.getProperty("user.home")) +private fun expandTilde(path: String): String = path.replaceFirst(tildeRegex, System.getProperty("user.home")) internal fun File.location( relative: Boolean diff --git a/ktlint/src/main/kotlin/com/pinterest/ktlint/internal/PrintASTSubCommand.kt b/ktlint/src/main/kotlin/com/pinterest/ktlint/internal/PrintASTSubCommand.kt index 64d39518ea..94849f4d9a 100644 --- a/ktlint/src/main/kotlin/com/pinterest/ktlint/internal/PrintASTSubCommand.kt +++ b/ktlint/src/main/kotlin/com/pinterest/ktlint/internal/PrintASTSubCommand.kt @@ -6,6 +6,7 @@ import com.pinterest.ktlint.core.ParseException import com.pinterest.ktlint.core.RuleSet import com.pinterest.ruleset.test.DumpASTRule import java.io.File +import java.nio.file.FileSystems import picocli.CommandLine @CommandLine.Command( @@ -47,9 +48,12 @@ internal class PrintASTSubCommand : Runnable { if (stdin) { printAST(KtLint.STDIN_FILE, String(System.`in`.readBytes())) } else { - for (file in patterns.fileSequence()) { - printAST(file.path, file.readText()) - } + FileSystems.getDefault() + .fileSequence(patterns) + .map { it.toFile() } + .forEach { + printAST(it.path, it.readText()) + } } } diff --git a/ktlint/src/test/kotlin/com/pinterest/ktlint/internal/FileUtilsFileSequenceTest.kt b/ktlint/src/test/kotlin/com/pinterest/ktlint/internal/FileUtilsFileSequenceTest.kt new file mode 100644 index 0000000000..00a0e363aa --- /dev/null +++ b/ktlint/src/test/kotlin/com/pinterest/ktlint/internal/FileUtilsFileSequenceTest.kt @@ -0,0 +1,161 @@ +package com.pinterest.ktlint.internal + +import com.google.common.jimfs.Configuration +import com.google.common.jimfs.Jimfs +import java.io.File +import java.nio.file.Files +import java.nio.file.Path +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.fail +import org.junit.After +import org.junit.Before +import org.junit.Test + +/** + * Tests for [fileSequence] method. + */ +internal class FileUtilsFileSequenceTest { + private val tempFileSystem = Jimfs.newFileSystem(Configuration.forCurrentPlatform()) + + private val rootDir = tempFileSystem.rootDirectories.first().toString() + private val project1Files = listOf( + "${rootDir}project1/.git/ignored.kts", + "${rootDir}project1/build.gradle.kts", + "${rootDir}project1/src/scripts/someScript.kts", + "${rootDir}project1/src/main/kotlin/One.kt", + "${rootDir}project1/src/main/kotlin/example/Two.kt", + ).map { it.normalizePath() } + + private val project2Files = listOf( + "${rootDir}project2/build.gradle.kts", + "${rootDir}project2/src/main/java/Three.kt" + ).map { it.normalizePath() } + + @Before + fun setUp() { + project1Files.createFiles() + } + + @After + fun tearDown() { + tempFileSystem.close() + } + + @Test + fun `On empty patterns should include all kt and kts files`() { + val foundFiles = getFiles() + + assertThat(foundFiles).hasSize(project1Files.size - 1) + assertThat(foundFiles).containsAll(project1Files.drop(1)) + } + + @Test + fun `Should ignore hidden dirs on empty patterns`() { + val foundFiles = getFiles() + + assertThat(foundFiles).doesNotContain(project1Files.first()) + } + + @Test + fun `Should return only files matching passed pattern`() { + val expectedResult = project1Files.drop(1).filter { it.endsWith(".kt") } + val foundFiles = getFiles( + listOf( + "**/src/**/*.kt".normalizeGlob() + ) + ) + + assertThat(foundFiles).hasSize(expectedResult.size) + assertThat(foundFiles).containsAll(expectedResult) + } + + @Test + fun `Should ignore hidden folders when some pattern is passed`() { + val expectedResult = project1Files.drop(1).filter { it.endsWith(".kts") } + val foundFiles = getFiles( + listOf( + "project1/**/*.kts".normalizeGlob(), + "project1/*.kts".normalizeGlob() + ) + ) + + assertThat(foundFiles).hasSize(expectedResult.size) + assertThat(foundFiles).containsAll(expectedResult) + } + + @Test + fun `Should ignore files in negated pattern`() { + val foundFiles = getFiles( + listOf( + "project1/src/**/*.kt".normalizeGlob(), + "!project1/src/**/example/*.kt".normalizeGlob() + ) + ) + + assertThat(foundFiles).hasSize(1) + assertThat(foundFiles).containsAll(project1Files.subList(3, 4)) + } + + @Test + fun `Should return only files for the the current rootDir matching passed patterns`() { + project2Files.createFiles() + + val foundFiles = getFiles( + listOf( + "**/main/**/*.kt".normalizeGlob() + ), + tempFileSystem.getPath("${rootDir}project2".normalizePath()) + ) + + assertThat(foundFiles).hasSize(1) + assertThat(foundFiles).containsAll(project2Files.subList(1, 1)) + } + + @Test + fun `Should treat correctly root path without separator in the end`() { + val foundFiles = getFiles( + patterns = listOf("src/main/kotlin/One.kt".normalizeGlob()), + rootDir = tempFileSystem.getPath("${rootDir}project1".normalizePath()) + ) + + assertThat(foundFiles).hasSize(1) + assertThat(foundFiles.first()).isEqualTo(project1Files[3]) + } + + @Test + fun `Should fail the search when glob contain absolute path`() { + val globs = listOf( + "src/main/kotlin/One.kt".normalizeGlob(), + "!${rootDir}project1/src/main/kotlin/example/Two.kt".normalizeGlob() + ) + + try { + getFiles( + patterns = globs + ) + fail("No exception was thrown!") + } catch (e: IllegalArgumentException) { + assertThat(e.message).contains( + globs.last().removePrefix("!") + ) + } + } + + private fun String.normalizePath() = replace('/', File.separatorChar) + private fun String.normalizeGlob(): String = replace("/", globSeparator) + + private fun List.createFiles() = forEach { + val filePath = tempFileSystem.getPath(it) + val fileDir = filePath.parent + if (!Files.exists(fileDir)) Files.createDirectories(fileDir) + Files.createFile(filePath) + } + + private fun getFiles( + patterns: List = emptyList(), + rootDir: Path = tempFileSystem.rootDirectories.first() + ): List = tempFileSystem + .fileSequence(patterns, rootDir) + .map { it.toString() } + .toList() +} From 57dc8bd479946ed7b494117b786cbec3fc214c98 Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Sun, 7 Mar 2021 12:31:16 +0100 Subject: [PATCH 48/53] Update Kotlin to 1.4.31 version. Kotlin dev is set to 1.5.0-M1. Kotlin pre-releases are now available on Maven Central. --- CHANGELOG.md | 2 +- build.gradle | 6 +----- gradle/verification-metadata.xml | 11 ++++++++--- settings.gradle | 5 +---- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e1e3c3787..e56a246d4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,7 +34,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Deprecated `idea` and `ascii` shortcuts as the `ij_kotlin_imports_layout` property does not support those. Please check README on how to achieve those with patterns ([#753](https://github.com/pinterest/ktlint/issues/753)) - Update Gradle to `6.8.1` version -- Update Kotlin to `1.4.30` version. Fixes [#1063](https://github.com/pinterest/ktlint/issues/1063). +- Update Kotlin to `1.4.31` version. Fixes [#1063](https://github.com/pinterest/ktlint/issues/1063). ### Removed diff --git a/build.gradle b/build.gradle index e6a5bce0d8..8b975196e7 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { } ext.versions = [ - 'kotlin': gradle.ext.isKotlinDev ? "1.5.0-M2-187" : "1.4.30", + 'kotlin': gradle.ext.isKotlinDev ? "1.5.0-M1" : "1.4.31", 'gradle': '6.8.1', 'gradle-sha256': 'fd591a34af7385730970399f473afabdb8b28d57fd97d6625c388d090039d6fd' ] @@ -48,10 +48,6 @@ allprojects { repositories { mavenCentral() gradlePluginPortal() - - if (gradle.ext.isKotlinDev) { - maven { url 'https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev/' } - } } tasks.withType(JavaCompile) { diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index fd96fc2f5a..e5de9fc573 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -776,9 +776,14 @@ - - - + + + + + + + + diff --git a/settings.gradle b/settings.gradle index 64648cdaf6..518b5dace4 100644 --- a/settings.gradle +++ b/settings.gradle @@ -2,13 +2,10 @@ pluginManagement { repositories { mavenCentral() gradlePluginPortal() - if (settings.hasProperty('kotlinDev')) { - maven { url 'https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev/' } - } } plugins { - def kotlinVersion = settings.hasProperty("kotlinDev") ? "1.5.0-M2-187" : "1.4.30" + def kotlinVersion = settings.hasProperty("kotlinDev") ? "1.5.0-M1" : "1.4.31" id 'org.jetbrains.kotlin.jvm' version kotlinVersion id 'com.github.breadmoirai.github-release' version '2.2.12' id 'com.github.johnrengelman.shadow' version '6.1.0' From e30be4bd49385f21fd3decd2e0e278235b178fca Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Sun, 7 Mar 2021 12:36:30 +0100 Subject: [PATCH 49/53] Update Gradle to '6.8.3' version. --- CHANGELOG.md | 2 +- build.gradle | 4 ++-- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e56a246d4c..9358784aab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,7 +33,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). that the Kotlin IDE plugin and ktlint use same imports layout ([#753](https://github.com/pinterest/ktlint/issues/753)) - Deprecated `idea` and `ascii` shortcuts as the `ij_kotlin_imports_layout` property does not support those. Please check README on how to achieve those with patterns ([#753](https://github.com/pinterest/ktlint/issues/753)) -- Update Gradle to `6.8.1` version +- Update Gradle to `6.8.3` version - Update Kotlin to `1.4.31` version. Fixes [#1063](https://github.com/pinterest/ktlint/issues/1063). ### Removed diff --git a/build.gradle b/build.gradle index 8b975196e7..035326a0e2 100644 --- a/build.gradle +++ b/build.gradle @@ -5,8 +5,8 @@ plugins { ext.versions = [ 'kotlin': gradle.ext.isKotlinDev ? "1.5.0-M1" : "1.4.31", - 'gradle': '6.8.1', - 'gradle-sha256': 'fd591a34af7385730970399f473afabdb8b28d57fd97d6625c388d090039d6fd' + 'gradle': '6.8.3', + 'gradle-sha256': '7faa7198769f872826c8ef4f1450f839ec27f0b4d5d1e51bade63667cbccd205' ] ext.deps = [ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f604ed1642..51d930a381 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=fd591a34af7385730970399f473afabdb8b28d57fd97d6625c388d090039d6fd -distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.1-bin.zip +distributionSha256Sum=7faa7198769f872826c8ef4f1450f839ec27f0b4d5d1e51bade63667cbccd205 +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 187ca66c54a54eb9e92d23b07c554b3a1d9884e1 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Sun, 7 Mar 2021 21:40:28 +0100 Subject: [PATCH 50/53] Use new editorconfig API for ignore_back_ticked property and make it ktlint-specific --- README.md | 20 ++++------- .../com/pinterest/ktlint/core/EditorConfig.kt | 4 +-- .../ruleset/standard/MaxLineLengthRule.kt | 36 +++++++++++++++++-- .../ruleset/standard/MaxLineLengthRuleTest.kt | 20 +++++++++-- 4 files changed, 58 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index bd894fa5cc..03b127e618 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,12 @@ disabled_rules=no-wildcard-imports,experimental:annotation,my-custom-ruleset:my- ij_kotlin_imports_layout=* # alphabetical with capital letters before lower case letters (e.g. Z before a), no blank lines ij_kotlin_imports_layout=*,java.**,javax.**,kotlin.**,^ # default IntelliJ IDEA style, same as alphabetical, but with "java", "javax", "kotlin" and alias imports in the end of the imports list ij_kotlin_imports_layout=android.**,|,^org.junit.**,kotlin.io.Closeable.*,|,*,^ # custom imports layout + +# According to https://kotlinlang.org/docs/reference/coding-conventions.html#names-for-test-methods it is acceptable to write method names +# in natural language. When using natural language, the description tends to be longer. Allow lines containing an identifier between +# backticks to be longer than the maximum line length. (Since 0.41.0) +[**/test/**.kt] +ktlint_ignore_back_ticked_identifier=true ``` ### Overriding Editorconfig properties for specific directories @@ -120,20 +126,6 @@ disabled_rules=import-ordering disabled_rules=indent ``` -### Overriding Editorconfig properties for unit testing - -According to the [Kotlin coding conventions](https://kotlinlang.org/docs/reference/coding-conventions.html#names-for-test-methods) -it is acceptable to write method names as natural language. As such descriptions tend to be longer than normal method -names, it is possible to ignore such method names when validation the maximum line length. It is advised though, to -limit this behavior to tests only. -```ini -[**/test/**.kt] -# According to https://kotlinlang.org/docs/reference/coding-conventions.html#names-for-test-methods it is acceptable to write method names -# in natural language. When using natural language, the description tends to be longer. Allow lines containing an identifier between -# backticks to be longer than the maximum line length. -ignore_back_ticked_identifier=true -``` - ## Online demo You can try `ktlint` online [here](https://ktlint-demo.herokuapp.com/) using the standard or a custom ruleset without installing it to your PC. \ To contribute or get more info, please visit the [GitHub repository](https://github.com/akuleshov7/diKTat-demo). diff --git a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/EditorConfig.kt b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/EditorConfig.kt index 0f1cfaea0a..bd189d5d84 100644 --- a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/EditorConfig.kt +++ b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/EditorConfig.kt @@ -13,7 +13,7 @@ interface EditorConfig { val indentSize: Int val tabWidth: Int val maxLineLength: Int - val ignoreBackTickedIdentifier: Boolean + @Deprecated( message = "Not used anymore by rules, please use 'insert_final_newline' directly via get()" ) @@ -33,14 +33,12 @@ interface EditorConfig { val tabWidth = map["indent_size"]?.toIntOrNull() val maxLineLength = map["max_line_length"]?.toIntOrNull() ?: -1 val insertFinalNewline = map["insert_final_newline"]?.toBoolean() ?: true - val ignoreBackTickedIdentifier = map["ignore_back_ticked_identifier"]?.toBoolean() ?: false return object : EditorConfig { override val indentStyle = indentStyle override val indentSize = indentSize override val tabWidth = tabWidth ?: indentSize override val maxLineLength = maxLineLength override val insertFinalNewline = insertFinalNewline - override val ignoreBackTickedIdentifier = ignoreBackTickedIdentifier override fun get(key: String): String? = map[key] } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/MaxLineLengthRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/MaxLineLengthRule.kt index dce2ee60cb..a191fdcec7 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/MaxLineLengthRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/MaxLineLengthRule.kt @@ -2,12 +2,16 @@ package com.pinterest.ktlint.ruleset.standard import com.pinterest.ktlint.core.KtLint import com.pinterest.ktlint.core.Rule +import com.pinterest.ktlint.core.api.EditorConfigProperties +import com.pinterest.ktlint.core.api.FeatureInAlphaState +import com.pinterest.ktlint.core.api.UsesEditorConfigProperties import com.pinterest.ktlint.core.ast.ElementType import com.pinterest.ktlint.core.ast.isPartOf import com.pinterest.ktlint.core.ast.isRoot import com.pinterest.ktlint.core.ast.nextLeaf import com.pinterest.ktlint.core.ast.parent import com.pinterest.ktlint.core.ast.prevCodeSibling +import org.ec4j.core.model.PropertyType import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.PsiComment import org.jetbrains.kotlin.com.intellij.psi.PsiElement @@ -16,7 +20,15 @@ import org.jetbrains.kotlin.kdoc.psi.api.KDoc import org.jetbrains.kotlin.psi.KtImportDirective import org.jetbrains.kotlin.psi.KtPackageDirective -class MaxLineLengthRule : Rule("max-line-length"), Rule.Modifier.Last { +@OptIn(FeatureInAlphaState::class) +class MaxLineLengthRule : + Rule("max-line-length"), + Rule.Modifier.Last, + UsesEditorConfigProperties { + + override val editorConfigProperties: List> = listOf( + ignoreBackTickedIdentifierProperty + ) private var maxLineLength: Int = -1 private var rangeTree = RangeTree() @@ -28,6 +40,9 @@ class MaxLineLengthRule : Rule("max-line-length"), Rule.Modifier.Last { ) { if (node.isRoot()) { val editorConfig = node.getUserData(KtLint.EDITOR_CONFIG_USER_DATA_KEY)!! + val editorConfigProperties: EditorConfigProperties = + node.getUserData(KtLint.EDITOR_CONFIG_PROPERTIES_USER_DATA_KEY)!! + val ignoreBackTickedIdentifier = editorConfigProperties.getEditorConfigValue(ignoreBackTickedIdentifierProperty) maxLineLength = editorConfig.maxLineLength if (maxLineLength <= 0) { return @@ -35,8 +50,8 @@ class MaxLineLengthRule : Rule("max-line-length"), Rule.Modifier.Last { val errorOffset = arrayListOf() node .getElementsPerLine() - .filter { it.lineLength(editorConfig.ignoreBackTickedIdentifier) > maxLineLength } - .forEach() { parsedLine -> + .filter { it.lineLength(ignoreBackTickedIdentifier) > maxLineLength } + .forEach { parsedLine -> val el = parsedLine.elements.last() if (!el.isPartOf(KDoc::class) && !el.isPartOfRawMultiLineString()) { if (!el.isPartOf(PsiComment::class)) { @@ -78,6 +93,21 @@ class MaxLineLengthRule : Rule("max-line-length"), Rule.Modifier.Last { private fun ASTNode.isPartOfRawMultiLineString() = parent(ElementType.STRING_TEMPLATE, strict = false) ?.let { it.firstChildNode.text == "\"\"\"" && it.textContains('\n') } == true + + public companion object { + internal const val KTLINT_IGNORE_BACKTICKED_IDENTIFIER_NAME = "ktlint_ignore_back_ticked_identifier" + private const val PROPERTY_DESCRIPTION = "Defines whether the backticked identifier (``) should be ignored" + + public val ignoreBackTickedIdentifierProperty: UsesEditorConfigProperties.EditorConfigProperty = + UsesEditorConfigProperties.EditorConfigProperty( + type = PropertyType( + /* name = */ KTLINT_IGNORE_BACKTICKED_IDENTIFIER_NAME, + /* description = */ PROPERTY_DESCRIPTION, + /* parser = */ PropertyType.PropertyValueParser.BOOLEAN_VALUE_PARSER + ), + defaultValue = false + ) + } } private fun ASTNode.getElementsPerLine(): List { diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/MaxLineLengthRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/MaxLineLengthRuleTest.kt index 0166a85291..31a3b3fc5c 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/MaxLineLengthRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/MaxLineLengthRuleTest.kt @@ -1,13 +1,21 @@ package com.pinterest.ktlint.ruleset.standard import com.pinterest.ktlint.core.LintError +import com.pinterest.ktlint.core.api.FeatureInAlphaState +import com.pinterest.ktlint.test.EditorConfigTestRule import com.pinterest.ktlint.test.diffFileLint import com.pinterest.ktlint.test.lint +import java.io.File import org.assertj.core.api.Assertions.assertThat +import org.junit.Rule import org.junit.Test +@OptIn(FeatureInAlphaState::class) class MaxLineLengthRuleTest { + @get:Rule + val editorConfigTestRule = EditorConfigTestRule() + @Test fun testLint() { assertThat( @@ -40,8 +48,11 @@ class MaxLineLengthRuleTest { @Test fun testErrorSuppressionOnTokensBetweenBackticks() { + val testFile = ignoreBacktickedIdentifier() + assertThat( MaxLineLengthRule().lint( + testFile.absolutePath, """ @Test fun `Some too long test description between backticks`() { @@ -49,8 +60,7 @@ class MaxLineLengthRuleTest { } """.trimIndent(), userData = mapOf( - "max_line_length" to "40", - "ignore_back_ticked_identifier" to "true" + "max_line_length" to "40" ) ) ).isEqualTo( @@ -104,4 +114,10 @@ class MaxLineLengthRuleTest { assertThat(RangeTree((5 until 10).asSequence().toList()).query(10, 15).toString()).isEqualTo("[]") assertThat(RangeTree(listOf(1, 5, 10)).query(3, 4).toString()).isEqualTo("[]") } + + + private fun ignoreBacktickedIdentifier(): File = editorConfigTestRule + .writeToEditorConfig( + mapOf(MaxLineLengthRule.ignoreBackTickedIdentifierProperty.type to true.toString()) + ) } From c25fd5ac1481a212addffafc2f037a96896e41db Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Sun, 7 Mar 2021 22:34:21 +0100 Subject: [PATCH 51/53] Add changelog entry --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9358784aab..fe426a0c74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## Unreleased ### Added +- New `ktlint_ignore_back_ticked_identifier` EditorConfig option for `max-line-length` rule to ignore long method names inside backticks + (primarily used in tests) ([#1007](https://github.com/pinterest/ktlint/issues/1007)) ### Fixed - Incorrect indentation with multiple interfaces ([#1003](https://github.com/pinterest/ktlint/issues/1003)) From b07b12104040d0e04c0043201c88eadf08fca703 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Sun, 7 Mar 2021 22:43:54 +0100 Subject: [PATCH 52/53] Fix violations --- .../pinterest/ktlint/ruleset/standard/MaxLineLengthRuleTest.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/MaxLineLengthRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/MaxLineLengthRuleTest.kt index 31a3b3fc5c..8fec93051e 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/MaxLineLengthRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/MaxLineLengthRuleTest.kt @@ -115,7 +115,6 @@ class MaxLineLengthRuleTest { assertThat(RangeTree(listOf(1, 5, 10)).query(3, 4).toString()).isEqualTo("[]") } - private fun ignoreBacktickedIdentifier(): File = editorConfigTestRule .writeToEditorConfig( mapOf(MaxLineLengthRule.ignoreBackTickedIdentifierProperty.type to true.toString()) From 72de0b091bdfa8499efe888ef2db1dfa7a9e7905 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Sun, 7 Mar 2021 23:04:37 +0100 Subject: [PATCH 53/53] Remove unnecessary comment --- .../pinterest/ktlint/ruleset/standard/MaxLineLengthRuleTest.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/MaxLineLengthRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/MaxLineLengthRuleTest.kt index 8fec93051e..ff9c2b33db 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/MaxLineLengthRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/MaxLineLengthRuleTest.kt @@ -85,7 +85,6 @@ class MaxLineLengthRuleTest { ) ).isEqualTo( listOf( - // Note that no error was generated on line 2 with the long fun name LintError(2, 1, "max-line-length", "Exceeded max line length (40)") ) )