Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Разработка плагина IntelliJ IDEA. Часть 4

Anna | 5.06.2014 | нет комментариев

IntelliJ IDEA это не только Java IDE, но и сильная платформа для построения инструментов разработки для всякого языка. Множество функций IDEA состоят из 2-х частей: само­стоятельной от языка и специфической для определенного языка программирования. Следственно помощь особенностей какого-либо языка не требует специальных усилий – нужно реализовать лишь специфическую часть, а самостоятельная от языка предоставляется платформой. В дополнение, IDEA предоставляет сильный фреймворк, тот, что разрешает реализовывать личные функции, нужные при разработке инструментария.

Регистрация типа файла

Первым шагом при разработке плагина специфического языка является регистрация связанного с ним файлового типа. Обыкновенно IDEA определяет тип файла в соответствии с его именем (растяжением).
Тип файла специфического языка – это класс, унаследованный от LanguageFileType, тот, что передает экземпляр класса Language в родительский конструктор. Для регистрации типа файла нужно предоставить реализацию интерфейса FileTypeFactory, зарегистрированную в точке растяжения com.intellij.fileTypeFactory:

<extensions defaultExtensionNs="com.intellij">
  …
  <fileTypeFactory implementation="com.intellij.lang.properties.PropertiesFileTypeFactory"/>
  …
</extensions>


Пример реализации класса LanguageFileType в Properties плагине.

Для проверки корректности регистрации, следует удостовериться, что иконка, отображаемая рядом с файлами, имеющими растяжение, ассоциированное с пользовательским типом файла, совпадает с иконкой, определенной в способе getIcon().

Реализация лексического анализатора

Лексер (лексический анализатор) определяет, как содержимое файла будет разбито на последовательность токенов. Лексер служит фундаментом для примерно всех функций языковых плагинов, начиная с подсветки синтаксиса и заканчивая функциями обзора кода. API лексера определен в интерфейсе Lexer.
IDEA вызывает лексер в 3 основных контекстах и плагин должен предоставить реализацию для всякого из них:

  • подсветка синтаксиса – лексер должен возвращаться реализацией интерфейса SyntaxHighlighterFactory, зарегистрированной в точке растяжения com.intellij.lang.syntaxHighlighterFactory;
  • построение абстрактного синтаксического дерева – лексер должен быть возвращен из способа ParserDefinition.createLexer(), реализация интерфейса ParserDefinition должна быть определена в точке растяжения com.intellij.lang.parserDefinition;
  • построение индекса слов, содержащихся в файле – если применяется реализация сканера, основанная на пользовательском лексере, то он передается как довод конструктора класса DefaultWordsScanner.

Лексер, применяемый для подсветки синтаксиса может вызываться инкрементально, для обработки лишь изменившейся части файла. В остальных случаях лексеры вызываются для обработки файла целиком, либо завершенной языковой конструкции, встроенной в файл иного типа.

Вызываемый инкрементально лексер должен возвращать свое нынешнее состояние, т.е. контекст, соответствующий всякой позиции в файле. Значимым требованием для подсветки синтаксиса является представление состояния обыкновенным числом (возвращаемым из способа Lexer.getState()). Это состояние будет передано в способ Lexer.start() совместно со стартовым смещением фрагмента для обработки, когда нужно продолжить лексический разбор в середине файла. Лексеры в других контекстах могут легко возвращать 0.

Для облегчения создания лексического анализатора специфического языка программирования дозволено воспользоваться генератором лексеров, таким как JFlex. IDEA включает классы адаптеров (FlexLexer и FlexAdapter), которые приспосабливают JFlex-лексеры к лексическому API IDEA. В начальных кодах Intellij IDEA Community Edition содержится модифицированная версия JFlex 1.4.1 и файл с заготовкой лексера, которые могут быть использованы при разработке лексеров, совместимых с FlexAdapter. Модифицированная версия JFlex предоставляет новую опцию командной строки --charat, которая изначений. Ключи применяются в операциях извлечения данных из индекса, скажем, в индексе слов ключом является строка, содержащая слово. Значением ключа в индексе может быть любая информация, скажем, в индексе слов это может быть маска, определяющая контекст, в котором находится слово (код, литерал, комментарий). В простейшем случае (когда требуется лишь определить в каком файле находятся данные) значение имеет тип void и не сохраняется в индексе.

Позже индексации файла, индекс возвращает таблицу ключей и ассоциированных с ними значений. При обращении к индексу по определенному ключу, он возвращает список файлов и данные, связанные с этим ключом.

Реализация файлового индекса

Для лучшего понимания приведем пример достаточно примитивный реализации файлового индекса, а именноиндекс границ форм, используемый в UI Designer.

Всякая специфическая реализация индекса наследует класс FileBasedIndexExtension и должна быть зарегистрирована в точке растяжения <fileBasedIndex>. Реализация содержит следующие основные части:

  • getIndexer() — возвращает класс индексатора, тот, что отвечает за построение актуального множества пар ключ/значение, основанного на содержимом файла;
  • getKeyDescriptor() — возвращает дескриптор, отвечающий за сопоставление ключей и сохранение их в бинарный формат. Допустимо самая распространенная реализация — это EnumeratorStringDescriptor (теснее адаптирован для результативного хранения идентификаторов);
  • getValueExternalizer() — возвращает сериализатор значений, берет на себя функцию сохранения значений в сериализованный бинарный формат;
  • getInputFilter() — разрешает ограничить индексацию определенным комплектом файлов;
  • getVersion() — версия реализации индекса. Индекс механически перестроится, если нынешняя версия отличается от той с поддержкой которой был построен предшествующий индекс.

Если не требуется объединять значения с файлами, упростить реализацию дозволено унаследовав класс ScalarIndexExtension.

Подметим, что данные, возвращаемые DataIndexer.map() обязаны зависеть только от значений, переданных в способ, а не от каких-либо внешних файлов. В отвратном случае, индекс не будет правильно обновлен и когда внешнее состояние изменится, индекс будет указывать на устаревшие данные.

Доступ к файловому индексу

Доступ к индексам производится посредством класса FileBasedIndex, которые поддерживает такие операции как:

  • getAllKeys() и processAllKeys() — разрешает получить список всех ключей, в файлах, относящихся к нынешнему плану. Подметим, что возвращенные данные гарантировано содержат все ключи, обнаруженные в актуальном содержимом плана, но также могут содержать и теснее несуществующие.
  • getValues() — разрешает получить все значения, ассоциированные с заданным ключом (но не файлы, в которых они обнаружены);
  • getContainingFiles() — разрешает получить список файлов, содержащих переданный ключ;
  • processValues() — разрешает ступенчато обработать все файлы, в которых находится определенный ключ и в то же время получить доступ к ассоциированным значениям.

 

Типовые индексы

Некоторые типовые файловые индексы, содержащиеся в IDEA Зачастую бывают пригодны разработчикам плагинов. Скажем, вышеупомянутый индекс слов. Не предоставляет прямой доступ плагинам, но применяется открытым вспомогательным классом PsiSearchHelper. 2-й пригодный индекс — это FilenameIndex, разрешающий стремительно находить файлы по их именам. И наконец FileTypeIndex — разрешает искать файлы по типу.

Деревья стабов

Как было подмечено выше, дерево стабов это подмножество PSI-дерева, сохраненное в суперкомпактном бинарном формате.
PSI-дерево отдельного файла может быть основано как на AST (т.е. сделано в процессе синтаксического разбора текста), так и на stub-дереве (десериализовано с диска), переключение между обоими вариантами прозрачно.
Stub-дерево содержит только подмножество узлов (обыкновенно только те узлы, которые применяются для разрешения внешних ссылок на определения, находящиеся в данном файле). Попытка доступа к любому узлу вне дерева либо выполнение операции, для которой не хватает данных, предоставленных деревом стабов, приводит к перестроению PSI-дерева на основе AST.

Всякий стаб в дереве является оr/>
Индексы стабов реализуются как классы расширяющие AbstractStubIndex. В больше распространенном случае, когда тип ключа — строка, дозволено применять больше специфичный класс StringStubIndexExtension. Индексы обязаны быть зарегистрированы в точке растяжения <stubIndex>.

Для того Дабы сберечь данные в индексе, следует реализовать способ IStubElementType.indexStub() (пример), тот, что принимает IndexSink как параметр и сберегает идентификатор и ключ в всяком индексе, где должен быть сохранен элемент.

Для приобретения доступа к данным индекса, применяются следующие способы:

  • AbstractStubIndex.getAllKeys() — возвращает список всех ключей в заданном индексе для определенного плана (скажем, список всех имен классов в плане);
  • AbstractStubIndex.get() — возвращает коллекцию PSI-элементов соответствующих заданному ключу (скажем, классы с заданным коротким именем) в определенном скопе.

В дальнейшей части: подсветка синтаксиса, ссылочная система и связанные с ней функции.
Все статьи цикла: 1234.
Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB