Привет, Хабр

В статье хочу показать как настроить 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;
  }
}

Завершение

По итогу, мы настроили тему для редактора, язык а также простые сниппеты. Вопросы, поправки, предложения пишите ниже.

Комментарии (0)