В настоящее время я работаю над 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, чтобы понять это, я не могу утверждать, что мое решение является лучшим. Пожалуйста, дайте мне знать, если вы знаете лучший способ сделать то, что я описал в этом посте ?
Обратная связь всегда приветствуется.