Expand Selection on opening curly brace should select the entire block right away #KT-18769 Fixed

This commit is contained in:
Toshiaki Kameyama
2018-04-03 17:57:34 +09:00
committed by Nikolay Krasko
parent 2227d205c7
commit 90be6235f7
7 changed files with 51 additions and 22 deletions
@@ -16,60 +16,69 @@
package org.jetbrains.kotlin.idea.editor.wordSelection package org.jetbrains.kotlin.idea.editor.wordSelection
import com.intellij.lang.ASTNode import com.intellij.codeInsight.editorActions.ExtendWordSelectionHandlerBase
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.util.TextRange import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiWhiteSpace import com.intellij.psi.PsiWhiteSpace
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtBlockExpression import org.jetbrains.kotlin.psi.KtBlockExpression
import org.jetbrains.kotlin.psi.KtWhenExpression import org.jetbrains.kotlin.psi.KtWhenExpression
import org.jetbrains.kotlin.lexer.KtTokens
import java.util.ArrayList
import com.intellij.codeInsight.editorActions.ExtendWordSelectionHandlerBase
import org.jetbrains.kotlin.psi.psiUtil.allChildren import org.jetbrains.kotlin.psi.psiUtil.allChildren
import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset
import java.util.*
/** /**
* Originally from IDEA platform: CodeBlockOrInitializerSelectioner * Originally from IDEA platform: CodeBlockOrInitializerSelectioner
*/ */
class KotlinCodeBlockSelectioner : ExtendWordSelectionHandlerBase() { class KotlinCodeBlockSelectioner : ExtendWordSelectionHandlerBase() {
override fun canSelect(e: PsiElement)
= e is KtBlockExpression || e is KtWhenExpression companion object {
fun canSelect(e: PsiElement) = isTarget(e) || (isBrace(e) && isTarget(e.parent))
private fun isTarget(e: PsiElement) = e is KtBlockExpression || e is KtWhenExpression
private fun isBrace(e: PsiElement): Boolean {
val elementType = e.node.elementType
return elementType == KtTokens.LBRACE || elementType == KtTokens.RBRACE
}
}
override fun canSelect(e: PsiElement) = KotlinCodeBlockSelectioner.canSelect(e)
override fun select(e: PsiElement, editorText: CharSequence, cursorOffset: Int, editor: Editor): List<TextRange>? { override fun select(e: PsiElement, editorText: CharSequence, cursorOffset: Int, editor: Editor): List<TextRange>? {
val result = ArrayList<TextRange>() val result = ArrayList<TextRange>()
val start = findBlockContentStart(e) val block = if (isBrace(e)) e.parent else e
val end = findBlockContentEnd(e) val start = findBlockContentStart(block)
val end = findBlockContentEnd(block)
if (end > start) { if (end > start) {
result.addAll(ExtendWordSelectionHandlerBase.expandToWholeLine(editorText, TextRange(start, end))) result.addAll(ExtendWordSelectionHandlerBase.expandToWholeLine(editorText, TextRange(start, end)))
} }
result.addAll(ExtendWordSelectionHandlerBase.expandToWholeLine(editorText, block.textRange!!))
result.addAll(ExtendWordSelectionHandlerBase.expandToWholeLine(editorText, e.textRange!!))
return result return result
} }
private fun findBlockContentStart(block: PsiElement): Int { private fun findBlockContentStart(block: PsiElement): Int {
val element = block.allChildren val element = block.allChildren
.dropWhile { it.node.elementType != KtTokens.LBRACE } // search for '{' .dropWhile { it.node.elementType != KtTokens.LBRACE } // search for '{'
.drop(1) // skip it .drop(1) // skip it
.dropWhile { it is PsiWhiteSpace } // and skip all whitespaces .dropWhile { it is PsiWhiteSpace } // and skip all whitespaces
.firstOrNull() ?: block .firstOrNull() ?: block
return element.startOffset return element.startOffset
} }
private fun findBlockContentEnd(block: PsiElement): Int { private fun findBlockContentEnd(block: PsiElement): Int {
val element = block.allChildren val element = block.allChildren
.toList() .toList()
.asReversed() .asReversed()
.asSequence() .asSequence()
.dropWhile { it.node.elementType != KtTokens.RBRACE } // search for '}' .dropWhile { it.node.elementType != KtTokens.RBRACE } // search for '}'
.drop(1) // skip it .drop(1) // skip it
.dropWhile { it is PsiWhiteSpace } // and skip all whitespaces .dropWhile { it is PsiWhiteSpace } // and skip all whitespaces
.firstOrNull() ?: block .firstOrNull() ?: block
return element.endOffset return element.endOffset
} }
} }
@@ -29,6 +29,7 @@ class KotlinWordSelectionFilter : Condition<PsiElement>{
if (e.language != KotlinLanguage.INSTANCE) return true if (e.language != KotlinLanguage.INSTANCE) return true
if (KotlinListSelectioner.canSelect(e)) return false if (KotlinListSelectioner.canSelect(e)) return false
if (KotlinCodeBlockSelectioner.canSelect(e)) return false
val elementType = e.node.elementType val elementType = e.node.elementType
if (elementType == KtTokens.REGULAR_STRING_PART || elementType == KtTokens.ESCAPE_SEQUENCE) return true if (elementType == KtTokens.REGULAR_STRING_PART || elementType == KtTokens.ESCAPE_SEQUENCE) return true
+4
View File
@@ -0,0 +1,4 @@
fun test() {<caret>
f()
g()
}
+4
View File
@@ -0,0 +1,4 @@
fun test() <selection>{<caret>
f()
g()
}</selection>
+4
View File
@@ -0,0 +1,4 @@
fun test() {
f()
g()
}<caret>
+4
View File
@@ -0,0 +1,4 @@
fun test() <selection>{
f()
g()
}</selection><caret>
@@ -130,6 +130,9 @@ public class WordSelectionTest extends KotlinLightCodeInsightFixtureTestCase {
public void testDeclarationWithComment3() { doTest(); } public void testDeclarationWithComment3() { doTest(); }
public void testDeclarationWithComment4() { doTest(); } public void testDeclarationWithComment4() { doTest(); }
public void testLeftBrace() { doTest(); }
public void testRightBrace() { doTest(); }
private void doTest() { private void doTest() {
String dirName = getTestName(false); String dirName = getTestName(false);