LineIndentProvider: support inside block body between { and first statement/expression

Part of #KT-22211
This commit is contained in:
Dmitry Gridin
2020-06-10 17:40:45 +07:00
parent 63a0b5bfde
commit da5b2cfb64
3 changed files with 71 additions and 24 deletions
@@ -71,6 +71,9 @@ abstract class KotlinLikeLangLineIndentProvider : JavaLikeLangLineIndentProvider
before.isAt(BlockOpeningBrace) && after.isAt(BlockClosingBrace) && !currentPosition.hasLineBreaksAfter(offset) ->
return factory.createIndentCalculator(Indent.getNoneIndent(), before.startOffset)
before.isAt(BlockOpeningBrace) && before.beforeIgnoringWhiteSpaceOrComment().isFunctionDeclaration() ->
return factory.createIndentCalculator(Indent.getNormalIndent(), before.startOffset)
after.isAt(Quest) && after.after().isAt(Colon) -> {
val indent = if (settings.continuationIndentInElvis)
Indent.getContinuationIndent()
@@ -102,7 +105,7 @@ abstract class KotlinLikeLangLineIndentProvider : JavaLikeLangLineIndentProvider
}
before.isAt(Eq) -> {
val declaration = findFunctionOrPropertyDeclarationBefore(before.copyAnd { it.moveBeforeIgnoringWhiteSpaceOrComment() })
val declaration = findFunctionOrPropertyOrMultiDeclarationBefore(before.beforeIgnoringWhiteSpaceOrComment())
if (declaration != null) {
val indent = if (settings.continuationIndentForExpressionBodies)
Indent.getContinuationIndent()
@@ -117,7 +120,7 @@ abstract class KotlinLikeLangLineIndentProvider : JavaLikeLangLineIndentProvider
factory.createIndentCalculatorForParenthesis(before, currentPosition, after, offset, settings)?.let { return it }
}
findFunctionOrPropertyDeclarationBefore(before)?.let {
findFunctionOrPropertyOrMultiDeclarationBefore(before)?.let {
return factory.createIndentCalculator(Indent.getNoneIndent(), it.startOffset)
}
@@ -149,7 +152,7 @@ abstract class KotlinLikeLangLineIndentProvider : JavaLikeLangLineIndentProvider
if (settings.alignWhenMultilineFunctionParentheses) createAlignMultilineIndent(leftParenthesis) else Indent.getNoneIndent()
}
findFunctionKeywordBeforeIdentifier(leftParenthesis.copyAnd { it.moveBeforeIgnoringWhiteSpaceOrComment() })?.let {
findFunctionKeywordBeforeIdentifier(leftParenthesis.beforeIgnoringWhiteSpaceOrComment())?.let {
return createIndentCalculator(indentForParentheses, it.startOffset)
}
@@ -162,7 +165,7 @@ abstract class KotlinLikeLangLineIndentProvider : JavaLikeLangLineIndentProvider
return createIndentCalculator(Indent.getNoneIndent(), leftParenthesis.startOffset)
}
leftParenthesis.copyAnd { it.moveBeforeIgnoringWhiteSpaceOrComment() }.let { keyword ->
leftParenthesis.beforeIgnoringWhiteSpaceOrComment().let { keyword ->
if (keyword.isControlFlowKeyword()) {
return createIndentCalculator(Indent.getNoneIndent(), keyword.startOffset)
}
@@ -180,36 +183,76 @@ abstract class KotlinLikeLangLineIndentProvider : JavaLikeLangLineIndentProvider
}
/**
* @param declarationPosition is position before '=' for expression body or '{'
* @receiver is position before '=' for expression body or '{' for block body
*/
private fun findFunctionOrPropertyDeclarationBefore(declarationPosition: SemanticEditorPosition): SemanticEditorPosition? {
private fun SemanticEditorPosition.isFunctionDeclaration(): Boolean = findFunctionDeclarationBeforeBody(this) != null
/**
* @param endOfDeclaration is position before '=' for expression body or '{' for block body
*/
private fun findFunctionOrPropertyOrMultiDeclarationBefore(endOfDeclaration: SemanticEditorPosition): SemanticEditorPosition? =
findFunctionDeclarationBeforeBody(endOfDeclaration)
?: findPropertyDeclarationBeforeAssignment(endOfDeclaration)
?: findMultiDeclarationBeforeAssignment(endOfDeclaration)
/**
* `val (a, b) = 1 to 2`
* ^
*/
private fun findMultiDeclarationBeforeAssignment(rightParenthesis: SemanticEditorPosition): SemanticEditorPosition? {
if (!rightParenthesis.isAt(RightParenthesis)) return null
return with(rightParenthesis.copy()) {
if (!moveBeforeParenthesesIfPossible()) return null
takeIf { isVarOrVal() }
}
}
/**
* `val a = 5`
* `val a: Int = 5`
* `val List<Int>.a: Int = 5`
* `val <T> List<Int>.a: Int = 5`
*/
private fun findPropertyDeclarationBeforeAssignment(endOfDeclaration: SemanticEditorPosition): SemanticEditorPosition? {
// `val a = 5`
// this is false positive for declaration with explicit return type
if (declarationPosition.isAt(Identifier)) {
findPropertyKeywordBeforeIdentifier(declarationPosition)?.let { return it }
// this is a false positive for a declaration with explicit return type
if (endOfDeclaration.isAt(Identifier)) {
findPropertyKeywordBeforeIdentifier(endOfDeclaration)?.let { return it }
}
return with(declarationPosition.copy()) {
return with(endOfDeclaration.copy()) {
// explicit type `fun a(): String` or `val a: String`
if (moveBeforeTypeQualifierIfPossible(true)) {
if (!isAt(Colon)) return null
moveBeforeIgnoringWhiteSpaceOrComment()
}
if (isAt(RightParenthesis)) {
if (!moveBeforeParenthesesIfPossible()) return null
// destructuring declaration `val (a, b)`
if (isVarOrVal())
this
else
findFunctionKeywordBeforeIdentifier(this)
} else {
findPropertyKeywordBeforeIdentifier(this)
}
findPropertyKeywordBeforeIdentifier(this)
}
}
/**
* `val a = fun() { }`
* `val a = fun String.Companion????.() { }`
* `fun a() = 4`
* `fun a(): Int = 4`
* `fun Int.a(): Int = 4`
* `fun <T> T.a(): Int = 4`
*/
private fun findFunctionDeclarationBeforeBody(endOfDeclaration: SemanticEditorPosition): SemanticEditorPosition? =
with(endOfDeclaration.copy()) {
// explicit type `fun a(): String`
// ^
if (moveBeforeTypeQualifierIfPossible(true)) {
if (!isAt(Colon)) return null
moveBeforeIgnoringWhiteSpaceOrComment()
}
if (!moveBeforeParenthesesIfPossible()) return null
findFunctionKeywordBeforeIdentifier(this)
}
private fun findPropertyKeywordBeforeIdentifier(identifierPosition: SemanticEditorPosition): SemanticEditorPosition? {
if (!identifierPosition.isAt(Identifier)) return null
return with(identifierPosition.copy()) {
@@ -226,12 +269,14 @@ abstract class KotlinLikeLangLineIndentProvider : JavaLikeLangLineIndentProvider
*/
private fun findFunctionKeywordBeforeIdentifier(identifierPosition: SemanticEditorPosition): SemanticEditorPosition? {
// anonymous function `val a = fun() { }`
// ^
if (identifierPosition.isAt(FunctionKeyword)) return identifierPosition
return with(identifierPosition.copy()) {
moveBeforeWhileThisIsWhiteSpaceOrComment()
// anonymous function with receiver `val a = fun String.Companion????.() { }`
// ^
if (isAt(Dot)) {
moveBeforeIgnoringWhiteSpaceOrComment()
if (!moveBeforeTypeQualifierIfPossible(true)) return null
@@ -453,6 +498,8 @@ abstract class KotlinLikeLangLineIndentProvider : JavaLikeLangLineIndentProvider
moveBeforeWhileThisIsWhiteSpaceOrComment()
}
private fun SemanticEditorPosition.beforeIgnoringWhiteSpaceOrComment() = copyAnd { it.moveBeforeIgnoringWhiteSpaceOrComment() }
private enum class KotlinElement : SemanticEditorPosition.SyntaxElement {
TemplateEntryOpen,
TemplateEntryClose,
+2 -2
View File
@@ -1,8 +1,8 @@
object Test { // TODO restore correct behavior
// some
fun test() {
<caret>
<caret>
}
}
// WITHOUT_CUSTOM_LINE_INDENT_PROVIDER
// IGNORE_FORMATTER
+1 -1
View File
@@ -4,4 +4,4 @@ object Test { // TODO restore correct behavior
}
}
// WITHOUT_CUSTOM_LINE_INDENT_PROVIDER
// IGNORE_FORMATTER