TGViewer
A bit deeper A bit deeper @abitdeeper · 199 subscribers
Post #33 1.81K
Немного про платформу Intellij

Сегодня у меня на работе была небольшая задача - нужно было исправить названия строковых ресурсов в нашем проекте - избавиться от точек.
Было: profile.expand
Стало: profile_expand
Зачем это было нужно: стандартная инспекция Find Usages в Android Studio не умеет понимать что использование R.string.profile_expand в коде это на самом деле использование ресурса profile.expand. Она работает только с названиями ресурсов у которых есть только андерскоры: profile_expand.
Соответственно, задача стояла в том чтобы перелопатить полторы тысячи ресурсов, делать это руками конечно же никакого желания и времени нету.
Также нужно не забывать что нужно не только переименовать ресурсы, но и их использование в xml по всему проекту.
На ум пришла давняя идея - сделать плагин для Intellij который позволит компилировать произвольный код для выполнения каких-то единичных операций прямо в IDE.
И тут я с удивлением обнаружил что такой инструмент давно существует в IDE!
Называется он IDE Scripting Console и получить к нему доступ можно через Cmd + Shift + A и поиск по строке IDE Scripting Console. Можете увидеть пример на скриншоте в первом комментарии к этому посту.

Внутри этого скрипта можно пользоваться всеми апи платформы Intellij которые обычно доступны при разработке плагинов для IDE. Также из скрипта можно получить значение последнего выполненного выражения, его будет видно в логе запуска скрипта.

Таким образом, задача упростилась донельзя:
1. Берём текущий открытый проект:
val project = ProjectManager.getInstance().openProjects.first()

2. Ищем все XML файлы в проекте (по пути отфильтровываем сгенерированные):
val baseDir = VfsUtil.findFile(Paths.get(project.basePath), true)!!  
val psiManager = PsiManager.getInstance(project)
val xmlFiles =
VfsUtil
.collectChildrenRecursively(baseDir)
.asSequence()
.filter { it.extension == "xml" }
.filterNot { it.path.contains("build") }
.mapNotNull { psiManager.findFile(it) as? XmlFile }

3. Переименовываем строковые ресурсы:
xmlFiles  
.filter { it.virtualFile.path.contains("/res/values/") }
.mapNotNull { xmlFile -> xmlFile.rootTag?.findSubTags("string")?.asSequence() }
.flatten()
.mapNotNull { stringTag -> stringTag.getAttribute("name") }
.filter { nameAttribute -> nameAttribute.value?.contains('.') == true }
.forEach {
val oldName = it.value!!
val newName = oldName.replace('.', '_')
WriteCommandAction.runWriteCommandAction(project) {
it.setValue(newName)
}
}

4. Ищем все XML атрибуты в проекте в которых есть значение начинающееся с @string и в котором присутствуют точки:
fun XmlFile.getAllAttributesRecursively() =  
generateSequence(
seed = listOf(element = rootTag!!),
nextFunction = {
val subTags = it.flatMap { it.subTags.toList() }
subTags.ifEmpty { null }
},
)
.flatMap { it.asSequence() }
.flatMap { it.attributes.asSequence() }


val allXmlAttributes = xmlFiles.flatMap { xmlFile ->
xmlFile
.getAllAttributesRecursively()
.filter { it.value!!.startsWith("@string/") }
.filter { it.value!!.contains('.') }
}

5. Заменяем точки в этих атрибутах на underscore:
allXmlAttributes.forEach {  
WriteCommandAction.runWriteCommandAction(project) {
it.setValue(
it.value!!.replace('.', '_')
)
}
}

Вот таким нехитрым способом я смог безопасно выполнить нетривиальный рефакторинг в проекте.
Пользуйтесь!
  • 🔥 20
  • 👍 5
  • 👏 3
More from @abitdeeper
  1. Sep 8, 2026Нагенерил себе за пару промптов приложеньку чтоб удобно тренировки домашние вести. Очень р…
  2. Sep 4, 2026«Агент, сделай проект. А мы пока на митап» 🤖 Возвращаемся с nullptr.talks[3] — в этот раз…
  3. Sep 4, 2026Готовим наш четвёртый митап, приходите если в Алмате 👀 Запись тоже будет
  4. Jul 23, 2026🚀 Ищем спикеров на nullptr.talks[3] 📅 Когда: сентябрь 2026 📍 Где: to be announced ⏰ Зая…
  5. Jul 15, 2026Нужна помощь, совсем не айтишного характера. Ищу потенциального донора крови для моего кот…
  6. Feb 13, 2026Когда-нибудь я найду в себе силы возобновить посты в этот канал, а пока что, читатели из А…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →