Всем привет!
Меня зовут Михаил. Я Android-разработчик в продуктовой команде Циан.
При разработке новой фичи приходится тратить время на создание однотипных файлов, в которых мы будем писать логику. Такие файлы очень похожи друг на друга, зачастую различие только в названии. Будь то создание presentation-слоя c Fragment -> ViewModel -> ComposeWidget (с типовыми State) или data-слоя c интерфейсами, Repository и их реализацией или создание компонента для DI.
Когда сталкиваешься с рутиной, есть желание от неё избавиться. Очень хочется, чтобы всё рутинное создавалось за пару кликов, и ты после этого мог создавать новую функциональность.
В моём представлении в этом могут помочь шаблоны. Вбиваю нужные параметры, и магическим образом всё готово, и всё на своих местах.
Размышляя, в каком виде я хочу видеть этот процесс, я пришёл к таким критериям:
Максимальная комфортность и простота генерации файлов, чтобы, буквально за пару кликов.
Шаблон должен легко модифицироваться всей командой разработки.
Шаблоны не надо искать или скачивать. При свежем develop – свежие шаблоны.
Это точно не скрипт, так как сложно поддерживать его актуальность.
Все должно работать в IDE и легко устанавливаться.
В далёком 2020-м году, Google удалили из AS поддержку кастомных Freemarker-ных шаблонов, альтернатив которым особо не было. А то, что имелось, не подходило моим критериям.
Совместив все критерии, я пришел к выводу, что мне нужен мой собственный plugin для IDE, который полностью удовлетворит все мои потребности.
И я его, знаете-ли, сделал..
Group File Template (GFT) — это плагин для создания классов из подготовленных заранее шаблонов. Настолько гибко, что подойдёт не только Android-разработчику.
Где его найти? В Marketplace IDEA или Android Studio. Просто вбиваем в поиск «GFT». Он там такой один.
Как работает плагин?
Шаблоны для использования должны находиться в корне вашего проекта в каталоге templates.
Файлы с расширением .tm — это абстрактный шаблон класса. На их основе и будут генерироваться классы. За генерацию по этим шаблонам отвечает «main.json».
В шаблон можно добавлять свои параметры, которые будут заполняться из IDE.
Main поддерживает 2 типа параметров:
поле для заполнения, в которое можно ввести любой текст;
выпадающий список, в котором надо будет выбрать один из вариантов.
Плагин создаёт файлы в той папке, из которой было вызвано создание.
Поближе рассмотрим main.json. Он имеет следующую структуру:
{
"name": "TestTemplate", // Название Шаблона
"description": "Example for documentation", // Описание шаблона
"path": "/templates/TestTemplate", // Путь к файлу main шаблона (Заполняется автоматически при создании шаблона из IDE)
"param": [ // Список параметров, которые надо будет ввести при создании файлов из шаблона
"name"
],
"selectParam": [ // Список параметров, которые надо будет выбрать при создании файлов из шаблона
{
"paramName": "ide", // Имя параметра
"paramValue": [ // Список вариантов выбора
"Android Studio",
"Pycharm",
"intellij"
]
},
],
"addFile": [ // Список файлов абстрактных шаблонов класса
{
"name": "{name}[-C]ViewModel.kt", // Имя, с каким создастся файл
"path": "{ide}/viewModels/{name}[-c]", // Путь, по которому будет создан файл
"fileTemplatePath": "example.tm" // Путь до абстрактного шаблона класса в каталоге с шаблоном
}
]
}
Опции
Можно заметить, что рядом с параметрами присутствуют какие-то загадочные символы в квадратных скобках. Например, «[-C]». Всё дело в том, что параметры имеют опции, которые могут поменять значение, вставленное из параметра:
[-s] - snake_case
[-C] - CamelCase
[-c] - camelCase
[-p] - point.between.words
[-sl] - slash/between/word
[-d] - dash-between-word
Например, написав «{"NewFeature"}[-s]», мы получим «new_feature».
Стандартные параметры
Помимо параметров, которые мы указываем в «main.json» файле, существуют стандартные параметры, которые можно применять в шаблоне.
{package} — пакет места, в которое вы делаете создание файла (если создать от каталога внутри модуля /ui/screen, то package будет выглядеть как ru.package.module.ui.screen).
{time} = Текущее время в 12-ти часовом формате -
15:56«03:56».{day} = Текущий день «04».
{month} = Текущий месяц «06».
{year} = Текущий год «2023».
Имена файлов и каталогов
Теперь давайте рассмотрим несколько особенностей и фишек плагина при работе с именами файлов и каталогов.
Если имя файла пустое, плагин создаст только цепочку каталогов, не создавая файл.
{
"name": "",
"path": "path/{name}[-sl]/catalog",
"fileTemplatePath": ""
}
Для добавления файлов в Android-ресурсы пропишите «res/» в параметре «path». Плагин сам найдёт папку res в вашем модуле
{
"name": "{name}[-s]_main.xml",
"path": "res/layout",
"fileTemplatePath": "layout.tm"
}
Чтобы добавить Unit-тест напишите «test/» в параметр «path». Плагин сам найдёт папку test в вашем модуле.
{
"name": "{name}[-C]ViewModelTest.kt",
"path": "test/viewModels/{name}[-c]",
"fileTemplatePath": "example.tm"
}
Бывают случаи, что нам надо создать файлы не только там, где мы вызвали, но и в корне проекта.
Чтобы создать файл в корне проекта, напишите «~/» в «path».
{
"name": "{name}[-C].txt",
"path": "~",
"fileTemplatePath": "example.tm"
}
Актуальные примеры шаблона можно посмотреть тут.
Что писать в .tm файле?
В tm файле находится ваш шаблон. Размечаем его параметрами, которые создали в main.json -> (param или selectParam)
К параметрам применяем нужные нам опции и собственно всё.
package {package}.analytics
import ru.cian.presentation.analytics.base.sender.AnalyticsSender
import {package}.analytics.builder.{name}[-C]Builder
import javax.inject.Inject
class {name}[-C]Analytics
@Inject constructor(
private val analyticsSender: AnalyticsSender
) {
fun {name}[-c]() {
val builder = {name}[-C]Builder()
analyticsSender.sendEvent(builder)
}
}
Если вы добавили .tm файл в шаблон не через IDE, а руками, то не забудьте прописать его в «main.json» как показано выше.
Как создать шаблон?
Вводим название вашего будущего шаблона в диалоговом окне.
Чтобы создать новый шаблон, нажмите на пункт меню «Tools» -> «Create New Template».
Готово. В вашей папке с проектом в подпапке «templates» появился новый шаблон.
Как добавить файл в свой шаблон?
Чтобы добавить файл в шаблон, щёлкните по этому файлу правой кнопкой мыши и выберите «Add File in Template».
Плагин попросит вас выбрать, в какой шаблон вы хотите добавить файл (если у вас их несколько).
Переименовываем файл так, как он будет назван в шаблоне.
Готово, файл появился в выбранном шаблоне и виден в main.json.
"addFile": [
{
"name": "build.gradle.kts",
"path": "",
"fileTemplatePath": "buildTemp.tm"
},
]
Как использовать созданный шаблон?
Кликаем правой кнопкой мыши по папке, в которой хотим создать файлы, и выбираем из списка нужный нам шаблон..
Заполняем параметры в диалоговом окне и нажимаем «OK».
Готово, создалась целая структура из шаблона!
Итого!
Что плагин умеет делать, мы разобрались, но чего он пока не умеет делать?
Пока что, не умеет вставлять куски кода в уже существующие файлы (фича на будущее), и чтобы подключить новые модули к app я создаю helpFile.md файл, в котором я прописываю путь до файла, и что в него надо вставить.
В main.json
{
"name": "helpFile.md",
"path": "~",
"fileTemplatePath": "helpFile.tm"
}
и сам шаблон файла
[Добавить в](app/build.gradle.kts)
implementation(project(":kmm_{name}[-s]_api"))
runtimeOnly(project(":kmm_{name}[-s]_impl"))
runtimeOnly(project(":{name}[-s]_compose"))
[Добавить в](settings.gradle.kts)
includeKmmFeatureModule("{name}[-s]")
Плагин помогает мне создавать большие модули, например, для использования kmm нам надо создать 3 модуля, в которых 8 каталогов.
-
api
androidMain
commonMain
-
impl
androidMain
androidTest
commonMain
commonTest
iosMain
-
ui module
androidMain
Он значительно сокращает время от старта фичи в работу до начала написания бизнес логики.
Использую даже для мелких задач, например, нам нужно создать из endPoint дерево каталогов.
Из - /getData/point/v2/map-search
Преобразует в дерево каталогов
Если у вас остались вопросы, пишите мне, всем с радостью отвечу!
Надеюсь, мой плагин облегчит вашу рутинную разработку!
Ccылка на Github
Ссылка на Marketplace
OldFisher
Аки Танос - это безвозвратно разнести в пыль половину проекта.
Louco11 Автор
Нуууу, тут аналогия что также быстро это сделать =))