Skip to content

Commit

Permalink
Wrap annotations before class constructor in code style `ktlint_offic…
Browse files Browse the repository at this point in the history
…ial`

Closes #1916

Annotations on type projections should be wrapped in same way as other annotations
Close #1917

Fix annotated expression inside function literal

Fix closing angle bracket on type argument list
  • Loading branch information
paul-dingemans committed Apr 6, 2023
1 parent 0aeae6c commit be43e79
Show file tree
Hide file tree
Showing 8 changed files with 775 additions and 388 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,8 @@ Previously the default value for `.editorconfig` property `max_line_length` was
* Wrap annotated function parameters to a separate line in code style `ktlint_official` only. `function-signature`, `parameter-list-wrapping` ([#1908](https://github.com/pinterest/ktlint/issues/1908))
* Wrap annotated projection types in type argument lists to a separate line `annotation` ([#1909](https://github.com/pinterest/ktlint/issues/1909))
* Add newline after adding trailing comma in parameter list of a function literal `trailing-comma-on-declaration-site` ([#1911](https://github.com/pinterest/ktlint/issues/1911))
* Wrap annotations before class constructor in code style `ktlint_official`. `annotation` ([#1916](https://github.com/pinterest/ktlint/issues/1916))
* Annotations on type projections should be wrapped in same way as other annotations `annotation` ([#1917](https://github.com/pinterest/ktlint/issues/1917))

### Changed
* Wrap the parameters of a function literal containing a multiline parameter list (only in `ktlint_official` code style) `parameter-list-wrapping` ([#1681](https://github.com/pinterest/ktlint/issues/1681)).
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import com.pinterest.ktlint.rule.engine.core.api.ElementType.OBJECT_DECLARATION
import com.pinterest.ktlint.rule.engine.core.api.ElementType.OPEN_QUOTE
import com.pinterest.ktlint.rule.engine.core.api.ElementType.OPERATION_REFERENCE
import com.pinterest.ktlint.rule.engine.core.api.ElementType.PARENTHESIZED
import com.pinterest.ktlint.rule.engine.core.api.ElementType.PRIMARY_CONSTRUCTOR
import com.pinterest.ktlint.rule.engine.core.api.ElementType.PROPERTY
import com.pinterest.ktlint.rule.engine.core.api.ElementType.PROPERTY_ACCESSOR
import com.pinterest.ktlint.rule.engine.core.api.ElementType.RBRACE
Expand Down Expand Up @@ -577,14 +578,24 @@ public class IndentationRule :
toAstNode = typeConstraintList.lastChildLeafOrSelf(),
).prevCodeLeaf()
}
node
.findChildByType(SUPER_TYPE_LIST)
?.let { superTypeList ->
nextToAstNode = startIndentContext(
fromAstNode = superTypeList.getPrecedingLeadingCommentsAndWhitespaces(),
toAstNode = superTypeList.lastChildLeafOrSelf(),
).prevCodeLeaf()
}

val primaryConstructor = node.findChildByType(PRIMARY_CONSTRUCTOR)
if (codeStyle == ktlint_official && primaryConstructor != null) {
// Indent both constructor and super type list
nextToAstNode = startIndentContext(
fromAstNode = primaryConstructor.getPrecedingLeadingCommentsAndWhitespaces(),
toAstNode = nextToAstNode,
).prevCodeLeaf()
} else {
node
.findChildByType(SUPER_TYPE_LIST)
?.let { superTypeList ->
nextToAstNode = startIndentContext(
fromAstNode = superTypeList.getPrecedingLeadingCommentsAndWhitespaces(),
toAstNode = superTypeList.lastChildLeafOrSelf(),
).prevCodeLeaf()
}
}

// Leading annotations and comments should be indented at same level as class itself
startIndentContext(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,48 @@
package com.pinterest.ktlint.ruleset.standard.rules

import com.pinterest.ktlint.rule.engine.core.api.ElementType
import com.pinterest.ktlint.rule.engine.core.api.IndentConfig
import com.pinterest.ktlint.rule.engine.core.api.Rule
import com.pinterest.ktlint.rule.engine.core.api.RuleId
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfig
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.INDENT_SIZE_PROPERTY
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.INDENT_STYLE_PROPERTY
import com.pinterest.ktlint.rule.engine.core.api.findCompositeParentElementOfType
import com.pinterest.ktlint.rule.engine.core.api.indent
import com.pinterest.ktlint.rule.engine.core.api.isPartOfCompositeElementOfType
import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpace
import com.pinterest.ktlint.rule.engine.core.api.nextLeaf
import com.pinterest.ktlint.rule.engine.core.api.nextSibling
import com.pinterest.ktlint.rule.engine.core.api.prevLeaf
import com.pinterest.ktlint.rule.engine.core.api.prevSibling
import com.pinterest.ktlint.rule.engine.core.api.upsertWhitespaceAfterMe
import com.pinterest.ktlint.rule.engine.core.api.upsertWhitespaceBeforeMe
import com.pinterest.ktlint.ruleset.standard.StandardRule
import org.jetbrains.kotlin.com.intellij.lang.ASTNode
import org.jetbrains.kotlin.utils.addToStdlib.applyIf

/**
* Lints and formats the spacing before and after the angle brackets of a type argument list.
*/
public class TypeArgumentListSpacingRule :
StandardRule("type-argument-list-spacing"),
StandardRule(
id = "type-argument-list-spacing",
usesEditorConfigProperties =
setOf(
INDENT_SIZE_PROPERTY,
INDENT_STYLE_PROPERTY,
),
),
Rule.Experimental {
private var indentConfig = IndentConfig.DEFAULT_INDENT_CONFIG

override fun beforeFirstNode(editorConfig: EditorConfig) {
indentConfig = IndentConfig(
indentStyle = editorConfig[INDENT_STYLE_PROPERTY],
tabWidth = editorConfig[INDENT_SIZE_PROPERTY],
)
}

override fun beforeVisitChildNodes(
node: ASTNode,
autoCorrect: Boolean,
Expand Down Expand Up @@ -70,21 +95,53 @@ public class TypeArgumentListSpacingRule :
autoCorrect: Boolean,
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit,
) {
// No whitespace expected after opening angle bracket of type argument list
// val list = listOf< String>()
val multiline = node.textContains('\n')
val expectedIndent =
node
.indent()
.applyIf(multiline) {
plus(indentConfig.indent)
}

node
.findChildByType(ElementType.LT)
?.nextSibling()
?.takeIf { it.elementType == ElementType.WHITE_SPACE }
?.let { noWhitespaceExpected(it, autoCorrect, emit) }
?.let { nextSibling ->
if (multiline) {
if (nextSibling.text != expectedIndent) {
emit(nextSibling.startOffset, "Expected newline", true)
if (autoCorrect) {
nextSibling.upsertWhitespaceAfterMe(expectedIndent)
}
}
} else {
if (nextSibling.isWhiteSpace()) {
// Disallow
// val list = listOf< String>()
noWhitespaceExpected(nextSibling, autoCorrect, emit)
}
}
}

// No whitespace expected before closing angle bracket of type argument list
// val list = listOf<String >()
node
.findChildByType(ElementType.GT)
?.prevSibling()
?.takeIf { it.elementType == ElementType.WHITE_SPACE }
?.let { noWhitespaceExpected(it, autoCorrect, emit) }
?.let { prevSibling ->
if (multiline) {
if (prevSibling.text != expectedIndent) {
emit(prevSibling.startOffset, "Expected newline", true)
if (autoCorrect) {
prevSibling.upsertWhitespaceBeforeMe(expectedIndent)
}
}
} else {
if (prevSibling.isWhiteSpace()) {
// Disallow
// val list = listOf<String >()
noWhitespaceExpected(prevSibling, autoCorrect, emit)
}
}
}
}

private fun noWhitespaceExpected(
Expand Down
Loading

0 comments on commit be43e79

Please # to comment.