LineIndentProvider: support catch and finally
Part of #KT-22211
This commit is contained in:
+99
-43
@@ -67,44 +67,38 @@ abstract class KotlinLikeLangLineIndentProvider : JavaLikeLangLineIndentProvider
|
||||
val after = currentPosition.afterOptionalMix(*WHITE_SPACE_OR_COMMENT_BIT_SET)
|
||||
when {
|
||||
before.isAt(TemplateEntryOpen) -> {
|
||||
val baseLineOffset = before.startOffset
|
||||
val indent = if (!currentPosition.hasEmptyLineAfter(offset) && after.isAt(TemplateEntryClose))
|
||||
val indent = if (!currentPosition.hasLineBreaksAfter(offset) && after.isAt(TemplateEntryClose))
|
||||
Indent.getNoneIndent()
|
||||
else
|
||||
Indent.getNormalIndent()
|
||||
|
||||
return factory.createIndentCalculator(indent) { baseLineOffset }
|
||||
return factory.createIndentCalculator(indent, before.startOffset)
|
||||
}
|
||||
|
||||
before.isAtAnyOf(TryKeyword, FinallyKeyword) -> return factory.createIndentCalculator(
|
||||
before.isAtAnyOf(TryKeyword) || before.isFinallyKeyword() -> return factory.createIndentCalculator(
|
||||
Indent.getNoneIndent(),
|
||||
IndentCalculator.LINE_BEFORE,
|
||||
)
|
||||
|
||||
after.isAt(TemplateEntryClose) -> {
|
||||
val indent = if (currentPosition.hasEmptyLineAfter(offset)) Indent.getNormalIndent() else Indent.getNoneIndent()
|
||||
after.moveBeforeParentheses(TemplateEntryOpen, TemplateEntryClose)
|
||||
return factory.createIndentCalculator(indent, after.startOffset)
|
||||
}
|
||||
}
|
||||
|
||||
before.controlFlowStatementBefore()?.let { controlFlowKeywordPosition ->
|
||||
return factory.createIndentCalculator(
|
||||
when {
|
||||
after.isAt(LeftParenthesis) -> Indent.getContinuationIndent()
|
||||
after.isAtAnyOf(BlockOpeningBrace, Arrow) || controlFlowKeywordPosition.isWhileInsideDoWhile() -> Indent.getNoneIndent()
|
||||
else -> Indent.getNormalIndent()
|
||||
},
|
||||
IndentCalculator.LINE_BEFORE,
|
||||
)
|
||||
}
|
||||
return before.controlFlowStatementBefore()?.let { controlFlowKeywordPosition ->
|
||||
val indent = when {
|
||||
controlFlowKeywordPosition.similarToCatchKeyword() -> if (before.isAt(RightParenthesis)) Indent.getNoneIndent() else Indent.getNormalIndent()
|
||||
after.isAt(LeftParenthesis) -> Indent.getContinuationIndent()
|
||||
after.isAtAnyOf(BlockOpeningBrace, Arrow) || controlFlowKeywordPosition.isWhileInsideDoWhile() -> Indent.getNoneIndent()
|
||||
else -> Indent.getNormalIndent()
|
||||
}
|
||||
|
||||
after.takeIf { it.isAt(TemplateEntryClose) }?.let { templateEntryPosition ->
|
||||
val indent = if (currentPosition.hasEmptyLineAfter(offset)) Indent.getNormalIndent() else Indent.getNoneIndent()
|
||||
templateEntryPosition.moveBeforeParentheses(TemplateEntryOpen, TemplateEntryClose)
|
||||
val baseLineOffset = templateEntryPosition.startOffset
|
||||
return factory.createIndentCalculator(indent) { baseLineOffset }
|
||||
factory.createIndentCalculator(indent, IndentCalculator.LINE_BEFORE)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun SemanticEditorPosition.moveBeforeIfThisIsWhiteSpaceOrComment() = moveBeforeOptionalMix(*WHITE_SPACE_OR_COMMENT_BIT_SET)
|
||||
|
||||
private fun SemanticEditorPosition.isWhileInsideDoWhile(): Boolean {
|
||||
if (!isAt(WhileKeyword)) return false
|
||||
with(copy()) {
|
||||
@@ -129,28 +123,86 @@ abstract class KotlinLikeLangLineIndentProvider : JavaLikeLangLineIndentProvider
|
||||
}
|
||||
|
||||
private fun SemanticEditorPosition.controlFlowStatementBefore(): SemanticEditorPosition? = with(copy()) {
|
||||
if (isAt(BlockOpeningBrace)) {
|
||||
moveBefore()
|
||||
moveBeforeParentheses(LeftParenthesis, RightParenthesis)
|
||||
moveBeforeIfThisIsWhiteSpaceOrComment()
|
||||
}
|
||||
if (isAt(BlockOpeningBrace)) moveBeforeIgnoringWhiteSpaceOrComment()
|
||||
|
||||
if (currElement in CONTROL_FLOW_CONSTRUCTIONS) return this
|
||||
if (!isAt(RightParenthesis)) return null
|
||||
if (isControlFlowKeyword()) return this
|
||||
if (!moveBeforeParenthesesIfPossible()) return null
|
||||
|
||||
moveBeforeParentheses(LeftParenthesis, RightParenthesis)
|
||||
moveBeforeIfThisIsWhiteSpaceOrComment()
|
||||
|
||||
return takeIf { currElement in CONTROL_FLOW_CONSTRUCTIONS }
|
||||
return takeIf { isControlFlowKeyword() }
|
||||
}
|
||||
|
||||
enum class KotlinElement : SemanticEditorPosition.SyntaxElement {
|
||||
private fun SemanticEditorPosition.isControlFlowKeyword(): Boolean =
|
||||
currElement in CONTROL_FLOW_KEYWORDS || isCatchKeyword() || isFinallyKeyword()
|
||||
|
||||
private fun SemanticEditorPosition.similarToCatchKeyword(): Boolean = textOfCurrentPosition() == KtTokens.CATCH_KEYWORD.value
|
||||
|
||||
private fun SemanticEditorPosition.isCatchKeyword(): Boolean = with(copy()) {
|
||||
// try-catch-*-catch
|
||||
do {
|
||||
if (!isAt(KtTokens.IDENTIFIER)) return false
|
||||
if (!similarToCatchKeyword()) return false
|
||||
|
||||
moveBeforeIgnoringWhiteSpaceOrComment()
|
||||
if (!moveBeforeBlockIfPossible()) return false
|
||||
|
||||
if (isAt(TryKeyword)) return true
|
||||
|
||||
if (!moveBeforeParenthesesIfPossible()) return false
|
||||
} while (!isAtEnd)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun SemanticEditorPosition.isFinallyKeyword(): Boolean {
|
||||
if (!isAt(KtTokens.IDENTIFIER)) return false
|
||||
if (textOfCurrentPosition() != KtTokens.FINALLY_KEYWORD.value) return false
|
||||
with(copy()) {
|
||||
moveBeforeIgnoringWhiteSpaceOrComment()
|
||||
if (!moveBeforeBlockIfPossible()) return false
|
||||
|
||||
// try-finally
|
||||
if (isAt(TryKeyword)) return true
|
||||
|
||||
if (!moveBeforeParenthesesIfPossible()) return false
|
||||
|
||||
// try-catch-finally
|
||||
return isCatchKeyword()
|
||||
}
|
||||
}
|
||||
|
||||
private fun SemanticEditorPosition.moveBeforeBlockIfPossible(): Boolean = moveBeforeParenthesesIfPossible(
|
||||
leftParenthesis = BlockOpeningBrace,
|
||||
rightParenthesis = BlockClosingBrace
|
||||
)
|
||||
|
||||
private fun SemanticEditorPosition.moveBeforeParenthesesIfPossible(): Boolean = moveBeforeParenthesesIfPossible(
|
||||
leftParenthesis = LeftParenthesis,
|
||||
rightParenthesis = RightParenthesis
|
||||
)
|
||||
|
||||
private fun SemanticEditorPosition.moveBeforeParenthesesIfPossible(
|
||||
leftParenthesis: SemanticEditorPosition.SyntaxElement,
|
||||
rightParenthesis: SemanticEditorPosition.SyntaxElement,
|
||||
): Boolean {
|
||||
if (!isAt(rightParenthesis)) return false
|
||||
|
||||
moveBeforeParentheses(leftParenthesis, rightParenthesis)
|
||||
moveBeforeIfThisIsWhiteSpaceOrComment()
|
||||
return true
|
||||
}
|
||||
|
||||
private fun SemanticEditorPosition.moveBeforeIfThisIsWhiteSpaceOrComment() = moveBeforeOptionalMix(*WHITE_SPACE_OR_COMMENT_BIT_SET)
|
||||
|
||||
private fun SemanticEditorPosition.moveBeforeIgnoringWhiteSpaceOrComment() {
|
||||
moveBefore()
|
||||
moveBeforeIfThisIsWhiteSpaceOrComment()
|
||||
}
|
||||
|
||||
private enum class KotlinElement : SemanticEditorPosition.SyntaxElement {
|
||||
TemplateEntryOpen,
|
||||
TemplateEntryClose,
|
||||
Arrow,
|
||||
WhenKeyword,
|
||||
CatchKeyword,
|
||||
FinallyKeyword,
|
||||
WhileKeyword,
|
||||
RegularStringPart,
|
||||
KDoc,
|
||||
@@ -173,8 +225,6 @@ abstract class KotlinLikeLangLineIndentProvider : JavaLikeLangLineIndentProvider
|
||||
KtTokens.ELSE_KEYWORD to ElseKeyword,
|
||||
KtTokens.WHEN_KEYWORD to WhenKeyword,
|
||||
KtTokens.TRY_KEYWORD to TryKeyword,
|
||||
KtTokens.CATCH_KEYWORD to CatchKeyword,
|
||||
KtTokens.FINALLY_KEYWORD to FinallyKeyword,
|
||||
KtTokens.WHILE_KEYWORD to WhileKeyword,
|
||||
KtTokens.DO_KEYWORD to DoKeyword,
|
||||
KtTokens.FOR_KEYWORD to ForKeyword,
|
||||
@@ -183,7 +233,7 @@ abstract class KotlinLikeLangLineIndentProvider : JavaLikeLangLineIndentProvider
|
||||
KtTokens.RBRACKET to ArrayClosingBracket,
|
||||
)
|
||||
|
||||
private val CONTROL_FLOW_CONSTRUCTIONS: HashSet<SemanticEditorPosition.SyntaxElement> = hashSetOf(
|
||||
private val CONTROL_FLOW_KEYWORDS: HashSet<SemanticEditorPosition.SyntaxElement> = hashSetOf(
|
||||
WhenKeyword,
|
||||
IfKeyword,
|
||||
ElseKeyword,
|
||||
@@ -191,8 +241,6 @@ abstract class KotlinLikeLangLineIndentProvider : JavaLikeLangLineIndentProvider
|
||||
WhileKeyword,
|
||||
ForKeyword,
|
||||
TryKeyword,
|
||||
CatchKeyword,
|
||||
FinallyKeyword,
|
||||
)
|
||||
|
||||
private val WHITE_SPACE_OR_COMMENT_BIT_SET: Array<SemanticEditorPosition.SyntaxElement> = arrayOf(
|
||||
@@ -201,4 +249,12 @@ abstract class KotlinLikeLangLineIndentProvider : JavaLikeLangLineIndentProvider
|
||||
BlockComment,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun JavaLikeLangLineIndentProvider.IndentCalculatorFactory.createIndentCalculator(
|
||||
indent: Indent?,
|
||||
baseLineOffset: Int
|
||||
): IndentCalculator? = createIndentCalculator(indent) { baseLineOffset }
|
||||
|
||||
private fun SemanticEditorPosition.textOfCurrentPosition(): String =
|
||||
if (isAtEnd) "" else chars.subSequence(startOffset, after().startOffset).toString()
|
||||
+10
@@ -93,6 +93,11 @@ public class PerformanceTypingIndentationTestGenerated extends AbstractPerforman
|
||||
runTest("idea/testData/indentationOnNewline/Catch3.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Catch4.kt")
|
||||
public void testCatch4() throws Exception {
|
||||
runTest("idea/testData/indentationOnNewline/Catch4.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ConsecutiveCallsAfterDot.kt")
|
||||
public void testConsecutiveCallsAfterDot() throws Exception {
|
||||
runTest("idea/testData/indentationOnNewline/ConsecutiveCallsAfterDot.kt");
|
||||
@@ -223,6 +228,11 @@ public class PerformanceTypingIndentationTestGenerated extends AbstractPerforman
|
||||
runTest("idea/testData/indentationOnNewline/Finally3.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Finally4.kt")
|
||||
public void testFinally4() throws Exception {
|
||||
runTest("idea/testData/indentationOnNewline/Finally4.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("For.kt")
|
||||
public void testFor() throws Exception {
|
||||
runTest("idea/testData/indentationOnNewline/For.kt");
|
||||
|
||||
@@ -2,5 +2,3 @@ fun a() = try {
|
||||
// do smth
|
||||
} catch (e: Exception)
|
||||
<caret>
|
||||
|
||||
// WITHOUT_CUSTOM_LINE_INDENT_PROVIDER
|
||||
-2
@@ -1,5 +1,3 @@
|
||||
fun a() = try {
|
||||
// do smth
|
||||
} catch (e: Exception)<caret>
|
||||
|
||||
// WITHOUT_CUSTOM_LINE_INDENT_PROVIDER
|
||||
@@ -4,5 +4,3 @@ fun a() = try {
|
||||
<caret>
|
||||
|
||||
}
|
||||
|
||||
// WITHOUT_CUSTOM_LINE_INDENT_PROVIDER
|
||||
@@ -3,5 +3,3 @@ fun a() = try {
|
||||
} catch (e: Exception) {<caret>
|
||||
|
||||
}
|
||||
|
||||
// WITHOUT_CUSTOM_LINE_INDENT_PROVIDER
|
||||
@@ -4,5 +4,3 @@ fun a() = try {
|
||||
<caret>(e: Exception) {
|
||||
|
||||
}
|
||||
|
||||
// WITHOUT_CUSTOM_LINE_INDENT_PROVIDER
|
||||
@@ -3,5 +3,3 @@ fun a() = try {
|
||||
} catch <caret>(e: Exception) {
|
||||
|
||||
}
|
||||
|
||||
// WITHOUT_CUSTOM_LINE_INDENT_PROVIDER
|
||||
@@ -0,0 +1,20 @@
|
||||
fun a() = try {
|
||||
// do smth
|
||||
} catch (e: Exception) {
|
||||
|
||||
} catch (e: Exception) {
|
||||
|
||||
} catch (e: Exception) {
|
||||
|
||||
} catch (e: Exception) {
|
||||
|
||||
} catch (e: Exception) {
|
||||
|
||||
} catch (e: Exception) {
|
||||
|
||||
} catch (e: Exception) {
|
||||
|
||||
} catch (e: Exception) {
|
||||
|
||||
} catch (e: Exception)
|
||||
<caret>
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
fun a() = try {
|
||||
// do smth
|
||||
} catch (e: Exception) {
|
||||
|
||||
} catch (e: Exception) {
|
||||
|
||||
} catch (e: Exception) {
|
||||
|
||||
} catch (e: Exception) {
|
||||
|
||||
} catch (e: Exception) {
|
||||
|
||||
} catch (e: Exception) {
|
||||
|
||||
} catch (e: Exception) {
|
||||
|
||||
} catch (e: Exception) {
|
||||
|
||||
} catch (e: Exception)<caret>
|
||||
@@ -4,5 +4,3 @@ fun a() = try {
|
||||
<caret>{
|
||||
|
||||
}
|
||||
|
||||
// WITHOUT_CUSTOM_LINE_INDENT_PROVIDER
|
||||
@@ -3,5 +3,3 @@ fun a() = try {
|
||||
} finally<caret> {
|
||||
|
||||
}
|
||||
|
||||
// WITHOUT_CUSTOM_LINE_INDENT_PROVIDER
|
||||
@@ -6,5 +6,3 @@ fun a() {
|
||||
a
|
||||
}
|
||||
}
|
||||
|
||||
// WITHOUT_CUSTOM_LINE_INDENT_PROVIDER
|
||||
@@ -5,5 +5,3 @@ fun a() {
|
||||
a
|
||||
}
|
||||
}
|
||||
|
||||
// WITHOUT_CUSTOM_LINE_INDENT_PROVIDER
|
||||
@@ -4,5 +4,3 @@ fun a() {
|
||||
} finally
|
||||
<caret>
|
||||
}
|
||||
|
||||
// WITHOUT_CUSTOM_LINE_INDENT_PROVIDER
|
||||
@@ -3,5 +3,3 @@ fun a() {
|
||||
dos()
|
||||
} finally<caret>
|
||||
}
|
||||
|
||||
// WITHOUT_CUSTOM_LINE_INDENT_PROVIDER
|
||||
@@ -0,0 +1,4 @@
|
||||
fun a() = try {
|
||||
// do smth
|
||||
} finally
|
||||
<caret>
|
||||
@@ -0,0 +1,3 @@
|
||||
fun a() = try {
|
||||
// do smth
|
||||
} finally<caret>
|
||||
+10
@@ -95,6 +95,11 @@ public class TypingIndentationTestBaseGenerated extends AbstractTypingIndentatio
|
||||
runTest("idea/testData/indentationOnNewline/Catch3.after.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Catch4.after.kt")
|
||||
public void testCatch4() throws Exception {
|
||||
runTest("idea/testData/indentationOnNewline/Catch4.after.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ConsecutiveCallsAfterDot.after.kt")
|
||||
public void testConsecutiveCallsAfterDot() throws Exception {
|
||||
runTest("idea/testData/indentationOnNewline/ConsecutiveCallsAfterDot.after.kt");
|
||||
@@ -225,6 +230,11 @@ public class TypingIndentationTestBaseGenerated extends AbstractTypingIndentatio
|
||||
runTest("idea/testData/indentationOnNewline/Finally3.after.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Finally4.after.kt")
|
||||
public void testFinally4() throws Exception {
|
||||
runTest("idea/testData/indentationOnNewline/Finally4.after.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("For.after.kt")
|
||||
public void testFor() throws Exception {
|
||||
runTest("idea/testData/indentationOnNewline/For.after.kt");
|
||||
|
||||
Reference in New Issue
Block a user