split string literal if Enter is pressed in the middle
#KT-6308 Fixed
This commit is contained in:
@@ -16,24 +16,26 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.editor
|
||||
|
||||
import com.intellij.codeInsight.editorActions.enter.EnterHandlerDelegateAdapter
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.util.Ref
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.editor.actionSystem.EditorActionHandler
|
||||
import com.intellij.codeInsight.editorActions.enter.EnterHandlerDelegate
|
||||
import com.intellij.codeInsight.CodeInsightSettings
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager
|
||||
import org.jetbrains.kotlin.psi.JetFile
|
||||
import com.intellij.util.IncorrectOperationException
|
||||
import org.jetbrains.kotlin.lexer.JetTokens
|
||||
import org.jetbrains.kotlin.psi.JetFunctionLiteral
|
||||
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils
|
||||
import com.intellij.codeInsight.editorActions.enter.EnterHandlerDelegate
|
||||
import com.intellij.codeInsight.editorActions.enter.EnterHandlerDelegateAdapter
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.psi.tree.TokenSet
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.actionSystem.EditorActionHandler
|
||||
import com.intellij.openapi.util.Ref
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiWhiteSpace
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager
|
||||
import com.intellij.psi.tree.TokenSet
|
||||
import com.intellij.util.IncorrectOperationException
|
||||
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils
|
||||
import org.jetbrains.kotlin.lexer.JetTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.isSingleQuoted
|
||||
|
||||
public class KotlinEnterHandler: EnterHandlerDelegateAdapter() {
|
||||
companion object {
|
||||
@@ -50,6 +52,11 @@ public class KotlinEnterHandler: EnterHandlerDelegateAdapter() {
|
||||
originalHandler: EditorActionHandler?
|
||||
): EnterHandlerDelegate.Result? {
|
||||
if (file !is JetFile) return EnterHandlerDelegate.Result.Continue
|
||||
|
||||
if (preprocessEnterInStringLiteral(file, editor, caretOffsetRef, caretAdvance)) {
|
||||
return EnterHandlerDelegate.Result.DefaultForceIndent
|
||||
}
|
||||
|
||||
if (!CodeInsightSettings.getInstance()!!.SMART_INDENT_ON_ENTER) return EnterHandlerDelegate.Result.Continue
|
||||
|
||||
val document = editor.getDocument()
|
||||
@@ -84,4 +91,36 @@ public class KotlinEnterHandler: EnterHandlerDelegateAdapter() {
|
||||
|
||||
return EnterHandlerDelegate.Result.Continue
|
||||
}
|
||||
|
||||
// We can't use the core platform logic (EnterInStringLiteralHandler) because it assumes that the string
|
||||
// is a single token and the first character of the token is an opening quote. In the case of Kotlin,
|
||||
// the opening quote is a separate token and the first character of the string token is just a random letter.
|
||||
private fun preprocessEnterInStringLiteral(psiFile: PsiFile,
|
||||
editor: Editor,
|
||||
caretOffsetRef: Ref<Int>,
|
||||
caretAdvanceRef: Ref<Int>): Boolean {
|
||||
var caretOffset = caretOffsetRef.get()
|
||||
val psiAtOffset = psiFile.findElementAt(caretOffset) ?: return false
|
||||
val stringTemplate = psiAtOffset.getStrictParentOfType<JetStringTemplateExpression>() ?: return false
|
||||
if (!stringTemplate.isSingleQuoted()) return false
|
||||
val tokenType = psiAtOffset.getNode().getElementType()
|
||||
when (tokenType) {
|
||||
JetTokens.CLOSING_QUOTE, JetTokens.REGULAR_STRING_PART, JetTokens.ESCAPE_SEQUENCE,
|
||||
JetTokens.SHORT_TEMPLATE_ENTRY_START, JetTokens.LONG_TEMPLATE_ENTRY_START -> {
|
||||
val doc = editor.getDocument()
|
||||
var caretAdvance = 1
|
||||
if (stringTemplate.getParent() is JetDotQualifiedExpression) {
|
||||
doc.insertString(stringTemplate.getTextRange().getEndOffset(), ")")
|
||||
doc.insertString(stringTemplate.getTextRange().getStartOffset(), "(")
|
||||
caretOffset++
|
||||
caretAdvance++
|
||||
}
|
||||
doc.insertString(caretOffset, "\" + \"")
|
||||
caretOffsetRef.set(caretOffset + 3)
|
||||
caretAdvanceRef.set(caretAdvance)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,11 @@ public class KotlinQuoteHandler : QuoteHandler {
|
||||
|
||||
override fun isInsideLiteral(iterator: HighlighterIterator): Boolean {
|
||||
val tokenType = iterator.getTokenType()
|
||||
return tokenType == JetTokens.REGULAR_STRING_PART || tokenType == JetTokens.OPEN_QUOTE || tokenType == JetTokens.CLOSING_QUOTE || tokenType == JetTokens.SHORT_TEMPLATE_ENTRY_START || tokenType == JetTokens.LONG_TEMPLATE_ENTRY_END || tokenType == JetTokens.LONG_TEMPLATE_ENTRY_START
|
||||
return tokenType == JetTokens.REGULAR_STRING_PART ||
|
||||
tokenType == JetTokens.OPEN_QUOTE ||
|
||||
tokenType == JetTokens.CLOSING_QUOTE ||
|
||||
tokenType == JetTokens.SHORT_TEMPLATE_ENTRY_START ||
|
||||
tokenType == JetTokens.LONG_TEMPLATE_ENTRY_END ||
|
||||
tokenType == JetTokens.LONG_TEMPLATE_ENTRY_START
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,6 +234,54 @@ public class TypedHandlerTest : LightCodeInsightTestCase() {
|
||||
"""fun f() { val a = f(<caret>) }"""
|
||||
)
|
||||
|
||||
public fun testSplitStringByEnter(): Unit = doCharTypeTest(
|
||||
'\n',
|
||||
"""val s = "foo<caret>bar"""",
|
||||
"val s = \"foo\" +\n" +
|
||||
" \"bar\""
|
||||
)
|
||||
|
||||
public fun testSplitStringByEnter_Empty(): Unit = doCharTypeTest(
|
||||
'\n',
|
||||
"""val s = "<caret>"""",
|
||||
"val s = \"\" +\n" +
|
||||
" \"\""
|
||||
)
|
||||
|
||||
public fun testSplitStringByEnter_BeforeEscapeSequence(): Unit = doCharTypeTest(
|
||||
'\n',
|
||||
"""val s = "foo<caret>\nbar"""",
|
||||
"val s = \"foo\" +\n" +
|
||||
" \"\\nbar\""
|
||||
)
|
||||
|
||||
public fun testSplitStringByEnter_BeforeSubstitution(): Unit = doCharTypeTest(
|
||||
'\n',
|
||||
"""val s = "foo<caret>${amp}bar"""",
|
||||
"val s = \"foo\" +\n" +
|
||||
" \"${amp}bar\""
|
||||
)
|
||||
|
||||
public fun testSplitStringByEnter_AddParentheses(): Unit = doCharTypeTest(
|
||||
'\n',
|
||||
"""val l = "foo<caret>bar".length()""",
|
||||
"val l = (\"foo\" +\n" +
|
||||
" \"bar\").length()"
|
||||
)
|
||||
|
||||
public fun testSplitStringByEnter_ExistingParentheses(): Unit = doCharTypeTest(
|
||||
'\n',
|
||||
"""val l = ("asdf" + "foo<caret>bar").length()""",
|
||||
"val l = (\"asdf\" + \"foo\" +\n" +
|
||||
" \"bar\").length()"
|
||||
)
|
||||
|
||||
public fun testSplitStringByEnter_TripleQuotedString(): Unit = doCharTypeTest(
|
||||
'\n',
|
||||
"val l = \"\"\"foo<caret>bar\"\"\"",
|
||||
"val l = \"\"\"foo\nbar\"\"\""
|
||||
)
|
||||
|
||||
public fun testTypeLtInFunDeclaration() {
|
||||
doLtGtTest("fun <caret>")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user