Привет, Хабр
В статье хочу показать как настроить SoraEditor от Resomoe. А конкретно то цветовую схему, язык и простые сниппеты. Так как я не нашел статей по этой теме, а также оффициальная документация в некоторых местах противоречива и не доделана, я решил написать данную статью. Весь код в статье будет на Java.
Про tree sitter и другие прелести с кодом писать не буду, так как об этом хорошо написано в документации.
Подключение...
Для начало добавляем библиотеку в зависимости.
Gradle
dependencies {
implementation(platform("io.github.Rosemoe.sora-editor:bom:0.23.4"))
implementation("io.github.Rosemoe.sora-editor:editor")
implementation("io.github.Rosemoe.sora-editor:language-textmate")
}
Maven
<dependencies>
<dependency>
<groupId>io.github.Rosemoe.sora-editor</groupId>
<artifactId>bom</artifactId>
<version>0.23.4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.github.Rosemoe.sora-editor</groupId>
<artifactId>editor</artifactId>
</dependency>
<dependency>
<groupId>io.github.Rosemoe.sora-editor</groupId>
<artifactId>language-textmate</artifactId>
</dependency>
</dependencies>
Нам понадобятся только эти базовые зависимости, где находится сам редактор, и textmate для подсветки синтаксиса.
Далее, обезательно ставим версию JDK 17 или выше, в файле app/build.gradle.
Также, если мы хотим хранить сниппеты в JSON как и остальной кинфиг редактора, нам понадобится библиотека Gson. Она создна для сериализации и десериализации обьектов JSON.
gradle
dependencies {
implementation 'com.google.code.gson:gson:2.11.0'
}
Maven
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.11.0</version>
</dependency>
Можем синхронизировать проект.
Создание обьекта редактора
Я буду делать все через код, как создать редактор в разметке смотрите в оффициальной документации. Мой редактор будет как корневой элемент активности.
CodeEditor editor;
// onCreate(Bundle)
editor = new CodeEditor(this);
setContentView(editor);
Также, переопределяем метод onDestroy, и в нем вызываем метод release() для освобождения ресурсов.
@Override
public void onDestroy() {
super.onDestroy();
editor.release();
}
Подсветка синтакиса & язык
Если вам нужна абстрактная подсветка, сниппеты и тема для Java, то можете подключить модуль language-java. Далее использовать его так, если вам нужен другой язык, и больше гибкости, то можете читать статью дальше. Примео использования language-java
editor.setEditorLanguage(new JavaLanguage());
Создаем такое дерево файлов и папок в ассетах:
.
└─ textmate
├─ lua
│ ├─ lua.tmLanguage.json
│ └─ language-configuration.json
├─ langs.json
└─ dercula.json // схема
Язык и тему, можно можно выбрать любую, но она должна создана именно для textmate, в формате JSON. Вы также сами можете делать темы и языки в ручную.
Вы можете как вам угодно размещать папки и файлы, главное не забыть поменять пути на новые в файле langs.json.
Пример textmate/langs.json:
{
"languages": [
{
"grammar": "textmate/lua/lua.tmLanguage.json",
"name": "lua",
"scopeName": "source.lua",
"languageConfiguration": "textmate/lua/language-configuration.json"
}
]
}
А, ну и все необходимые ресурсы можете скачать сдесь: VSCode Extensions.
Цветовая схема редактора
Я сразу предоставлю код который будет настраивать схему, так как в документации об этом подробно написано. Предоставлю в качестве метода.
Функция инициализации цветовой схемы
public static void initSchemes(Context context) {
FileProviderRegistry.getInstance()
.addFileProvider(new AssetsFileResolver(context.getAssets()));
var themeRegistry = ThemeRegistry.getInstance();
String name = "darcula";
String themeAssetsPath = "textmate/" + name + ".json";
ThemeModel model =
new ThemeModel(
IThemeSource.fromInputStream(
FileProviderRegistry.getInstance()
.tryGetInputStream(themeAssetsPath),
themeAssetsPath,
null),
name);
try {
themeRegistry.loadTheme(model);
} catch (Exception err) {
err.printStackTrace();
}
GrammarRegistry.getInstance().loadGrammars("textmate/langs.json");
}
Далее нужно установить собственно тему и язык в редактор. Так как там очень мало кода, помещу в 1 фрагмент.
Установка темы и языка
// устанавливаем тему
ThemeRegistry
.getInstance()
.setTheme("Darcula");
// устанавливаем язык
String languageScopeName
= "source.lua";
try {
TextMateLanguage language =
new TextMateLanguage(languageScopeName);
editor.setEditorLanguage(language);
} catch (Exception e) {
e.printStackTrace();
}
Сниппеты
Этого в документации вобще нет, так что думаю будет трудновато самому об этом узнать. Сделаем простейшие сниппеты, а далее можно самому разбираться, так как у SoraEditor открытый исодный код на гитхабе.
Для начало создадим файл snippets.json, со следующим содержанием.
[
{
"prefix": "var",
"body": [
"local a = 0"
],
"description": "create variable",
"length": 3
}
// Другие сниппеты
]
На самом деле вы можете сделать любую свою структуру сниппета, так, как вам будет удобно. Я буду ориентировать именно на такую структуру.
Далее создадим наследник класса TextMateLanguage, я назову его TML, пример кода:
public class TML extends TextMateLanguage {
private String scope;
public TextMateLanguage(String scopeName) {
super(
GrammarRegistry.getInstance().findGrammar(scopeName),
GrammarRegistry.getInstance().findLanguageConfiguration(scopeName),
GrammarRegistry.getInstance(),
ThemeRegistry.getInstance(),
true);
scope = scopeName;
}
}
Что за что отвечает можете посмотреть в исходниках в модуле textmate.
Переопределяем метод requireAutoComplete:
public class TML extends TextMateLanguage {
// ... конструктор
@Override
public void requireAutoComplete(
ContentReference content,
CharPosition position,
CompletionPublisher publisher,
Bundle extraArguments) {
super.requireAutoComplete(content, position, publisher, extraArguments);
for (SnippetReader.Snippet s : SnippetReader.read(context, "lua")) {
publisher.addItem(
new SimpleCompletionItem(
s.prefix,
s.description,
s.length,
s.text)
);
}
CompletionHelper.computePrefix(content, position, MyCharacter::isJavaIdentifierPart);
}
}
SnippetReader - кастомный класс для чтения сниппетов из ассетов. Немного позже разберем этот класс.
Будем делать сниппеты с помощью обьекта pulisher и класса SimpleComplectionItem, у него довольно много конструкторов, мы будем использовать такой:
SimpleCompletionItem(
label,
desc,
prefixLength,
textToInsert)
Остальное можете просто скопировать.
Класс SnippetReader
Вобщем не вижу смысла тянуть, сразу дам код. Если что, для чтения ассетов использую свою библиотеку под названием Assets++.
public class SnippetReader {
public static Snippet[] read(Context c, String langName) {
String json =
Assets.from(c).asset(
String.format(
"textmate/%s/snippets.json",
langName))
.readAsset();
Type snippetsListType = new TypeToken<Snippet[]>() {}.getType();
Snippet[] snippets = new Gson().fromJson(json, snippetsListType);
for(Snippet s : snippets) {
s.text = String.join("\n", s.body);
}
return snippets;
}
public static class Snippet {
@SerializedName("prefix")
public String prefix;
@SerializedName("body")
public String body;
@SerializedName("description")
public String description;
@SerializedName("length")
public int length;
public String text;
}
}
Завершение
По итогу, мы настроили тему для редактора, язык а также простые сниппеты. Вопросы, поправки, предложения пишите ниже.