В настоящее время я работаю над Xtext- редактором для языка BUILD от Google, как описано в нашем блоге Google Engineering . Файлы BUILD не содержат расширения, они просто называются «BUILD». Это соглашение об именах делает работу с Xtext довольно сложной. Мне нужно было изменить поведение Xtext по умолчанию: вместо того, чтобы связывать редакторы Xtext с расширениями файлов, мне нужно было связывать их с именами файлов. В этом посте я описываю, как мне это удалось.
В этом посте я буду использовать совершенно новый проект Xtext. Для простоты я буду использовать расширение файла по умолчанию (mydsl) и грамматику. Вы можете получить исходный код этого поста из Xtext Samples , нового проекта с открытым исходным кодом, где я планирую хранить код, который я использую в постах, связанных с Xtext. Весь код выпущен в соответствии с Eclipse Public License (EPL) 1.0 .
Следующие шаги предполагают, что мы собираемся связать наш редактор Xtext с именем файла «MyDsl».
- Измените XtextEditor плагина, чтобы он понимал имена файлов, а не расширения файлов.
- Создайте ContentHandler, который описывает содержимое файлов с именем «MyDsl». В рамках этого шага нам нужно создать новый тип контента для файлов «MyDsl».
- Создайте IResourceServiceProvider, который предоставляет сервисы (например, валидацию, описание контента, кодировку) для файлов с именем «MyDsl».
- Зарегистрируйте классы, созданные на предыдущих шагах, чтобы сделать их видимыми для EMF и Xtext.
1. Измените XtextEditor плагина, чтобы он понимал имена файлов
Это самый простой из всех шагов:
- Откройте файл plugin.xml в проекте «ui».
- Выберите вкладку «plugin.xml» в нижней части редактора, чтобы напрямую редактировать код XML.
- В элементе редактора замените extensions = «mydsl» на filenames = «MyDsl»
2. Создайте ContentHandler, который описывает содержимое файлов с именем «MyDsl»
Вот упрощенная версия нашего ContentHandler. Вы можете найти полный файл здесь .
public class MyDslContentHandler extends ContentHandlerImpl { public static final String MY_DSL_FILE_CONTENT_TYPE = "com.google.eclipse.MyDsl"; @Override public boolean canHandle(URI uri) { return isMyDslFile(uri); } @Override public Map<String, Object> contentDescription(URI uri, InputStream inputStream, Map<?, ?> options, Map context) throws IOException { Map<String, Object> description = super.contentDescription(uri, inputStream, options, context); if (canHandle(uri)) { description.put(VALIDITY_PROPERTY, VALID); description.put(CONTENT_TYPE_PROPERTY, MY_DSL_FILE_CONTENT_TYPE); } return description; } }
Строка 2: мы определяем новый тип контента для файлов «MyDsl». Мы держим постоянную публику, так как мы будем использовать это позже.
Строка 4: мы указываем, что имена файлов с именем «MyDsl» могут обрабатываться ContentHandler. Мы вызываем служебный метод isMyDslFile (URI) из URI класса .
Строка 8: мы объявляем содержимое файлов «MyDsl» как допустимое и сопоставляем его тип с типом, определенным в строке 2.
3. Создайте IResourceServiceProvider, который предоставляет сервисы для файлов с именем «MyDsl»
Вот упрощенная версия нашего IResourceServiceProvider. Вы можете найти полный файл здесь .
public class MyDslResourceServiceProvider extends DefaultResourceServiceProvider { @Override public boolean canHandle(URI uri) { return isMyDslFile(uri); } }
Мы вызываем служебный метод isMyDslFile (URI) из URI класса, чтобы указать, что этот IResourceServiceProvider может обрабатывать файлы с именами «MyDsl».
4. Зарегистрируйте классы, созданные на предыдущих шагах, чтобы сделать их видимыми для EMF и Xtext.
Теперь пришло время соединить все вместе. Сначала мы связываем IResourceServiceProvider с нашим MyDslResourceServiceProvider в модуле времени выполнения плагина :
public Class<? extends IResourceServiceProvider> bindIResourceServiceProvider() { return MyDslResourceServiceProvider.class; }
Теперь мы говорим Xtext и EMF в конструкторе MyDslUiModule , чтобы они понимали файлы с именем «MyDsl».
public class MyDslUiModule extends AbstractMyDslUiModule { public MyDslUiModule(AbstractUIPlugin plugin) { super(plugin); configureXtextToWorkWithFileNames(new InjectorProvider()); } }
Мы передаем InjectorProvider, который получает Injector плагина только в тот момент, когда это необходимо. На данный момент мы не можем передать Инжектор напрямую, так как он не был полностью создан при вызове configureXtextToWorkWithFileNames.
Метод configureXtextToWorkWithFileNames (InjectorProvider) определен в XtextSetup :
final class XtextSetup { static void configureXtextToWorkWithFileNames(InjectorProvider injectorProvider) { register(new MyDslContentHandler()); register(new ResourceFactoryDescriptor(injectorProvider)); register(new ResourceServiceProvider(injectorProvider)); } private static void register(ContentHandler h) { ContentHandler.Registry registry = ContentHandler.Registry.INSTANCE; registry.put(HIGH_PRIORITY, h); } private static void register(Resource.Factory.Descriptor d) { Resource.Factory.Registry registry = Resource.Factory.Registry.INSTANCE; registry.getContentTypeToFactoryMap().put(MY_DSL_FILE_CONTENT_TYPE, d); } private static void register(Provider<IResourceServiceProvider> p) { IResourceServiceProvider.Registry registry = IResourceServiceProvider.Registry.INSTANCE; registry.getContentTypeToFactoryMap().put(MY_DSL_FILE_CONTENT_TYPE, p); } }
Строка 4: мы регистрируем ContentHandler, который мы создали на шаге 2.
Строка 5: мы связываем IResourceFactory в модуле Guice плагина с типом контента, определенным на шаге 2.
Строка 6: мы связываем IResourceServiceProvider, который мы создали на шаге 3 (и позже зарегистрирован в модуле Guice плагина) с типом контента, определенным на шаге 2.
Тестирование редактора
Чтобы запустить редактор, щелкните правой кнопкой мыши любой из проектов и выберите «Запуск от имени»> «Приложение Eclipse» из контекстного меню.
Вот скриншот редактора, открывающего файл с именем «MyDsl»
Резюме
В этом посте я показал, как заставить Xtext понимать имена файлов в четыре простых шага. Вы можете найти код для этого поста в проекте Xtext Samples . Вы можете оформить заказ , просмотреть его или загрузить .
Несмотря на то, что я потратил много времени на чтение кода Xtext и EMF, чтобы понять это, я не могу утверждать, что мое решение является лучшим. Пожалуйста, дайте мне знать, если вы знаете лучший способ сделать то, что я описал в этом посте ?
Обратная связь всегда приветствуется.