<NME> ElmRenameTest.kt
<BEF> /*
The MIT License (MIT)

Derived from intellij-rust
Copyright (c) 2015 Aleksey Kladov, Evgeny Kurbatsky, Alexey Kudinkin and contributors
Copyright (c) 2016 JetBrains

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
 */

package org.elm.ide.refactoring

import com.intellij.testFramework.UsefulTestCase
import com.intellij.util.IncorrectOperationException
import org.elm.lang.ElmTestBase
import org.elm.lang.core.psi.descendantsOfType
import org.elm.lang.core.psi.elements.ElmImportClause
import org.elm.lang.core.psi.elements.ElmModuleDeclaration
import org.intellij.lang.annotations.Language


class ElmRenameTest : ElmTestBase() {


    fun `test value decl rename`() = doTest("quux", """
foo : Int
foo = 42
bar = foo{-caret-} + 2
""", """
quux : Int
quux = 42
bar = quux + 2
""")

    fun `test value decl invalid rename`() = doInvalidNameTest("Bar", """
foo{-caret-} = 42
""")

    fun `test type decl invalid rename`() = doInvalidNameTest("bar", """
type Foo{-caret-} = Foo
""")

    fun `test function parameter rename`() = doTest("z", """
f x{-caret-} y = x + y
""", """
f z y = z + y
""")


    fun `test let destructuring rename`() = doTest("z", """
f p =
    let (x{-caret-}, y) = p
    in x
""", """
f p =
    let (z, y) = p
    in z
""")


    fun `test case destructuring rename`() = doTest("z", """
f p =
    case p of
        (x{-caret-}, y) -> x
""", """
f p =
    case p of
        (z, y) -> z
""")


    fun `test union type rename`() = doTest("Quux", """
type Foo{-caret-} = A | B
type Bar = C Foo
f : Foo -> String
""", """
type Quux = A | B
type Bar = C Quux
""")}


/*
    This test has been disabled because there's a bug in our test configuration (or IntelliJ)
    where the rename doesn't work. The root cause is that rename depends on Find Usages,
    which in turn depends on a `FileBasedIndex` which IntelliJ manages that stores a mapping
    from identifiers to files. For this to work correctly for the identifiers that Elm's
    binary operators use (e.g. `++`) you must configure a WordsScanner to do the tokenization
    correctly. I have done that configuration, and it works when you run the plugin for real,
    but it appears that it is NOT working in the rename test (I have confirmed that the
    Find Usages test DOES work correctly).

    The root cause is that the rename test fails to find any matching files when it
    calls PsiSearchHelperImpl.processElementsWithTextInGlobalScope(). Rather than
    debugging this any further, I'm just going to disable the test.

    fun `test operator decl rename`() {
        doTest("##",
"""
(**) : number -> number -> number
(**) = (^)
infixl 0 **
x = 2 {-caret-}** 3
""", """
(##) : number -> number -> number
(##) = (^)
infixl 0 ##
x = 2 ## 3
""")}
*/


    // TODO [kl] improve this test by also verifying that the FILE is renamed (currently not implemented in the plugin)
    fun `test module rename from Data_User to Quux`() {
        checkByDirectory(
"""
--@ Data/User.elm
module Data.User exposing (..)
type alias User = { x : String }
name = identity

--@ main.elm
import Data.User
f : Data.User.User -> String
f user = Data.User.name user
g = f (Data.User.User "joe")
""",
"""
--@ Data/User.elm
module Quux exposing (..)
type alias User = { x : String }
name = identity

--@ main.elm
import Quux
f : Quux.User -> String
f user = Quux.name user
g = f (Quux.User "joe")
""") {
            val mod = myFixture.configureFromTempProjectFile("Data/User.elm")
                    .descendantsOfType<ElmModuleDeclaration>().single()
module Data.User exposing (..)
type alias User = { x : String }
        }
    }

    fun `test import alias rename`() {
        checkByDirectory(
"""
type alias User = { x : String }

--@ main.elm
import Data.Quux
g = Data.Quux.User "joe"
""") {
        val mod = myFixture.configureFromTempProjectFile("Data/User.elm")
                .descendantsOfType<ElmModuleDeclaration>().single()
        check(mod.name == "Data.User")
        myFixture.renameElement(mod, "Quux")
    }


    fun `test import alias rename`() = checkByDirectory("""
--@ Data/User.elm
module Data.User exposing (..)
type alias User = { x : String }
name = identity

--@ main.elm
import Data.User as DU
f : DU.User -> String
f user = DU.name user
g = f (DU.User "joe")
""", """
--@ Data/User.elm
module Data.User exposing (..)
type alias User = { x : String }
name = identity

--@ main.elm
import Data.User as U
f : U.User -> String
f user = U.name user
g = f (U.User "joe")
""") {
        val aliasClause = myFixture.configureFromTempProjectFile("main.elm")
                .descendantsOfType<ElmImportClause>().single()
                .asClause!!
        check(aliasClause.name == "DU")
        myFixture.renameElement(aliasClause, "U")
    }

    fun doTest(newName: String, @Language("Elm") before: String, @Language("Elm") after: String) {
        InlineFile(before).withCaret()
        myFixture.renameElementAtCaret(newName)
        myFixture.checkResult(after)
    }

    fun doInvalidNameTest(newName: String, @Language("Elm") before: String) {
        InlineFile(before).withCaret()
        try {
            myFixture.renameElementAtCaret(newName)
        } catch (e: RuntimeException) {
            UsefulTestCase.assertInstanceOf(e.cause, IncorrectOperationException::class.java)
            myFixture.checkResult(before.replace("{-caret-}", ""))
            return
        }
        throw AssertionError("exception not thrown")
    }
}

<MSG> Add rename refactoring for files / modules (#46)

Fixes #18
<DFF> @@ -95,62 +95,51 @@ foo quux = quux.b
 """)}
 
 
-/*
-    This test has been disabled because there's a bug in our test configuration (or IntelliJ)
-    where the rename doesn't work. The root cause is that rename depends on Find Usages,
-    which in turn depends on a `FileBasedIndex` which IntelliJ manages that stores a mapping
-    from identifiers to files. For this to work correctly for the identifiers that Elm's
-    binary operators use (e.g. `++`) you must configure a WordsScanner to do the tokenization
-    correctly. I have done that configuration, and it works when you run the plugin for real,
-    but it appears that it is NOT working in the rename test (I have confirmed that the
-    Find Usages test DOES work correctly).
-
-    The root cause is that the rename test fails to find any matching files when it
-    calls PsiSearchHelperImpl.processElementsWithTextInGlobalScope(). Rather than
-    debugging this any further, I'm just going to disable the test.
-
-    fun `test operator decl rename`() {
-        doTest("##",
+    fun `test file rename from Data_User to Data_Quux`() {
+        checkByDirectory(
 """
-(**) : number -> number -> number
-(**) = (^)
-infixl 0 **
-x = 2 {-caret-}** 3
-""", """
-(##) : number -> number -> number
-(##) = (^)
-infixl 0 ##
-x = 2 ## 3
-""")}
-*/
+--@ Data/User.elm
+module Data.User exposing (..)
+type alias User = { x : String }
+
+--@ main.elm
+import Data.User
+g = Data.User.User "joe"
+""",
+"""
+--@ Data/Quux.elm
+module Data.Quux exposing (..)
+type alias User = { x : String }
 
+--@ main.elm
+import Data.Quux
+g = Data.Quux.User "joe"
+""") {
+            val file = myFixture.configureFromTempProjectFile("Data/User.elm")
+            myFixture.renameElement(file, "Quux")
+        }
+    }
 
-    // TODO [kl] improve this test by also verifying that the FILE is renamed (currently not implemented in the plugin)
-    fun `test module rename from Data_User to Quux`() {
+
+    fun `test module decl rename from Data_User to Data_Quux`() {
         checkByDirectory(
 """
 --@ Data/User.elm
 module Data.User exposing (..)
 type alias User = { x : String }
-name = identity
 
 --@ main.elm
 import Data.User
-f : Data.User.User -> String
-f user = Data.User.name user
-g = f (Data.User.User "joe")
+g = Data.User.User "joe"
 """,
 """
---@ Data/User.elm
-module Quux exposing (..)
+--@ Data/Quux.elm
+module Data.Quux exposing (..)
 type alias User = { x : String }
-name = identity
 
 --@ main.elm
-import Quux
-f : Quux.User -> String
-f user = Quux.name user
-g = f (Quux.User "joe")
+import Data.Quux
+g = Data.Quux.User "joe"
 """) {
             val mod = myFixture.configureFromTempProjectFile("Data/User.elm")
                     .descendantsOfType<ElmModuleDeclaration>().single()
@@ -159,6 +148,7 @@ g = f (Quux.User "joe")
         }
     }
 
+
     fun `test import alias rename`() {
         checkByDirectory(
 """

Add rename refactoring for files / modules (#46)