Smart enter for fun declaration

#KT-3600 In Progress
This commit is contained in:
Nikolay Krasko
2014-06-23 17:04:27 +04:00
parent 150ac0ab91
commit d4ac3454ce
4 changed files with 240 additions and 3 deletions
@@ -31,6 +31,7 @@ import org.jetbrains.jet.lang.psi.JetExpression
import org.jetbrains.jet.lang.psi.JetDeclarationWithBody
import org.jetbrains.jet.lang.psi.JetIfExpression
import org.jetbrains.jet.lang.psi.JetForExpression
import org.jetbrains.jet.lang.psi.JetParameter
import com.intellij.psi.tree.TokenSet
import org.jetbrains.jet.JetNodeTypes
import org.jetbrains.jet.lang.psi.JetLoopExpression
@@ -48,7 +49,10 @@ public class KotlinSmartEnterHandler: SmartEnterProcessorWithFixers() {
KotlinWhenSubjectCaretFixer(),
KotlinMissingWhenBodyFixer(),
KotlinDoWhileFixer()
KotlinDoWhileFixer(),
KotlinFunctionParametersFixer(),
KotlinFunctionDeclarationBodyFixer()
)
addEnterProcessors(KotlinPlainEnterProcessor())
@@ -62,7 +66,9 @@ public class KotlinSmartEnterHandler: SmartEnterProcessorWithFixers() {
while (atCaret != null) {
if (atCaret?.isJetStatement() == true) return atCaret
if (atCaret is JetDeclaration && (atCaret?.getParent() !is JetForExpression)) {
if (atCaret is JetDeclaration &&
atCaret !is JetParameter &&
(atCaret?.getParent() !is JetForExpression)) {
return atCaret
}
@@ -94,7 +100,9 @@ public class KotlinSmartEnterHandler: SmartEnterProcessorWithFixers() {
val old = settings.KEEP_SIMPLE_BLOCKS_IN_ONE_LINE
settings.KEEP_SIMPLE_BLOCKS_IN_ONE_LINE = false
val elt = PsiTreeUtil.getParentOfType(file.findElementAt(caretOffset - 1), javaClass<JetBlockExpression>())
reformat(elt)
if (elt != null) {
reformat(elt)
}
settings.KEEP_SIMPLE_BLOCKS_IN_ONE_LINE = old
editor.getCaretModel().moveToOffset(caretOffset - 1)
}
@@ -0,0 +1,54 @@
/*
* Copyright 2010-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.plugin.editor.fixers
import com.intellij.lang.SmartEnterProcessorWithFixers
import com.intellij.openapi.editor.Editor
import org.jetbrains.jet.plugin.editor.KotlinSmartEnterHandler
import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.jet.lang.psi.JetDeclaration
import org.jetbrains.jet.lang.psi.JetFunction
import org.jetbrains.jet.lang.psi.JetClassOrObject
import org.jetbrains.jet.lang.psi.JetPsiUtil
import org.jetbrains.jet.lexer.JetTokens
import org.jetbrains.jet.lang.psi.JetNamedFunction
public class KotlinFunctionDeclarationBodyFixer : SmartEnterProcessorWithFixers.Fixer<KotlinSmartEnterHandler>() {
override fun apply(editor: Editor, processor: KotlinSmartEnterHandler, psiElement: PsiElement) {
if (psiElement !is JetNamedFunction) return
if (psiElement.getBodyExpression() != null|| psiElement.getEqualsToken() != null) return
val parentDeclaration = PsiTreeUtil.getParentOfType(psiElement, javaClass<JetDeclaration>())
if (parentDeclaration is JetClassOrObject) {
if (JetPsiUtil.isTrait(parentDeclaration) || psiElement.hasModifier(JetTokens.ABSTRACT_KEYWORD)) {
return
}
}
val doc = editor.getDocument()
var endOffset = psiElement.range.end
if (psiElement.getText()?.last() == ';') {
doc.deleteString(endOffset - 1, endOffset)
endOffset--
}
doc.insertString(endOffset, "{}")
}
}
@@ -0,0 +1,51 @@
/*
* Copyright 2010-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.plugin.editor.fixers
import com.intellij.lang.SmartEnterProcessorWithFixers
import com.intellij.psi.PsiElement
import org.jetbrains.jet.plugin.editor.KotlinSmartEnterHandler
import com.intellij.openapi.editor.Editor
import org.jetbrains.jet.lang.psi.JetNamedFunction
public class KotlinFunctionParametersFixer : SmartEnterProcessorWithFixers.Fixer<KotlinSmartEnterHandler>() {
override fun apply(editor: Editor, processor: KotlinSmartEnterHandler, psiElement: PsiElement) {
if (psiElement !is JetNamedFunction) return;
val parameterList = psiElement.getValueParameterList()
if (parameterList == null) {
val identifier = psiElement.getNameIdentifier()
if (identifier == null) return
// Insert () after name or after type parameters list when it placed after name
val offset = Math.max(identifier.range.end, psiElement.getTypeParameterList()?.range?.end ?: psiElement.range.start)
editor.getDocument().insertString(offset, "()")
processor.registerUnresolvedError(offset + 1)
}
else {
val rParen = parameterList.getLastChild()
if (rParen == null) return
if (")" != rParen.getText()) {
val params = parameterList.getParameters()
val offset = if (params.isEmpty()) parameterList.range.start + 1 else params.last().range.end
editor.getDocument().insertString(offset, ")")
}
}
}
}
@@ -723,6 +723,124 @@ class SmartEnterTest : JetLightCodeInsightFixtureTestCase() {
"""
)
fun testFunBody() = doFileTest(
"""
fun test<caret>()
"""
,
"""
fun test() {
<caret>
}
"""
)
fun testFunBody1() = doFileTest(
"""
fun test<caret>
"""
,
"""
fun test() {
<caret>
}
"""
)
fun testFunBody2() = doFileTest(
"""
fun (p: Int, s: String<caret>
"""
,
"""
fun (p: Int, s: String) {
<caret>
}
"""
)
fun testFunBody3() = doFileTest(
"""
trait Some {
fun (<caret>p: Int)
}
"""
,
"""
trait Some {
fun (p: Int)
<caret>
}
"""
)
fun testFunBody4() = doFileTest(
"""
class Some {
abstract fun (<caret>p: Int)
}
"""
,
"""
class Some {
abstract fun (p: Int)
<caret>
}
"""
)
fun testFunBody5() = doFileTest(
"""
class Some {
fun test(<caret>p: Int) = 1
}
"""
,
"""
class Some {
fun test(p: Int) = 1
<caret>
}
"""
)
fun testFunBody6() = doFileTest(
"""
fun test(<caret>p: Int) {
}
"""
,
"""
fun test(p: Int) {
<caret>
}
"""
)
fun testFunBody7() = doFileTest(
"""
trait T
fun <U> other() where U: T<caret>
""",
"""
trait T
fun <U> other() where U : T {
<caret>
}
"""
)
fun testFunBody8() = doFileTest(
"""
fun Int.other<caret>
""",
"""
fun Int.other() {
<caret>
}
"""
)
fun doFunTest(before: String, after: String) {
fun String.withFunContext(): String {
val bodyText = "//----${this.trimIndent()}//----"
@@ -734,6 +852,10 @@ class SmartEnterTest : JetLightCodeInsightFixtureTestCase() {
doTest(before.withFunContext(), after.withFunContext())
}
fun doFileTest(before: String, after: String) {
doTest(before.trimIndent().removeFirstEmptyLines(), after.trimIndent().removeFirstEmptyLines())
}
fun doTest(before: String, after: String) {
myFixture.configureByText(JetFileType.INSTANCE, before)
myFixture.performEditorAction(IdeActions.ACTION_EDITOR_COMPLETE_STATEMENT)
@@ -741,4 +863,6 @@ class SmartEnterTest : JetLightCodeInsightFixtureTestCase() {
}
override fun getProjectDescriptor(): LightProjectDescriptor = LightCodeInsightFixtureTestCase.JAVA_LATEST
private fun String.removeFirstEmptyLines() = this.split("\n").dropWhile { it.isEmpty() }.joinToString(separator = "\n")
}