Как применить стили для окрашивания синтаксиса в JEditorPane с комплектом редактора Java Netbeans (Javakit)?

Я хочу добиться окраски синтаксиса внутри JEditorPane, который уже оснащен комплектом редактора Java Netbeans (класс org.netbeans.modules.editor.java.JavaKit). Я заметил, что когда JEditorPane вместо этого имеет набор редактора RTF, ключевые слова окрашены (см. этот вопрос и ответ https://stackoverflow.com/a/67025502/8315843). Дело в том, что в Netbeans есть интересный компонент, называемый представлением различий, который я хочу использовать, и это представление различий использует две JEditorPanes, которые поставляются с этим Javakit, который, похоже, не предлагает окраску синтаксиса.

В приведенном ниже примере кода я показываю 2 возможных пути выполнения:

  • В наборе редактора RTF ключевое слово public окрашено в синий цвет.
  • Как видите, с Netbeans Javakit это не сработало.

Что касается ввода, я использую очень минималистский класс public class Hello {}, который находится в переменной с именем text.

    boolean useNetbeansJavakit = JOptionPane.showConfirmDialog(null, "Use Netbeans Javakit ?") == JOptionPane.YES_OPTION;

    JFrame f = new JFrame("JAVA Syntax Coloring");

    // Create the StyleContext, the document and the pane
    StyleContext sc = new StyleContext();
    final DefaultStyledDocument doc = new DefaultStyledDocument(sc);
    JEditorPane pane;
    if (useNetbeansJavakit) {
        pane = new JEditorPane();
        pane.setEditorKit(CloneableEditorSupport.getEditorKit("text/x-java"));
    } else {
        pane = new JEditorPane("text/rtf", "");
    }

    System.out.println(pane.getEditorKit());
    pane.setDocument(doc);

    // Create and add the constant width style
    final Style cwStyle = sc.addStyle("ConstantWidth", null);
    StyleConstants.setFontFamily(cwStyle, "monospaced");
    StyleConstants.setForeground(cwStyle, Color.blue);

    try {
        SwingUtilities.invokeAndWait(new Runnable() {
            public void run() {
                try {
                    // Add the text to the document
                    doc.insertString(0, text, null);

                    // Only color the word public for now in a hardcoded style
                    doc.setCharacterAttributes(0, 6, cwStyle, false);

                } catch (BadLocationException e) {
                }
            }
        });
    } catch (Exception e) {
        System.out.println("Exception when constructing document: " + e);
        System.exit(1);
    }
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.getContentPane().add(new JScrollPane(pane));
    f.setSize(400, 300);
    f.setVisible(true);
}
public static final String text = "public class Hello {}";

Вот файл сборки Gradle:

plugins {
    id 'java'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation files("${System.properties['java.home']}/../lib/tools.jar")
    implementation 'org.netbeans.modules:org-netbeans-modules-java-editor:RELEASE123'
    implementation 'org.netbeans.modules:org-netbeans-modules-editor-mimelookup-impl:RELEASE123'
    implementation 'org.apache.commons:commons-lang3:3.10'
}

Я работал над попыткой использовать подсветку синтаксиса на основе лексера, но я не знаю, как его использовать, я могу только догадываться, как его использовать, как показано ниже, но я просто не понимаю, как это просто не приходит изначально. В библиотеке netbeans, похоже, есть все, чтобы сделать некоторую окраску синтаксиса (см. класс org.netbeans.modules.editor.lib2.highlighting.SyntaxHighlighting, но я не могу найти объяснения, как его использовать). В приведенном ниже коде я разбиваю текст на идентификаторы и проверяю ключевые слова, а затем добавляю информацию о цвете для токенов ключевых слов.

    JFrame f = new JFrame("JAVA Syntax Coloring");
    // Create the StyleContext, the document and the pane
    StyleContext sc = new StyleContext();
    final DefaultStyledDocument doc = new DefaultStyledDocument(sc);
    JEditorPane pane = new JEditorPane();
    pane.setEditorKit(CloneableEditorSupport.getEditorKit("text/x-java"));
    System.out.println(pane.getEditorKit());
    pane.setDocument(doc);
    LexerBasedHighlightLayer lexerBasedHighlightLayer = (LexerBasedHighlightLayer) doc.getProperty(SemanticHighlighter.class);
    HashMap<Token, Coloring> colorings = new HashMap<>(lexerBasedHighlightLayer.getColorings());
    Language<JavaTokenId> java = JavaTokenId.language();
    TokenHierarchy<String> th = TokenHierarchy.create(text, java);
    TokenSequence<JavaTokenId> ts = th.tokenSequence(java);
    while (ts.moveNext()) {
        Token<JavaTokenId> token = ts.token();
        String tokenText = token.text().toString();
        if (tokenText.matches(
                "(abstract|continue|for|new|switch|assert|default|goto|package|synchronized|boolean|do|if|private|this|break|double|implements|protected|throw|byte|else|import|public|throws|case|enum|instanceof|return|transient|catch|extends|int|short|try|char|final|interface|static|void|class|finally|long|strictfp|volatile|const|float|native|super|while)")) {
            ColoringAttributes colorAttr = EnumUtils.getEnum(ColoringAttributes.class, tokenText.toUpperCase());
            if (colorAttr != null) {
                Coloring coloring = ColoringAttributes.add(ColoringAttributes.empty(), colorAttr);
                colorings.put(token, coloring);
            }
        }
    }
    lexerBasedHighlightLayer.setColorings(colorings, colorings.keySet(), null);

    try {
        SwingUtilities.invokeAndWait(new Runnable() {
            public void run() {
                try {
                    // Add the text to the document
                    doc.insertString(0, text, null);

                } catch (BadLocationException e) {
                }
            }
        });
    } catch (Exception e) {
        System.out.println("Exception when constructing document: " + e);
        System.exit(1);
    }
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.getContentPane().add(new JScrollPane(pane));
    f.setSize(400, 300);
    f.setVisible(true);
}
public static final String text = "public class Hello {}";

Информация о цвете настраивается в файлах XML в файле jar org-netbeans-modules-java-editor-RELEASE123.jar. Чтобы настроить собственные цвета, я скопировал каталог org/netbeans/modules/java/editor/resources из jar-файла в свою рабочую область по адресу src/main/resources/org/netbeans/modules/java/editor/resources.

Внутри файла src/main/resources/org/netbeans/modules/java/editor/resources/fontsColors.xml я добавил атрибут foreColor=blue в mod-public, чтобы попытаться выделить ключевое слово public в blue:

<fontcolor name="mod-public"  foreColor="blue" />

Я также изменил файл fontsColors-highlighting.xml в том же каталоге, и теперь он выглядит так:

<fontscolors>
    <fontcolor name="remove-surround-code-delete" foreColor="ffB4B4B4" bgColor="ffF5F5F5"/>
    <fontcolor name="remove-surround-code-remain" bgColor="ffCCFFCC"/>
    <fontcolor name="mod-public" foreColor="blue" />
</fontscolors>

Что мне здесь не хватает?


person Sybuser    schedule 09.04.2021    source источник


Ответы (1)


Ответ здесь:

https://bits.netbeans.org/dev/javadoc/org-netbeans-modules-lexer/org/netbeans/api/lexer/TokenHierarchy.html

Документ может определить основной язык, выполнив doc.putProperty(mimeType, mimeType) (будет выполняться поиск и использование языка, определенного для данного типа mime) или выполнив putProperty(Language.class, language). В противном случае возвращенная иерархия будет неактивной, а TokenHierarchy.tokenSequence() вернет значение null.

И этот TokenHierarchy.get(doc) вызывается в конструкторе SyntaxHighlighting:

/** Creates a new instance of SyntaxHighlighting */
public SyntaxHighlighting(Document document) {
    this.document = document;
    String mimeType = (String) document.getProperty("mimeType"); //NOI18N
    if (mimeType != null && mimeType.startsWith("test")) { //NOI18N
        this.mimeTypeForOptions = mimeType;
    } else {
        this.mimeTypeForOptions = null;
    }
    
    // Start listening on changes in global colorings since they may affect colorings for target language
    findFCSInfo("", null);

    hierarchy = TokenHierarchy.get(document);
    hierarchy.addTokenHierarchyListener(WeakListeners.create(TokenHierarchyListener.class, this, hierarchy));
}

Однако просто установка типа пантомимы у меня не сработала. Это то, что сработало для меня:

doc.putProperty(Language.class, JavaTokenId.language());

Также нет необходимости токенизировать, библиотека netbeans делает всю работу, а код сводится к следующему:

public void doSyntaxColoring(String fileName) {
    JFrame f = new JFrame("JAVA Syntax Coloring");

    DefaultStyledDocument doc = new DefaultStyledDocument();
    doc.putProperty(Language.class, JavaTokenId.language());
    JEditorPane pane = new JEditorPane();
    pane.setEditorKit(CloneableEditorSupport.getEditorKit(JavaKit.JAVA_MIME_TYPE));
    pane.setDocument(doc);

    try {
        SwingUtilities.invokeAndWait(new Runnable() {

            public void run() {
                try (FileReader fileReader = new FileReader(fileName)) {

                    String text = IOUtils.toString(fileReader);
                    // Add the text to the document
                    doc.insertString(0, text, null);

                } catch (BadLocationException | IOException e) {
                    e.printStackTrace();
                }
            }
        });
    } catch (Exception e) {
        System.out.println("Exception when constructing document: " + e);
        System.exit(1);
    }
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.getContentPane().add(new JScrollPane(pane));
    f.setSize(400, 300);
    f.setVisible(true);
}

И файл сборки Gradle:

plugins {
    id 'java'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation files("${System.properties['java.home']}/../lib/tools.jar")
    implementation 'org.netbeans.modules:org-netbeans-modules-java-editor:RELEASE123'
    implementation 'org.netbeans.modules:org-netbeans-modules-editor-mimelookup-impl:RELEASE123'
    implementation 'org.apache.commons:commons-lang3:3.10'
    implementation 'commons-io:commons-io:2.8.0'
}

Также не нужно ничего делать в файлах ресурсов (разве что для смены цвета)

person Sybuser    schedule 10.04.2021