Подсветка синтаксиса JTextPane

В настоящее время я работаю над очень простой IDE, предназначенной для программирования C на Java. И под простым я подразумеваю, что у него есть незначительное угадывание кода (аналогично Eclipse), небольшое автозаполнение (опять же, вспомните Eclipse) и некоторая подсветка синтаксиса. Я почти все понял и набросал (т.е. работает, но не красиво или эффективно), за исключением того, что у меня есть некоторые проблемы с правильной подсветкой синтаксиса.

Что я подразумеваю под этим; в моем коде JFrame я вставил JTextPane, чтобы я мог использовать разные шрифты, полужирный, нежирный, курсив и добавлять разные цвета текста с относительной легкостью. У меня есть прослушиватель ключей, подключенный к этому JTextPane, и при каждом нажатии пробела он захватывает то, что вы только что написали, прогоняет его через дерево операторов «если», чтобы увидеть, является ли слово, которое вы написали, ключевым словом. Если вы это сделали, он попытается выделить (или нет) то, что вы только что написали. Однако в некоторых случаях мне нужно изменить цвет, прежде чем я нажму пробел (например, комментарий или оператор #define). Нет проблем, верно? Просто добавьте еще один оператор «if», чтобы определить, была ли нажата эта клавиша, и, если да, измените цвет шрифта. Ну, это то, что я пытался сделать, однако это не работает. Это действительно мешает мне видеть, как я использую ТОЧНО тот же код для изменения цветов, что и нажатие пробела (что работает просто отлично).

Извините, если это не имеет особого смысла, я буду рад объяснить немного больше, если это потребуется. Я также удалил как можно больше ненужного кода, чтобы попытаться сократить его.

Спасибо вам большое за ваше время!

~Цент

SSCCE:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;

import javax.swing.JFrame;
import javax.swing.JTextPane;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;

public class Main implements Runnable
{
private static final long serialVersionUID = 1L;
private static final int WIDTH = 800;
private static final int HEIGHT = 600;
private static final String NAME = "";
private JFrame frame;
private JTextPane textPane = new JTextPane();
private int keysPressed = -1; /* Keys pressed */
private ArrayList<Integer> keyCode = new ArrayList<Integer>(); /* List of our keyCodes */

public void run()
{
    frame = new JFrame(NAME);
    frame.setPreferredSize(new Dimension(WIDTH, HEIGHT));
    frame.setMinimumSize(new Dimension(WIDTH, HEIGHT));
    frame.setMaximumSize(new Dimension(WIDTH, HEIGHT));
    frame.setResizable(false);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
    frame.setLayout(new BorderLayout()); 
    frame.add(textPane, BorderLayout.CENTER);
    textPane.setPreferredSize(new Dimension(WIDTH, HEIGHT));
    textPane.setEditable(true);
    textPane.addKeyListener(new KeyListener(){
                @Override 
                public void keyPressed(KeyEvent e) 
                {
                     int previousPos; /* Previous position of the caret */
                     int length; /* Length of our grabbed string */

                     int currentPos; /* Current position of the caret */

                     String text; /* The entire text of the JTextPane */
                     String subText; /* Our sub-string-text we're using */
                     String subTextP; /* Our sub-string-text plus one character*/

                     boolean first = true; /* Be default, it's the first letter */

                     StyledDocument doc = textPane.getStyledDocument(); 
                     SimpleAttributeSet sas = new SimpleAttributeSet(); /* So we can set bold, and such */

                     keyCode.add(new Integer(e.getKeyCode())); /* The key pressed */
                     keysPressed++;
                     currentPos = textPane.getCaretPosition(); /* The current position of the caret */

                     text = textPane.getText(); /* Grabbing the text on the text pane */
                     previousPos = text.lastIndexOf(" ", currentPos); /* Getting the last position of a space */
                     if(previousPos <= 0) /* If the position if before or equal to 0 */ 
                     {
                         previousPos = 0; /* Then the position is 0 */
                     }
                     length = currentPos - previousPos; /* The length of the string we're messing with, is between the two positions */
                     subText = text.substring(previousPos, currentPos); /* Grabbing the string between our two positions */
                     if(first) /* If this is the first letter, or insert */
                     {
                         if(keyCode.contains(KeyEvent.VK_SHIFT))
                         {
                             first = true;
                             subTextP = text.substring(0, 0); /* Then we want to grab it, at 0, 0 */
                         }
                         else
                         {
                             subTextP = text.substring(0, 0); /* Then we want to grab it, at 0, 0 */
                             first = false; /* it's no longer the first */
                         }
                     }
                     else /* If it isn't */
                     {
                         subTextP = text.substring(previousPos + 1, currentPos); /* Then we want to grab the usual */
                     }
                     subText = subText.replaceAll("[\\n\\t\\r]", ""); /* Getting rid of all the tabs and newlines */
                     subTextP = subTextP.replaceAll("[\\n\\t\\r]", ""); /*Getting rid of all the tabs and new lines */

                     if(keyCode.contains(KeyEvent.VK_3)) 
                     {
                         if(keyCode.contains(KeyEvent.VK_SHIFT)) 
                         {
                             System.out.println("Number sign hit!");                                 
                             StyleConstants.setForeground(sas, Color.GREEN); /* Anything following a number sign will be green */
                             doc.setCharacterAttributes(previousPos, length, sas, false);  /* Turning it green */
                         }
                     }
                     if(keyCode.contains(KeyEvent.VK_SPACE)) /* If a space has been hit! */
                     {
                         /* This is were we'll do all text coloring and such */
                         if(subText.equals(" if") || subText.equals("if") || subTextP.equals("if")) /* All things to be bolded */
                         {
                             StyleConstants.setForeground(sas, Color.GRAY); /* All of these statements will be gray... */
                             StyleConstants.setBold(sas, true); /* ... and bold */
                             doc.setCharacterAttributes(previousPos, length, sas, false); /* Making them so! */
                             StyleConstants.setBold(sas, false); /* We don't want these attributes to remain... */
                             StyleConstants.setForeground(sas, Color.black); /* ... So we're removing them. */
                         }
                     }
                }

                public void keyReleased(KeyEvent e) 
                {
                    for(int i = keysPressed; i >= 0; i--) /* For loop to remove all keyPresses from our list */
                    {
                        keyCode.remove(i); /* Removing the specified keyPress */
                    }
                    keysPressed = -1; /* Because the first index is 0, and we want to add one to keysPressed, we need to start below 0 */
                }

                @Override
                public void keyTyped(KeyEvent arg0) {
                }
    });
    frame.pack(); 
    frame.setVisible(true);
}

public void start()
{
    new Thread(this).start();
}

public final static void main(String args[])
{
    new Main().start();
}

}

РЕДАКТИРОВАТЬ:

Чтобы воспроизвести проблему, запустите приведенный выше код. В текстовой панели введите слово «если» (без кавычек), а затем нажмите пробел. Теперь это слово должно быть выделено жирным шрифтом и серым цветом. Теперь попробуйте ввести «#» (без кавычек) после него (пробел между ними) и нажмите пробел или любую другую клавишу; Ничего не произошло. Тем не менее, система должна распечатать "Число попало!" как только вы вводите «#», это означает, что код на самом деле все еще доступен. Также обратите внимание, что я использую тот же код (помимо изменения цвета) для "#", что и для "if". Надеюсь, это поможет вам, ребята, немного лучше понять проблему.


person centip3de    schedule 03.12.2012    source источник


Ответы (1)


Во-первых, здесь неправильно использовать KeyListener.

Вместо этого используйте KeyBindings, DocumentListener или DocumentFilter (или даже замените Document собственным расширением, чтобы переопределить методы insertString() и remove()). Вы должны менять выделение не только после набора пробела, но и когда кто-то удаляет символы из середины ключевого слова.

Разместите сообщение SSCCE, чтобы показать реальную проблему и предоставить шаги для воспроизведения фактического поведения и описания желаемого поведения.

person StanislavL    schedule 04.12.2012
comment
Изменил опубликованный код на SSCCE и добавил шаги для воспроизведения проблемы. Спасибо за советы! - person centip3de; 05.12.2012
comment
Я набрал if # и увидел попадание знака Number! в консоли. В чем проблема? - person StanislavL; 07.12.2012
comment
Как вы можете прочитать в шагах, которые я предпринял для воссоздания проблемы, или в самом исходном посте, проблема в том, что знак числа и все, что после него, должно быть зеленым, а это не так. - person centip3de; 07.12.2012