If you're fine using intellij then you can probably use its scripting to do this. It has access to most of the same API as plugins and can access intellij features such as its refactor rename.
The (meager) docs on scripting: https://www.jetbrains.com/help/idea/ide-scripting-console.ht.... The plugin docs & intellij source code (especially the unit tests) are a better source of information.
And here's a script that will use intellij's refactoring to rename all classes recursively in a directory to add the prefix Renamed, correctly dealing with references & renaming files if necessary:
@file:Suppress("NAME_SHADOWING")
import com.intellij.notification.Notification
import com.intellij.notification.NotificationType
import com.intellij.notification.Notifications
import com.intellij.openapi.actionSystem.*
import com.intellij.openapi.keymap.KeymapManager
import com.intellij.openapi.command.WriteCommandAction
import com.intellij.psi.*
import com.intellij.psi.search.*
import com.intellij.refactoring.rename.RenameProcessor
import com.intellij.util.ThrowableConsumer
import java.io.PrintWriter
import java.io.StringWriter
import javax.swing.KeyStroke
// Usage: In IDEA: Tools -> IDE Scripting Console -> Kotlin
// Ctrl+A, Ctrl+Enter to run the script
// Select folder containing target classes, Ctrl+Shift+A to open action menu, search for Bulk refactor
//<editor-fold desc="Boilerplate">
val b = bindings as Map<*, *>
val IDE = b["IDE"] as com.intellij.ide.script.IDE
fun registerAction(
name: String,
keyBind: String? = null,
consumer: ThrowableConsumer<AnActionEvent, Throwable>
) {
registerAction(name, keyBind, object : AnAction() {
override fun actionPerformed(event: AnActionEvent) {
try {
consumer.consume(event);
} catch (t: Throwable) {
val sw = StringWriter()
t.printStackTrace(PrintWriter(sw))
log("Exception in action $name: $t\n\n\n$sw", NotificationType.ERROR)
throw t
}
}
});
}
fun registerAction(name: String, keyBind: String? = null, action: AnAction) {
action.templatePresentation.text = name;
action.templatePresentation.description = name;
KeymapManager.getInstance().activeKeymap.removeAllActionShortcuts(name);
ActionManager.getInstance().unregisterAction(name);
ActionManager.getInstance().registerAction(name, action);
if (keyBind != null) {
KeymapManager.getInstance().activeKeymap.addShortcut(
name,
KeyboardShortcut(KeyStroke.getKeyStroke(keyBind), null)
);
}
}
fun log(msg: String, notificationType: NotificationType = NotificationType.INFORMATION) {
log("Scripted Action", msg, notificationType)
}
fun log(
title: String,
msg: String,
notificationType: NotificationType = NotificationType.INFORMATION
) {
Notifications.Bus.notify(
Notification(
"scriptedAction",
title,
msg,
notificationType
)
)
}
//</editor-fold>
registerAction("Bulk refactor") lambda@{ event ->
val project = event.project ?: return@lambda;
val psiElement = event.getData(LangDataKeys.PSI_ELEMENT) ?: return@lambda
log("Bulk refactor for: $psiElement")
WriteCommandAction.writeCommandAction(event.project).withGlobalUndo().run<Throwable> {
psiElement.accept(object : PsiRecursiveElementWalkingVisitor() {
override fun visitElement(element: PsiElement) {
super.visitElement(element);
if (element !is PsiClass) {
return
}
if(element.name?.startsWith("Renamed") == false) {
log("Renaming $element")
// arg4 = isSearchInComments
// arg5 = isSearchTextOccurrences
val processor = object : RenameProcessor(project, element, "Renamed" + element.name, false, false) {
override fun isPreviewUsages(usages: Array<out UsageInfo>): Boolean {
return false
}
}
processor.run()
}
}
})
}
}
If your project is large this will temporarily freeze intellij since it doesn't properly run on the background thread but just give it a minute.
By the way, this should work on all languages that IntelliJ has a language plugin for, not just Java.
Edit: Modified script to override isPreviewUsages to prevent intellij from opening confirmation dialogs