Smart enter for while statement
#KT-3600 In Progress
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2013 JetBrains s.r.o.
|
||||
* 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.
|
||||
@@ -17,7 +17,10 @@
|
||||
package org.jetbrains.jet.lang.psi;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lexer.JetTokens;
|
||||
|
||||
public class JetWhileExpression extends JetWhileExpressionBase {
|
||||
public JetWhileExpression(@NotNull ASTNode node) {
|
||||
@@ -28,4 +31,15 @@ public class JetWhileExpression extends JetWhileExpressionBase {
|
||||
public <R, D> R accept(@NotNull JetVisitor<R, D> visitor, D data) {
|
||||
return visitor.visitWhileExpression(this, data);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@IfNotParsed
|
||||
public PsiElement getLeftParenthesis() {
|
||||
return findChildByType(JetTokens.LPAR);
|
||||
}
|
||||
|
||||
@Nullable @IfNotParsed
|
||||
public PsiElement getRightParenthesis() {
|
||||
return findChildByType(JetTokens.RPAR);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,21 +29,26 @@ import com.intellij.psi.PsiWhiteSpace
|
||||
import org.jetbrains.jet.lang.psi.JetDeclaration
|
||||
import org.jetbrains.jet.lang.psi.JetBlockExpression
|
||||
import org.jetbrains.jet.lang.psi.JetExpression
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrIfStatement
|
||||
import org.jetbrains.jet.lang.psi.JetDeclarationWithBody
|
||||
import org.jetbrains.jet.lang.psi.JetIfExpression
|
||||
import org.jetbrains.jet.plugin.editor.fixers.KotlinWhileConditionFixer
|
||||
import org.jetbrains.jet.plugin.editor.fixers.KotlinMissingWhileBodyFixer
|
||||
import org.jetbrains.jet.lang.psi.JetWhileExpression
|
||||
import org.jetbrains.jet.plugin.editor.fixers.isWithCaret
|
||||
import com.intellij.psi.tree.TokenSet
|
||||
import org.jetbrains.jet.lexer.JetTokens
|
||||
import org.jetbrains.jet.JetNodeTypes
|
||||
|
||||
public class KotlinSmartEnterHandler: SmartEnterProcessorWithFixers() {
|
||||
{
|
||||
addFixers(
|
||||
KotlinMissingIfBranchFixer,
|
||||
KotlinIfConditionFixer
|
||||
KotlinIfConditionFixer(),
|
||||
KotlinMissingIfBranchFixer(),
|
||||
|
||||
KotlinWhileConditionFixer(),
|
||||
KotlinMissingWhileBodyFixer()
|
||||
)
|
||||
|
||||
addEnterProcessors(KotlinPlainEnterProcessor)
|
||||
addEnterProcessors(KotlinPlainEnterProcessor())
|
||||
}
|
||||
|
||||
override fun getStatementAtCaret(editor: Editor?, psiFile: PsiFile?): PsiElement? {
|
||||
@@ -99,16 +104,16 @@ public class KotlinSmartEnterHandler: SmartEnterProcessorWithFixers() {
|
||||
private fun PsiElement.isJetStatement() =
|
||||
getParent() is JetBlockExpression || (getParent()?.getNode()?.getElementType() in IF_BRANCHES_CONTAINERS)
|
||||
|
||||
object KotlinPlainEnterProcessor : SmartEnterProcessorWithFixers.FixEnterProcessor() {
|
||||
private fun getControlStatementBlock(caret: Int, element: PsiElement): JetBlockExpression? {
|
||||
if (element is JetDeclarationWithBody) return element.getBodyExpression() as? JetBlockExpression
|
||||
if (element is JetIfExpression) {
|
||||
if (element.getThen()?.getTextRange()?.contains(caret) == true) {
|
||||
return element.getThen() as? JetBlockExpression
|
||||
class KotlinPlainEnterProcessor : SmartEnterProcessorWithFixers.FixEnterProcessor() {
|
||||
private fun getControlStatementBlock(caret: Int, element: PsiElement): JetExpression? {
|
||||
when (element) {
|
||||
is JetDeclarationWithBody -> return element.getBodyExpression()
|
||||
is JetIfExpression -> {
|
||||
if (element.getThen().isWithCaret(caret)) return element.getThen()
|
||||
if (element.getElse().isWithCaret(caret)) return element.getElse()
|
||||
}
|
||||
|
||||
if (element.getElse()?.getTextRange()?.contains(caret) == true) {
|
||||
return element.getElse() as? JetBlockExpression
|
||||
is JetWhileExpression -> {
|
||||
if (element.getBody()?.getTextRange()?.contains(caret) == true) return element.getBody()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,7 +121,7 @@ public class KotlinSmartEnterHandler: SmartEnterProcessorWithFixers() {
|
||||
}
|
||||
|
||||
override fun doEnter(atCaret: PsiElement, file: PsiFile?, editor: Editor, modified: Boolean): Boolean {
|
||||
val block = getControlStatementBlock(editor.getCaretModel().getOffset(), atCaret)
|
||||
val block = getControlStatementBlock(editor.getCaretModel().getOffset(), atCaret) as? JetBlockExpression
|
||||
if (block != null) {
|
||||
val firstElement = block.getFirstChild()?.getNextSibling()
|
||||
|
||||
|
||||
@@ -24,37 +24,11 @@ import org.jetbrains.jet.lang.psi.JetIfExpression
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.openapi.util.TextRange
|
||||
|
||||
object KotlinIfConditionFixer : SmartEnterProcessorWithFixers.Fixer<KotlinSmartEnterHandler>() {
|
||||
override fun apply(editor: Editor, processor: KotlinSmartEnterHandler, element: PsiElement) {
|
||||
if (element !is JetIfExpression) return
|
||||
val ifExpression = element as JetIfExpression
|
||||
|
||||
val doc = editor.getDocument()
|
||||
val lParen = ifExpression.getLeftParenthesis()
|
||||
val rParen = ifExpression.getRightParenthesis()
|
||||
val condition = ifExpression.getCondition()
|
||||
|
||||
if (condition == null) {
|
||||
if (lParen == null || rParen == null) {
|
||||
var stopOffset = doc.getLineEndOffset(doc.getLineNumber(ifExpression.range.start))
|
||||
val then = ifExpression.getThen()
|
||||
if (then != null) {
|
||||
stopOffset = Math.min(stopOffset, then.range.start)
|
||||
}
|
||||
|
||||
stopOffset = Math.min(stopOffset, ifExpression.range.end)
|
||||
|
||||
doc.replaceString(ifExpression.range.start, stopOffset, "if ()")
|
||||
processor.registerUnresolvedError(ifExpression.range.start + "if (".length())
|
||||
}
|
||||
else {
|
||||
processor.registerUnresolvedError(lParen.range.end)
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (rParen == null) {
|
||||
doc.insertString(condition.range.end, ")")
|
||||
}
|
||||
}
|
||||
}
|
||||
public class KotlinIfConditionFixer : MissingConditionFixer<JetIfExpression>() {
|
||||
override val keyword = "if"
|
||||
override fun getElement(element: PsiElement?) = element as? JetIfExpression
|
||||
override fun getCondition(element: JetIfExpression) = element.getCondition()
|
||||
override fun getLeftParenthesis(element: JetIfExpression) = element.getLeftParenthesis()
|
||||
override fun getRightParenthesis(element: JetIfExpression) = element.getRightParenthesis()
|
||||
override fun getBody(element: JetIfExpression) = element.getThen()
|
||||
}
|
||||
@@ -25,7 +25,7 @@ import org.jetbrains.jet.lang.psi.JetIfExpression
|
||||
import org.jetbrains.jet.plugin.formatter.JetBlock
|
||||
import org.jetbrains.jet.lang.psi.JetBlockExpression
|
||||
|
||||
object KotlinMissingIfBranchFixer : SmartEnterProcessorWithFixers.Fixer<KotlinSmartEnterHandler>() {
|
||||
public class KotlinMissingIfBranchFixer : SmartEnterProcessorWithFixers.Fixer<KotlinSmartEnterHandler>() {
|
||||
override fun apply(editor: Editor, processor: KotlinSmartEnterHandler, element: PsiElement) {
|
||||
if (element !is JetIfExpression) return
|
||||
val ifExpression = element as JetIfExpression
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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 org.jetbrains.jet.plugin.editor.KotlinSmartEnterHandler
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.jet.lang.psi.JetWhileExpression
|
||||
import org.jetbrains.jet.lang.psi.JetBlockExpression
|
||||
|
||||
public class KotlinMissingWhileBodyFixer: SmartEnterProcessorWithFixers.Fixer<KotlinSmartEnterHandler>() {
|
||||
override fun apply(editor: Editor, processor: KotlinSmartEnterHandler, element: PsiElement) {
|
||||
if (element !is JetWhileExpression) return
|
||||
val whileStatement = element as JetWhileExpression
|
||||
|
||||
val doc = editor.getDocument()
|
||||
|
||||
val body = whileStatement.getBody()
|
||||
if (body is JetBlockExpression) return
|
||||
if (body != null && body.startLine(doc) == whileStatement.startLine(doc) && whileStatement.getCondition() != null) return
|
||||
|
||||
val rParenth = whileStatement.getRightParenthesis()
|
||||
if (rParenth == null) return
|
||||
|
||||
doc.insertString(rParenth.range.end, "{}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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 org.jetbrains.jet.lang.psi.JetIfExpression
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.jet.lang.psi.JetWhileExpression
|
||||
|
||||
public class KotlinWhileConditionFixer: MissingConditionFixer<JetWhileExpression>() {
|
||||
override val keyword = "while"
|
||||
override fun getElement(element: PsiElement?) = element as? JetWhileExpression
|
||||
override fun getCondition(element: JetWhileExpression) = element.getCondition()
|
||||
override fun getLeftParenthesis(element: JetWhileExpression) = element.getLeftParenthesis()
|
||||
override fun getRightParenthesis(element: JetWhileExpression) = element.getRightParenthesis()
|
||||
override fun getBody(element: JetWhileExpression) = element.getBody()
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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 org.jetbrains.jet.plugin.editor.KotlinSmartEnterHandler
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.psi.PsiElement
|
||||
|
||||
abstract class MissingConditionFixer<T: PsiElement>() : SmartEnterProcessorWithFixers.Fixer<KotlinSmartEnterHandler>() {
|
||||
override fun apply(editor: Editor, processor: KotlinSmartEnterHandler, element: PsiElement) {
|
||||
val workElement = getElement(element)
|
||||
if (workElement == null) return
|
||||
|
||||
val doc = editor.getDocument()
|
||||
val lParen = getLeftParenthesis(workElement)
|
||||
val rParen = getRightParenthesis(workElement)
|
||||
val condition = getCondition(workElement)
|
||||
|
||||
if (condition == null) {
|
||||
if (lParen == null || rParen == null) {
|
||||
var stopOffset = doc.getLineEndOffset(doc.getLineNumber(workElement.range.start))
|
||||
val then = getBody(workElement)
|
||||
if (then != null) {
|
||||
stopOffset = Math.min(stopOffset, then.range.start)
|
||||
}
|
||||
|
||||
stopOffset = Math.min(stopOffset, workElement.range.end)
|
||||
|
||||
doc.replaceString(workElement.range.start, stopOffset, "$keyword ()")
|
||||
processor.registerUnresolvedError(workElement.range.start + "$keyword (".length())
|
||||
}
|
||||
else {
|
||||
processor.registerUnresolvedError(lParen.range.end)
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (rParen == null) {
|
||||
doc.insertString(condition.range.end, ")")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract val keyword: String
|
||||
abstract fun getElement(element: PsiElement?): T?
|
||||
abstract fun getCondition(element: T): PsiElement?
|
||||
abstract fun getLeftParenthesis(element: T): PsiElement?
|
||||
abstract fun getRightParenthesis(element: T): PsiElement?
|
||||
abstract fun getBody(element: T): PsiElement?
|
||||
}
|
||||
@@ -25,3 +25,4 @@ val TextRange.start: Int get() = getStartOffset()
|
||||
val TextRange.end: Int get() = getEndOffset()
|
||||
|
||||
fun PsiElement.startLine(doc: Document): Int = doc.getLineNumber(range.start)
|
||||
fun PsiElement?.isWithCaret(caret: Int) = this?.getTextRange()?.contains(caret) == true
|
||||
@@ -266,6 +266,100 @@ class SmartEnterTest : JetLightCodeInsightFixtureTestCase() {
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
fun testWhile() = doFunTest(
|
||||
"""
|
||||
while <caret>
|
||||
"""
|
||||
,
|
||||
"""
|
||||
while (<caret>) {
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
fun testWhile2() = doFunTest(
|
||||
"""
|
||||
while<caret>
|
||||
"""
|
||||
,
|
||||
"""
|
||||
while (<caret>) {
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
fun testWhile3() = doFunTest(
|
||||
"""
|
||||
while (<caret>
|
||||
"""
|
||||
,
|
||||
"""
|
||||
while (<caret>) {
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
fun testWhile4() = doFunTest(
|
||||
"""
|
||||
while (true<caret>) {
|
||||
}
|
||||
"""
|
||||
,
|
||||
"""
|
||||
while (true) {
|
||||
<caret>
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
fun testWhile5() = doFunTest(
|
||||
"""
|
||||
while (true) {<caret>
|
||||
"""
|
||||
,
|
||||
"""
|
||||
while (true) {
|
||||
<caret>
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
fun testWhile6() = doFunTest(
|
||||
"""
|
||||
while (true<caret>) {
|
||||
println()
|
||||
}
|
||||
"""
|
||||
,
|
||||
"""
|
||||
while (true) {
|
||||
<caret>
|
||||
println()
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
fun testWhile7() = doFunTest(
|
||||
"""
|
||||
while ()<caret>
|
||||
""",
|
||||
"""
|
||||
while (<caret>) {
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
fun testWhileSingle() = doFunTest(
|
||||
"""
|
||||
<caret>while (true) println()
|
||||
""",
|
||||
"""
|
||||
while (true) println()
|
||||
<caret>
|
||||
"""
|
||||
)
|
||||
|
||||
fun doFunTest(before: String, after: String) {
|
||||
fun String.withFunContext(): String {
|
||||
val bodyText = "//----${this.trimIndent()}//----"
|
||||
|
||||
Reference in New Issue
Block a user