split string literal if Enter is pressed in the middle

#KT-6308 Fixed
This commit is contained in:
Dmitry Jemerov
2015-04-24 20:02:34 +02:00
parent 22aa8599c9
commit cb553bc952
3 changed files with 108 additions and 16 deletions
@@ -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>")
}