Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Вакханалия со ссылками и языками в IntelliJ IDEA

Вакханалия со ссылками и языками в IntelliJ IDEA

Yuriy Artamonov

February 29, 2020
Tweet

More Decks by Yuriy Artamonov

Other Decks in Programming

Transcript

  1. Вакханалия со
    ссылками и
    языками
    в IntelliJ IDEA
    Юрий Артамонов

    View Slide

  2. Автор ты кто
    Юрий Артамонов @jreznot
    ■ Разрабатывал фреймворки и
    библиотеки для Java на
    протяжении 9 лет
    ■ Придумываю новые
    возможности IDE для ваших
    любимых JVM фреймворков в
    IntelliJ IDEA
    ■ Беспристрастный член ПК
    конференций Joker/JPoint
    2

    View Slide

  3. План действий
    1. Что такое Language Injections
    2. Как использовать в проекте
    3. Пишем плагин
    4. Как предоставить свои Injected References
    5. А ещё: auto completion, find usages и rename refactoring
    3

    View Slide

  4. 4

    View Slide

  5. String Obsession!
    Строки используются в дизайне API повсеместно:
    1. Ключи в словарях
    2. Файловые и сетевые пути
    3. Идентификаторы ресурсов, URL
    4. Логины пользователей
    5. Заголовки HTTP
    6. …
    Но к чему это приводит ?!!
    5

    View Slide

  6. Причины String Obsession
    ■ Сложно расширить компилятор / интерпретатор
    любимого языка
    ■ Мета-данные и аннотации требуют строк
    ■ Внешние системы принимают строковые запросы
    (пример: SQL)
    ■ В конфигурационных файлах нельзя писать код
    ■ Так проще и все привыкли!
    6

    View Slide

  7. Лечение есть: Language and Reference Injection!
    7

    View Slide

  8. Решение 1. Language Injections
    Точки расширения для плагинов:
    ■ LanguageInjector
    ■ ReferenceInjector
    8

    View Slide

  9. Где полезно в реальной жизни
    ■ Regexp
    ■ SQL
    ■ JSON
    ■ XML & HTML
    ■ XPath
    ■ JavaScript
    ■ ...
    9

    View Slide

  10. Способы применения
    1. Вручную
    2. Комментарий // language=SQL
    3. Аннотация @Language(“SQL”)
    compile 'org.jetbrains:annotations:18.0.0'
    4. Плагин для IDE!
    10

    View Slide

  11. Новый JEP ?
    [ide-support-dev] @Language annotation for JDK 13 text blocks.
    > For example, imagine this annotation on top of a text block:
    > @Language("text/html")
    Проблемы:
    1. Аннотации привязаны к языку
    2. Формат MIME Type / Language ID
    3. Нужны ли ещё параметры: prefix / suffix ?
    4. Должны ли редакторы выводить язык литерала из использований?
    5. А что там с @Nullable / @NotNull в JDK ?
    11

    View Slide

  12. Конфигурация
    12

    View Slide

  13. Как поделиться настройками
    Способы:
    1. Move to Project Scope + Commit .idea to VCS
    2. Export to XML
    3. Плагин для IDE !
    13

    View Slide

  14. Как создать плагин
    14
    1. Проверить/включить
    Plugin DevKit
    2. Создать Gradle проект с
    библиотекой
    IntelliJ Platform Plugin
    3. Объявить и реализовать
    нужные точки
    расширения

    View Slide

  15. Ключевые внутренности IDE
    ■ Компоненты и сервисы
    ■ Виртуальная файловая система (VFS)
    ■ Поддержка языков (PSI)
    ■ Редактор кода
    ■ Инспекции
    ■ Индексы
    ■ Фоновые процессы
    ■ UI библиотека
    ■ Точки расширения
    IntelliJ Platform
    IDEA CE (Java,
    Groovy, Kotlin)
    PHP Support Ruby Support
    IDEA Ultimate PHP Storm RubyMine
    15

    View Slide

  16. Расширенная конфигурация IntelliLang
    Точка расширения: org.intellij.intelliLang.injectionConfig
    org.intellij.intelliLang



    Полезные атрибуты:
    ■ prefix / suffix
    ■ value-pattern
    ■ ignore-pattern
    16

    View Slide

  17. Инжектим в XML
    Plugin DevKit:

    IntelliJ IDEA pattern
    xmlTag().withLocalName("place")
    .withParent(xmlTag().withLocalName("injection")
    .withParent(xmlTag().withLocalName("component")))
    ]]>


    17

    View Slide

  18. JSON Schema
    Файлы: JSON / YAML
    Атрибут: x-intellij-language-injection:
    /schema.json
    "messageRegex": {
    "type": "string",
    "description": "Regex pattern to check test error message",
    "x-intellij-language-injection": "RegExp"
    }
    18

    View Slide

  19. Решение 2. Ссылки в PSI дереве
    ■ Позволяют установить связь
    между PsiElement и его
    использованиями:
    usage ➜ declaration
    ■ Можно добавить cсылки без
    необходимости расширять
    язык!
    См.
    PsiReferenceContributor
    PsiLanguageInjectionHost
    19

    View Slide

  20. Internal Mode
    Специальный режим работы IDE, в котором доступны опции для
    разработчиков.
    ■ Internal Actions
    ■ View PSI / Dump UAST
    ■ Dumb Mode Switch
    ■ …
    Автоматически доступен в режиме отладки плагина.
    Включить в VM Options своей IDE: -Didea.is.internal=true
    20

    View Slide

  21. PSI Viewer
    Tools - View PSI Structure of Current File
    Позволяет изучить структуру PSI
    дерева в файле:
    ■ PSI Tree
    ■ References
    ■ Language injections
    21

    View Slide

  22. Повторное использование ссылок
    Все PsiLanguageInjectionHost
    элементы потенциально могут
    содержать ссылки и код на
    другом языке:
    ■ Строковые литералы
    ■ Комментарии
    ■ XML теги и атрибуты
    ■ JSON, .properties, ...
    Точка расширения:
    ReferenceInjector
    22

    View Slide

  23. Ссылки на веб-ресурсы
    class IssueCodeInjector : ReferenceInjector() {
    override fun getReferences(element: PsiElement,
    context: ProcessingContext,
    range: TextRange): Array
    По шагам:
    1. Получаем значение элемента по range
    2. Определяем целевой URL
    3. Переиспользуем WebReference из
    platform-impl
    23

    View Slide

  24. Валидация значений
    Способы валидации:
    1. Инспекции, см.
    LocalInspectionTool
    2. Переопределение способа
    поиска деклараций
    А какие значения правильные?
    a. Спецификация на значение
    b. Белый список
    c. Здравый смысл
    24
    (?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*
    |"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x
    09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+
    [a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[
    0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]
    *[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x0
    1-\x09\x0b\x0c\x0e-\x7f])+)\])

    View Slide

  25. Предоставляем Quick Fix
    Ссылка должна реализовать LocalQuickFixProvider:
    override fun getQuickFixes(): Array? {
    return arrayOf(AddCustomHttpHeaderFix(myElement))
    }
    и обработчик:
    class AddCustomHttpHeaderFix(myElement: PsiElement) : LocalQuickFixOnPsiElement(myElement) {
    override fun invoke(project: Project,
    file: PsiFile,
    startElement: PsiElement,
    endElement: PsiElement) {
    25

    View Slide

  26. Find Usages по ссылкам
    Упрощённый алгоритм поиска элементов:
    1. Выяснить область поиска ссылок и деклараций:
    getUseScope() и getResolveScope()
    2. Найти все файлы, в которых встречается текст по words index
    3. Для каждого найденного места определить есть ли там ссылки,
    resolve() и isEquivalentTo()
    См. ReferencesSearch.search(SearchParameters)
    26

    View Slide

  27. Rename Refactoring
    1. Вызов диалога в
    RenamePsiElementProcessor.createRenameDialog
    2. Поиск использований
    3. Изменение литералов в
    PsiReferenceBase.handleElementRename(newElementName)
    27

    View Slide

  28. Автодополнение по коду проекта
    Варианты для ссылок:
    1. Захардкодить!
    2. Сконфигурировать
    3. Спросить у пользователя
    4. Поискать при помощи магии !
    28

    View Slide

  29. Как что-то нужное найти
    Модуль java-indexing-api предоставляет методы поиска по стандартным
    индексам:
    ■ ReferencesSearch
    ■ MethodReferenceSearch
    ■ AnnotatedElementsSearch
    ■ …
    Query MethodReferencesSearch.search(psiMethod, scope, strict)
    Query AnnotatedElementsSearch.searchPsiMethods(annotationClass, scope)
    29

    View Slide

  30. Стандартные индексы IDE
    Индексация:
    ■ WordIndex - слова
    ■ IdIndex - идентификаторы
    ■ FileNameIndex - имена файлов
    ■ FileTypeIndex - файлы по типам
    ■ JavaAnnotationIndex - аннотации
    ■ JavaShortClassNameIndex -
    имена классов
    ■ ...
    30

    View Slide

  31. Сложные применения
    1. Литерал может содержать
    множество ссылок,
    пример - URL Path *
    2. Реактивное автодополнение при
    помощи CompletionContributor,
    пример - Message Queue *:
    * новые возможности IntelliJ IDEA Ultimate 2020.1
    31

    View Slide

  32. Куда копать дальше
    ■ Исходный код IntelliJ IDEA CE:
    github.com/JetBrains/intellij-community
    ■ Документация Plugin DevKit:
    jetbrains.org/intellij/sdk/docs/basics.html
    ■ Slack: jetbrains-platform NEW!
    https://plugins.jetbrains.com/slack
    ■ Forum: IntelliJ IDEA Open API and Plugin Development
    32

    View Slide

  33. Вопросы ?
    Код: github.com/jreznot/intellij-uber-injector
    Twitter:
    @Yuriy_Artamonov

    View Slide