Create From Usage: Do not generate unnecessary/skip necessary empty lines

This commit is contained in:
Alexey Sedunov
2014-11-12 15:49:06 +03:00
parent 2e7cbab453
commit c487cc7838
123 changed files with 141 additions and 154 deletions
@@ -109,10 +109,15 @@ public class JetPsiFactory(private val project: Project) {
return createProperty("val${text}x").findElementAt(3)!! return createProperty("val${text}x").findElementAt(3)!!
} }
// Remove when all Java usages are rewritten to Kotlin
public fun createNewLine(): PsiElement { public fun createNewLine(): PsiElement {
return createWhiteSpace("\n") return createWhiteSpace("\n")
} }
public fun createNewLine(lineBreaks: Int): PsiElement {
return createWhiteSpace("\n".repeat(lineBreaks))
}
public fun createClass(text: String): JetClass { public fun createClass(text: String): JetClass {
return createDeclaration(text) return createDeclaration(text)
} }
@@ -66,6 +66,8 @@ import org.jetbrains.jet.plugin.util.IdeDescriptorRenderers
import org.jetbrains.jet.plugin.quickfix.createFromUsage.createClass.ClassKind import org.jetbrains.jet.plugin.quickfix.createFromUsage.createClass.ClassKind
import org.jetbrains.jet.plugin.util.isAny import org.jetbrains.jet.plugin.util.isAny
import org.jetbrains.jet.utils.addToStdlib.singletonOrEmptyList import org.jetbrains.jet.utils.addToStdlib.singletonOrEmptyList
import org.jetbrains.jet.lang.psi.psiUtil.siblings
import org.jetbrains.jet.plugin.refactoring.getLineCount
private val TYPE_PARAMETER_LIST_VARIABLE_NAME = "typeParameterList" private val TYPE_PARAMETER_LIST_VARIABLE_NAME = "typeParameterList"
private val TEMPLATE_FROM_USAGE_FUNCTION_BODY = "New Kotlin Function Body.kt" private val TEMPLATE_FROM_USAGE_FUNCTION_BODY = "New Kotlin Function Body.kt"
@@ -427,40 +429,31 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
val newLine = psiFactory.createNewLine() val newLine = psiFactory.createNewLine()
fun prepend(element: PsiElement, elementBeforeStart: PsiElement, skipInitial: Boolean): PsiElement { fun calcNecessaryEmptyLines(decl: JetDeclaration, after: Boolean): Int {
val parent = elementBeforeStart.getParent()!! var lineBreaksPresent: Int = 0
val anchor = var neighbor: PsiElement? = null
if (!skipInitial && elementBeforeStart !is PsiWhiteSpace) { for (sibling in decl.siblings(forward = after, withItself = false)) {
elementBeforeStart when (sibling) {
is PsiWhiteSpace -> lineBreaksPresent += (sibling.getText() ?: "").count { it == '\n' }
else -> {
neighbor = sibling
break
} }
else { }
PsiTreeUtil.skipSiblingsForward(elementBeforeStart, javaClass<PsiWhiteSpace>())
}
val addedElement = parent.addBefore(element, anchor)!!
parent.addAfter(newLine, addedElement)
parent.addAfter(newLine, addedElement)
return addedElement
}
fun append(element: PsiElement, elementAfterEnd: PsiElement, skipInitial: Boolean): PsiElement {
val parent = elementAfterEnd.getParent()!!
val anchor =
if (!skipInitial && elementAfterEnd !is PsiWhiteSpace) {
elementAfterEnd
}
else {
PsiTreeUtil.skipSiblingsBackward(elementAfterEnd, javaClass<PsiWhiteSpace>())
}
val addedElement = parent.addAfter(element, anchor)!!
if (anchor?.getNode()?.getElementType() != JetTokens.LBRACE) {
parent.addAfter(newLine, anchor)
parent.addAfter(newLine, anchor)
} }
return addedElement
val neighborType = neighbor?.getNode()?.getElementType()
val lineBreaksNeeded = when {
neighborType == JetTokens.LBRACE, neighborType == JetTokens.RBRACE -> 1
neighbor is JetDeclaration && (neighbor !is JetProperty || decl !is JetProperty) -> 2
else -> 1
}
return Math.max(lineBreaksNeeded - lineBreaksPresent, 0)
} }
when (containingElement) { val declarationInPlace = when (containingElement) {
is JetFile -> return append(declaration, containingElement.getLastChild()!!, false) as JetNamedDeclaration is JetFile -> containingElement.add(declaration) as JetNamedDeclaration
is JetClassOrObject -> { is JetClassOrObject -> {
var classBody = containingElement.getBody() var classBody = containingElement.getBody()
@@ -470,11 +463,13 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
} }
if (declaration is JetNamedFunction) { if (declaration is JetNamedFunction) {
val rBrace = classBody!!.getRBrace() val anchor = PsiTreeUtil.skipSiblingsBackward(
return (rBrace?.let { append(declaration, it, true) } classBody!!.getRBrace() ?: classBody!!.getLastChild()!!,
?: append(declaration, classBody!!.getLastChild()!!, false)) as JetNamedDeclaration javaClass<PsiWhiteSpace>()
)
classBody.addAfter(declaration, anchor) as JetNamedDeclaration
} }
return prepend(declaration, classBody!!.getLBrace()!!, true) as JetNamedDeclaration else classBody.addAfter(declaration, classBody!!.getLBrace()!!) as JetNamedDeclaration
} }
is JetBlockExpression -> { is JetBlockExpression -> {
@@ -485,13 +480,23 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
parent.addAfter(newLine, containingElement) parent.addAfter(newLine, containingElement)
} }
return prepend(declaration, containingElement.getFirstChild()!!, false) as JetNamedDeclaration containingElement.addBefore(declaration, containingElement.getFirstChild()!!) as JetNamedDeclaration
} }
return prepend(declaration, containingElement.getLBrace()!!, true) as JetNamedDeclaration else containingElement.addAfter(declaration, containingElement.getLBrace()!!) as JetNamedDeclaration
} }
else -> throw AssertionError("Invalid containing element: ${containingElement.getText()}") else -> throw AssertionError("Invalid containing element: ${containingElement.getText()}")
} }
val parent = declarationInPlace.getParent()
calcNecessaryEmptyLines(declarationInPlace, false).let {
if (it > 0) parent.addBefore(psiFactory.createNewLine(it), declarationInPlace)
}
calcNecessaryEmptyLines(declarationInPlace, true).let {
if (it > 0) parent.addAfter(psiFactory.createNewLine(it), declarationInPlace)
}
return declarationInPlace
} }
} }
@@ -1,7 +1,6 @@
// "Create class 'Foo'" "true" // "Create class 'Foo'" "true"
class A<T>(val n: T) { class A<T>(val n: T) {
class Foo(i: Int) { class Foo(i: Int) {
} }
@@ -3,7 +3,6 @@
// ERROR: <html>Type mismatch.<table><tr><td>Required:</td><td>U</td></tr><tr><td>Found:</td><td>U</td></tr></table></html> // ERROR: <html>Type mismatch.<table><tr><td>Required:</td><td>U</td></tr><tr><td>Found:</td><td>U</td></tr></table></html>
class A<T>(val n: T) { class A<T>(val n: T) {
inner class Foo<U>(u: U) { inner class Foo<U>(u: U) {
} }
@@ -1,7 +1,6 @@
// "Create class 'Foo'" "true" // "Create class 'Foo'" "true"
object A { object A {
class Foo(i: Int) { class Foo(i: Int) {
} }
@@ -1,7 +1,6 @@
// "Create class 'Foo'" "true" // "Create class 'Foo'" "true"
class A<T>(val n: T) { class A<T>(val n: T) {
inner class Foo(i: Int) { inner class Foo(i: Int) {
} }
@@ -1,7 +1,6 @@
// "Create class 'Foo'" "true" // "Create class 'Foo'" "true"
class A<T>(val n: T) { class A<T>(val n: T) {
inner class Foo(i: Int, s: String) { inner class Foo(i: Int, s: String) {
} }
@@ -1,7 +1,6 @@
// "Create class 'Foo'" "true" // "Create class 'Foo'" "true"
class B<T>(val t: T) { class B<T>(val t: T) {
class Foo<U, V>(u: U, v: V) { class Foo<U, V>(u: U, v: V) {
} }
@@ -3,7 +3,6 @@
// ERROR: An integer literal does not conform to the expected type U // ERROR: An integer literal does not conform to the expected type U
class B<T>(val t: T) { class B<T>(val t: T) {
inner class Foo<U, V>(u: U, v: V) { inner class Foo<U, V>(u: U, v: V) {
} }
@@ -2,7 +2,6 @@
// ERROR: <html>Type mismatch.<table><tr><td>Required:</td><td>U</td></tr><tr><td>Found:</td><td>kotlin.String</td></tr></table></html> // ERROR: <html>Type mismatch.<table><tr><td>Required:</td><td>U</td></tr><tr><td>Found:</td><td>kotlin.String</td></tr></table></html>
class B<T>(val t: T) { class B<T>(val t: T) {
inner class Foo<U>(i: Int, u: U) { inner class Foo<U>(i: Int, u: U) {
} }
@@ -3,7 +3,6 @@
// ERROR: An integer literal does not conform to the expected type V // ERROR: An integer literal does not conform to the expected type V
class B<T>(val t: T) { class B<T>(val t: T) {
inner class Foo<U, V, W>(v: V, w: W) { inner class Foo<U, V, W>(v: V, w: W) {
} }
@@ -1,7 +1,6 @@
// "Create class 'Foo'" "true" // "Create class 'Foo'" "true"
class B<T>(val t: T) { class B<T>(val t: T) {
class Foo<U>(i: Int, u: U) { class Foo<U>(i: Int, u: U) {
} }
@@ -2,7 +2,6 @@
package p package p
class X { class X {
open class A(i: Int, s: String) { open class A(i: Int, s: String) {
} }
@@ -4,7 +4,6 @@ package p
import p.X.A import p.X.A
class X { class X {
annotation class A annotation class A
} }
@@ -4,7 +4,6 @@ package p
import p.X.A import p.X.A
class X { class X {
class A { class A {
} }
@@ -4,7 +4,6 @@ package p
import p.E.A import p.E.A
enum class E { enum class E {
A A
} }
@@ -4,7 +4,6 @@ package p
import p.X.A import p.X.A
class X { class X {
enum class A { enum class A {
} }
@@ -4,7 +4,6 @@ package p
import p.X.A import p.X.A
class X { class X {
object A { object A {
} }
@@ -4,7 +4,6 @@ package p
import p.X.A import p.X.A
class X { class X {
trait A { trait A {
} }
@@ -5,7 +5,6 @@ package p
fun foo() = A.B.C fun foo() = A.B.C
class A { class A {
class B { class B {
} }
@@ -4,7 +4,6 @@ package p
fun foo() = X.A fun foo() = X.A
enum class X { enum class X {
A A
} }
@@ -5,7 +5,6 @@ package p
fun foo() = X.A fun foo() = X.A
enum class X(n: Int) { enum class X(n: Int) {
A : X() A : X()
} }
@@ -4,7 +4,6 @@ package p
fun foo(): E = E.A fun foo(): E = E.A
enum class E { enum class E {
A A
} }
@@ -4,7 +4,6 @@ package p
fun foo() = X.A fun foo() = X.A
class X { class X {
object A { object A {
} }
@@ -2,7 +2,6 @@
package p package p
class T { class T {
class A { class A {
} }
@@ -0,0 +1,10 @@
// "Create class 'A'" "true"
package p
class T {
class A {
}
}
fun foo(): T.A = throw Throwable("")
@@ -0,0 +1,6 @@
// "Create class 'A'" "true"
package p
class T
fun foo(): T.<caret>A = throw Throwable("")
@@ -5,7 +5,6 @@
class A { class A {
val t: Int get() { val t: Int get() {
val foo: Int val foo: Int
return foo return foo
} }
} }
@@ -5,7 +5,6 @@
class A { class A {
val t: Int get() { val t: Int get() {
val foo: Int val foo: Int
return foo return foo
} }
} }
@@ -4,6 +4,5 @@
fun test(): Int { fun test(): Int {
val foo: Int val foo: Int
return foo return foo
} }
@@ -4,6 +4,5 @@
fun test(): Int { fun test(): Int {
val foo: Int val foo: Int
return foo return foo
} }
@@ -5,7 +5,6 @@
fun test(n: Int) { fun test(n: Int) {
val f: () -> Int = { val f: () -> Int = {
val foo: Int val foo: Int
foo foo
} }
} }
@@ -5,7 +5,6 @@
fun test(n: Int) { fun test(n: Int) {
val f: (Int, Int) -> Int = { (a, b) -> val f: (Int, Int) -> Int = { (a, b) ->
val foo: Int val foo: Int
foo foo
} }
} }
@@ -5,7 +5,6 @@
fun test(n: Int) { fun test(n: Int) {
val f: () -> Int = { val f: () -> Int = {
val foo: Int val foo: Int
foo foo
} }
} }
@@ -5,7 +5,6 @@
fun test(n: Int) { fun test(n: Int) {
val f: (Int, Int) -> Int = { (a, b) -> val f: (Int, Int) -> Int = { (a, b) ->
val foo: Int val foo: Int
foo foo
} }
} }
@@ -5,7 +5,6 @@ fun test(n: Int): Int {
return when (n) { return when (n) {
1 -> { 1 -> {
val foo: Int val foo: Int
foo foo
} }
else -> { else -> {
@@ -6,6 +6,5 @@ package foo
fun test(): Int { fun test(): Int {
val foo: Int val foo: Int
return foo return foo
} }
@@ -3,6 +3,5 @@
fun test(): Int? { fun test(): Int? {
val foo: Int? val foo: Int?
return foo return foo
} }
@@ -4,6 +4,5 @@
fun test() { fun test() {
val foo: Unit val foo: Unit
val u: Unit = foo val u: Unit = foo
} }
@@ -4,7 +4,6 @@
class A<T>(val n: T) { class A<T>(val n: T) {
val foo: Any val foo: Any
} }
fun test(): Int { fun test(): Int {
@@ -6,6 +6,5 @@ import kotlin.properties.ReadOnlyProperty
class A<T> { class A<T> {
val foo: ReadOnlyProperty<A<T>, A<Int>> val foo: ReadOnlyProperty<A<T>, A<Int>>
val x: A<Int> by foo val x: A<Int> by foo
} }
@@ -6,6 +6,5 @@ import kotlin.properties.ReadWriteProperty
class A<T> { class A<T> {
val foo: ReadWriteProperty<A<T>, A<Int>> val foo: ReadWriteProperty<A<T>, A<Int>>
var x: A<Int> by foo var x: A<Int> by foo
} }
@@ -3,7 +3,6 @@
class A<T>(val n: T) { class A<T>(val n: T) {
val foo: A<Int> val foo: A<Int>
} }
fun <U> A<U>.test(): A<Int> { fun <U> A<U>.test(): A<Int> {
@@ -3,7 +3,6 @@
class A<T>(val n: T) { class A<T>(val n: T) {
class object { class object {
val foo: Int val foo: Int
} }
@@ -3,7 +3,6 @@
object A { object A {
val foo: Int val foo: Int
} }
fun test() { fun test() {
@@ -3,7 +3,6 @@
class A<T>(val n: T) { class A<T>(val n: T) {
val foo: A<T> val foo: A<T>
} }
fun test() { fun test() {
@@ -3,7 +3,6 @@
class A<T>(val n: T) { class A<T>(val n: T) {
val foo: A<T> val foo: A<T>
} }
fun test<U>(u: U) { fun test<U>(u: U) {
@@ -3,7 +3,6 @@
class A<T>(val n: T) { class A<T>(val n: T) {
val foo: A<T>? val foo: A<T>?
} }
fun test() { fun test() {
@@ -3,7 +3,6 @@
class A<T>(val n: T) { class A<T>(val n: T) {
var foo: String var foo: String
} }
fun test() { fun test() {
@@ -1334,6 +1334,12 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("beforeClassUserTypeReceiverNoBody.kt")
public void testClassUserTypeReceiverNoBody() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/typeReference/beforeClassUserTypeReceiverNoBody.kt");
doTest(fileName);
}
@TestMetadata("beforeEnumEntryNotQualifierNoTypeArgs.kt") @TestMetadata("beforeEnumEntryNotQualifierNoTypeArgs.kt")
public void testEnumEntryNotQualifierNoTypeArgs() throws Exception { public void testEnumEntryNotQualifierNoTypeArgs() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/typeReference/beforeEnumEntryNotQualifierNoTypeArgs.kt"); String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/typeReference/beforeEnumEntryNotQualifierNoTypeArgs.kt");