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)
|
val after = currentPosition.afterOptionalMix(*WHITE_SPACE_OR_COMMENT_BIT_SET)
|
||||||
when {
|
when {
|
||||||
before.isAt(TemplateEntryOpen) -> {
|
before.isAt(TemplateEntryOpen) -> {
|
||||||
val baseLineOffset = before.startOffset
|
val indent = if (!currentPosition.hasLineBreaksAfter(offset) && after.isAt(TemplateEntryClose))
|
||||||
val indent = if (!currentPosition.hasEmptyLineAfter(offset) && after.isAt(TemplateEntryClose))
|
|
||||||
Indent.getNoneIndent()
|
Indent.getNoneIndent()
|
||||||
else
|
else
|
||||||
Indent.getNormalIndent()
|
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(),
|
Indent.getNoneIndent(),
|
||||||
IndentCalculator.LINE_BEFORE,
|
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 before.controlFlowStatementBefore()?.let { controlFlowKeywordPosition ->
|
||||||
return factory.createIndentCalculator(
|
val indent = when {
|
||||||
when {
|
controlFlowKeywordPosition.similarToCatchKeyword() -> if (before.isAt(RightParenthesis)) Indent.getNoneIndent() else Indent.getNormalIndent()
|
||||||
after.isAt(LeftParenthesis) -> Indent.getContinuationIndent()
|
after.isAt(LeftParenthesis) -> Indent.getContinuationIndent()
|
||||||
after.isAtAnyOf(BlockOpeningBrace, Arrow) || controlFlowKeywordPosition.isWhileInsideDoWhile() -> Indent.getNoneIndent()
|
after.isAtAnyOf(BlockOpeningBrace, Arrow) || controlFlowKeywordPosition.isWhileInsideDoWhile() -> Indent.getNoneIndent()
|
||||||
else -> Indent.getNormalIndent()
|
else -> Indent.getNormalIndent()
|
||||||
},
|
}
|
||||||
IndentCalculator.LINE_BEFORE,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
after.takeIf { it.isAt(TemplateEntryClose) }?.let { templateEntryPosition ->
|
factory.createIndentCalculator(indent, IndentCalculator.LINE_BEFORE)
|
||||||
val indent = if (currentPosition.hasEmptyLineAfter(offset)) Indent.getNormalIndent() else Indent.getNoneIndent()
|
|
||||||
templateEntryPosition.moveBeforeParentheses(TemplateEntryOpen, TemplateEntryClose)
|
|
||||||
val baseLineOffset = templateEntryPosition.startOffset
|
|
||||||
return factory.createIndentCalculator(indent) { baseLineOffset }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun SemanticEditorPosition.moveBeforeIfThisIsWhiteSpaceOrComment() = moveBeforeOptionalMix(*WHITE_SPACE_OR_COMMENT_BIT_SET)
|
|
||||||
|
|
||||||
private fun SemanticEditorPosition.isWhileInsideDoWhile(): Boolean {
|
private fun SemanticEditorPosition.isWhileInsideDoWhile(): Boolean {
|
||||||
if (!isAt(WhileKeyword)) return false
|
if (!isAt(WhileKeyword)) return false
|
||||||
with(copy()) {
|
with(copy()) {
|
||||||
@@ -129,28 +123,86 @@ abstract class KotlinLikeLangLineIndentProvider : JavaLikeLangLineIndentProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun SemanticEditorPosition.controlFlowStatementBefore(): SemanticEditorPosition? = with(copy()) {
|
private fun SemanticEditorPosition.controlFlowStatementBefore(): SemanticEditorPosition? = with(copy()) {
|
||||||
if (isAt(BlockOpeningBrace)) {
|
if (isAt(BlockOpeningBrace)) moveBeforeIgnoringWhiteSpaceOrComment()
|
||||||
moveBefore()
|
|
||||||
moveBeforeParentheses(LeftParenthesis, RightParenthesis)
|
|
||||||
moveBeforeIfThisIsWhiteSpaceOrComment()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currElement in CONTROL_FLOW_CONSTRUCTIONS) return this
|
if (isControlFlowKeyword()) return this
|
||||||
if (!isAt(RightParenthesis)) return null
|
if (!moveBeforeParenthesesIfPossible()) return null
|
||||||
|
|
||||||
moveBeforeParentheses(LeftParenthesis, RightParenthesis)
|
return takeIf { isControlFlowKeyword() }
|
||||||
moveBeforeIfThisIsWhiteSpaceOrComment()
|
|
||||||
|
|
||||||
return takeIf { currElement in CONTROL_FLOW_CONSTRUCTIONS }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
TemplateEntryOpen,
|
||||||
TemplateEntryClose,
|
TemplateEntryClose,
|
||||||
Arrow,
|
Arrow,
|
||||||
WhenKeyword,
|
WhenKeyword,
|
||||||
CatchKeyword,
|
|
||||||
FinallyKeyword,
|
|
||||||
WhileKeyword,
|
WhileKeyword,
|
||||||
RegularStringPart,
|
RegularStringPart,
|
||||||
KDoc,
|
KDoc,
|
||||||
@@ -173,8 +225,6 @@ abstract class KotlinLikeLangLineIndentProvider : JavaLikeLangLineIndentProvider
|
|||||||
KtTokens.ELSE_KEYWORD to ElseKeyword,
|
KtTokens.ELSE_KEYWORD to ElseKeyword,
|
||||||
KtTokens.WHEN_KEYWORD to WhenKeyword,
|
KtTokens.WHEN_KEYWORD to WhenKeyword,
|
||||||
KtTokens.TRY_KEYWORD to TryKeyword,
|
KtTokens.TRY_KEYWORD to TryKeyword,
|
||||||
KtTokens.CATCH_KEYWORD to CatchKeyword,
|
|
||||||
KtTokens.FINALLY_KEYWORD to FinallyKeyword,
|
|
||||||
KtTokens.WHILE_KEYWORD to WhileKeyword,
|
KtTokens.WHILE_KEYWORD to WhileKeyword,
|
||||||
KtTokens.DO_KEYWORD to DoKeyword,
|
KtTokens.DO_KEYWORD to DoKeyword,
|
||||||
KtTokens.FOR_KEYWORD to ForKeyword,
|
KtTokens.FOR_KEYWORD to ForKeyword,
|
||||||
@@ -183,7 +233,7 @@ abstract class KotlinLikeLangLineIndentProvider : JavaLikeLangLineIndentProvider
|
|||||||
KtTokens.RBRACKET to ArrayClosingBracket,
|
KtTokens.RBRACKET to ArrayClosingBracket,
|
||||||
)
|
)
|
||||||
|
|
||||||
private val CONTROL_FLOW_CONSTRUCTIONS: HashSet<SemanticEditorPosition.SyntaxElement> = hashSetOf(
|
private val CONTROL_FLOW_KEYWORDS: HashSet<SemanticEditorPosition.SyntaxElement> = hashSetOf(
|
||||||
WhenKeyword,
|
WhenKeyword,
|
||||||
IfKeyword,
|
IfKeyword,
|
||||||
ElseKeyword,
|
ElseKeyword,
|
||||||
@@ -191,8 +241,6 @@ abstract class KotlinLikeLangLineIndentProvider : JavaLikeLangLineIndentProvider
|
|||||||
WhileKeyword,
|
WhileKeyword,
|
||||||
ForKeyword,
|
ForKeyword,
|
||||||
TryKeyword,
|
TryKeyword,
|
||||||
CatchKeyword,
|
|
||||||
FinallyKeyword,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
private val WHITE_SPACE_OR_COMMENT_BIT_SET: Array<SemanticEditorPosition.SyntaxElement> = arrayOf(
|
private val WHITE_SPACE_OR_COMMENT_BIT_SET: Array<SemanticEditorPosition.SyntaxElement> = arrayOf(
|
||||||
@@ -201,4 +249,12 @@ abstract class KotlinLikeLangLineIndentProvider : JavaLikeLangLineIndentProvider
|
|||||||
BlockComment,
|
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");
|
runTest("idea/testData/indentationOnNewline/Catch3.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("Catch4.kt")
|
||||||
|
public void testCatch4() throws Exception {
|
||||||
|
runTest("idea/testData/indentationOnNewline/Catch4.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("ConsecutiveCallsAfterDot.kt")
|
@TestMetadata("ConsecutiveCallsAfterDot.kt")
|
||||||
public void testConsecutiveCallsAfterDot() throws Exception {
|
public void testConsecutiveCallsAfterDot() throws Exception {
|
||||||
runTest("idea/testData/indentationOnNewline/ConsecutiveCallsAfterDot.kt");
|
runTest("idea/testData/indentationOnNewline/ConsecutiveCallsAfterDot.kt");
|
||||||
@@ -223,6 +228,11 @@ public class PerformanceTypingIndentationTestGenerated extends AbstractPerforman
|
|||||||
runTest("idea/testData/indentationOnNewline/Finally3.kt");
|
runTest("idea/testData/indentationOnNewline/Finally3.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("Finally4.kt")
|
||||||
|
public void testFinally4() throws Exception {
|
||||||
|
runTest("idea/testData/indentationOnNewline/Finally4.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("For.kt")
|
@TestMetadata("For.kt")
|
||||||
public void testFor() throws Exception {
|
public void testFor() throws Exception {
|
||||||
runTest("idea/testData/indentationOnNewline/For.kt");
|
runTest("idea/testData/indentationOnNewline/For.kt");
|
||||||
|
|||||||
@@ -2,5 +2,3 @@ fun a() = try {
|
|||||||
// do smth
|
// do smth
|
||||||
} catch (e: Exception)
|
} catch (e: Exception)
|
||||||
<caret>
|
<caret>
|
||||||
|
|
||||||
// WITHOUT_CUSTOM_LINE_INDENT_PROVIDER
|
|
||||||
-2
@@ -1,5 +1,3 @@
|
|||||||
fun a() = try {
|
fun a() = try {
|
||||||
// do smth
|
// do smth
|
||||||
} catch (e: Exception)<caret>
|
} catch (e: Exception)<caret>
|
||||||
|
|
||||||
// WITHOUT_CUSTOM_LINE_INDENT_PROVIDER
|
|
||||||
@@ -4,5 +4,3 @@ fun a() = try {
|
|||||||
<caret>
|
<caret>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WITHOUT_CUSTOM_LINE_INDENT_PROVIDER
|
|
||||||
@@ -3,5 +3,3 @@ fun a() = try {
|
|||||||
} catch (e: Exception) {<caret>
|
} catch (e: Exception) {<caret>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WITHOUT_CUSTOM_LINE_INDENT_PROVIDER
|
|
||||||
@@ -4,5 +4,3 @@ fun a() = try {
|
|||||||
<caret>(e: Exception) {
|
<caret>(e: Exception) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WITHOUT_CUSTOM_LINE_INDENT_PROVIDER
|
|
||||||
@@ -3,5 +3,3 @@ fun a() = try {
|
|||||||
} catch <caret>(e: Exception) {
|
} 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>{
|
<caret>{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WITHOUT_CUSTOM_LINE_INDENT_PROVIDER
|
|
||||||
@@ -3,5 +3,3 @@ fun a() = try {
|
|||||||
} finally<caret> {
|
} finally<caret> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WITHOUT_CUSTOM_LINE_INDENT_PROVIDER
|
|
||||||
@@ -6,5 +6,3 @@ fun a() {
|
|||||||
a
|
a
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WITHOUT_CUSTOM_LINE_INDENT_PROVIDER
|
|
||||||
@@ -5,5 +5,3 @@ fun a() {
|
|||||||
a
|
a
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WITHOUT_CUSTOM_LINE_INDENT_PROVIDER
|
|
||||||
@@ -4,5 +4,3 @@ fun a() {
|
|||||||
} finally
|
} finally
|
||||||
<caret>
|
<caret>
|
||||||
}
|
}
|
||||||
|
|
||||||
// WITHOUT_CUSTOM_LINE_INDENT_PROVIDER
|
|
||||||
@@ -3,5 +3,3 @@ fun a() {
|
|||||||
dos()
|
dos()
|
||||||
} finally<caret>
|
} 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");
|
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")
|
@TestMetadata("ConsecutiveCallsAfterDot.after.kt")
|
||||||
public void testConsecutiveCallsAfterDot() throws Exception {
|
public void testConsecutiveCallsAfterDot() throws Exception {
|
||||||
runTest("idea/testData/indentationOnNewline/ConsecutiveCallsAfterDot.after.kt");
|
runTest("idea/testData/indentationOnNewline/ConsecutiveCallsAfterDot.after.kt");
|
||||||
@@ -225,6 +230,11 @@ public class TypingIndentationTestBaseGenerated extends AbstractTypingIndentatio
|
|||||||
runTest("idea/testData/indentationOnNewline/Finally3.after.kt");
|
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")
|
@TestMetadata("For.after.kt")
|
||||||
public void testFor() throws Exception {
|
public void testFor() throws Exception {
|
||||||
runTest("idea/testData/indentationOnNewline/For.after.kt");
|
runTest("idea/testData/indentationOnNewline/For.after.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user