java - JTextPane 语法突出显示

标签 java swing jtextpane syntaxhighlighter

我目前正在开发一个非常简单的 IDE,用于 Java 中的 C 编程。简单地说,我的意思是它有一些小的代码猜测(类似于 Eclipse)、一些小的自动完成功能(再次想想 Eclipse)和一些语法突出显示。我几乎已经弄清楚并粗略地解决了所有问题(即工作,但不漂亮或高效),除了我在正确的语法突出显示方面遇到了一些麻烦。

我的意思是;在我的代码 JFrame 中,我放入了 JTextPane,这样我就可以使用不同的字体、粗体、非粗体、斜体,并相对轻松地添加不同的文本颜色。我有一个连接到此 JTextPane 的按键监听器,在每次按下空格键时,它都会抓取您刚刚编写的内容,通过“if”语句树运行它以查看您编写的单词是否是关键字。如果您这样做了,它会尝试突出显示(或不突出显示)您刚刚写的内容。然而,在某些情况下,我需要它在点击空格键之前改变颜色(例如注释或#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();
}

}

编辑:

要重现该问题,请运行上面提供的代码。在文本 Pane 中,输入单词“if”(不带引号),然后按空格键。这个词现在应该是粗体,并且是灰色的。现在,尝试在其后面输入“#”(无引号)(中间有空格)并按空格或任何其他键;什么都没发生。然而,系统应该打印出“Number sign hit!”一旦你输入“#”,这意味着代码实际上仍然可以访问。另请注意,我对“#”使用与“if”相同的代码(除了更改颜色之外)。希望能帮助大家更好地理解这个问题。

最佳答案

首先,这里使用 KeyListener 是不正确的。

使用KeyBindingsDocumentListenerDocumentFilter代替(或者甚至用自己的扩展替换文档以覆盖insertString()remove() 方法)。您不仅应该在输入空格后更改突出显示,还应该在有人从关键字中间删除字符时更改突出显示。

请发帖SSCCE显示真正的问题并提供重现实际行为和描述所需行为的步骤。

关于java - JTextPane 语法突出显示,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13690269/

相关文章:

java - LinkedLists 是一种不直观的解决方案,因为大多数时候我不需要知道集合中元素的物理位置吗?

java - 该文件对象是否保持打开状态?

java - 通过java中的参数集找到最合适的对象

java - 互连两个 ActionListener

java - 我想以某种方式添加延迟,单击按钮更新标签, hibernate 几秒钟,然后更新其他标签

java - JScrollPane 隐藏 JTextPane

java - 将 Collection 和 Iterator 接口(interface)实现为内部类

java - 如何自动调整JSplitPane?

java - 当我将文本从 java servlet 加载到 JTextPane 时,为什么会丢失换行符?

Java JTextPane 向 JScrollPane 添加空白区域