Search code examples
kotlinintellij-plugin

How to change a Kotlin code reference from a function to a field in an intellij plugin?


I'm writing an intellij plugin where I'm refactoring a class, changing its getters (e.g., fun name(): String) to fields (e.g., val name: String).

However, I don't know how best to update the corresponding PsiReference instances. A Kotlin caller needs to change from myObj.name() to myObj.name without the parenthesis.

Currently, I'm doing the following:

ReferencesSearch.search(function).findAll().forEach {
    val nextSibling = it.element.nextSibling
    if ((nextSibling as? KtValueArgumentList)?.arguments?.isEmpty() == true) {
        nextSibling.delete()
    }
}

The above works somewhat. That is, the conversion happens correctly. However, the IDE still thinks it is calling a function. It underlines an error in the converted myObj.name with the following message:

Expression 'name' of type String cannot be invoked as a function. The function 'invoke()' is not found

Manually rewriting name in the editor forces intellij to refresh the reference and error disappears.

What should I do instead to prevent this from happening?


Solution

  • You are getting that error message because you are not modifying the reference to the old method. Intellij still thinks your call myObj.name is trying to access some method called name() that doesn't exist anymore.

    Also, the search result is going to point to the leaf node in the AST that uses your method. In this case name() has a parent PSI object that holds a reference to name and to (). That's why calling element.nextSibling gives you the () which you can then call delete() on. But this doesn't change the parent's reference.

    I'm not sure what is the best way to do what you want but you could try to replace the parent's reference directly. Try:

    element.parent.replace(<reference to your the data class field>)