キーワードを強調表示する方法2

a-san2007-09-08

編集をしているとき、キーワードが入力されると自動的に強調表示されるモノを作ってみます。ドキュメントが変わったときには、DocumentListenerで通知してもらいます。

import java.awt.Color;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;

public class JTextPaneTest2 extends JFrame {
    JTextPane pane;
    JTextPaneTest2() {
        super("JTextPaneTest2");
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setSize(640, 480);
        pane = new JTextPane();
        StyledDocument doc = (StyledDocument) pane.getDocument();
        doc.addDocumentListener(new DocListener());
        getContentPane().add(pane);
        pane.setText("検索文字は「文字」と「String」です。\n"+
                "大文字、小文字は区別しています。\n"+
                "なので、Stringは引っかかっても、stringやSTRINGには引っかかりません。\n"+
                "また単語ごとの検索もダメなので、SuperStringにも引っかかってしまいます。\n"+
                "ソースファイルの強調表示をやろうとすると、\"文字列中の単語\"や\n"+
                "/** コメント中の文字 */ なども考慮しなければならないので厄介。\n"+
                "秀丸のように、正規表現を駆使したり、対象の言語にあった字句解析を行う必要があります。\n"+
                "♪ねぇムーミン。こっち向いて。恥ずかしがら〜な〜いで〜。文字文字しな〜い〜で〜。♪");
        invokeSetHighlight();
        setVisible(true);
    }
    void setHighlight() {
        String text = pane.getText();
        StyledDocument doc = (StyledDocument) pane.getDocument();
        // すべての属性を消す
        SimpleAttributeSet plane = new SimpleAttributeSet();
        doc.setCharacterAttributes(0, text.length(), plane, true);
        // キーワードを強調表示
        SimpleAttributeSet attr = new SimpleAttributeSet();
        //StyleConstants.setBold(attr, true);              // 太字
        //StyleConstants.setForeground(attr, Color.BLUE);  // 文字の色
        StyleConstants.setBackground(attr, Color.YELLOW);  // 背景の色
        // キーワードが見つかったら、属性をつける。
        String[] keywords = {"文字", "String"};
        for (int i=0; i<keywords.length; i++) {
            String keyword = keywords[i];
            int pos = text.indexOf(keyword);
            while (pos != -1) {
                doc.setCharacterAttributes(pos, keyword.length(), attr, true);
                pos = text.indexOf(keyword, pos+keyword.length());
            }
        }
    }
    void invokeSetHighlight() {
        // ドキュメント変更中に呼ぶと競合するので、処理後に呼ぶようにする。
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                setHighlight();
            }
        });
    }
    
    class DocListener implements DocumentListener {
        public void insertUpdate(DocumentEvent e) {
            System.out.println("insertUpdate(e)"+e);
            invokeSetHighlight();
        }
        public void removeUpdate(DocumentEvent e) {
            System.out.println("removeUpdate(e)"+e);
            invokeSetHighlight();
        }
        public void changedUpdate(DocumentEvent e) {
            System.out.println("changedUpdate(e)"+e);
            // 属性が変わったときは、何もしなくてよい。
        }
    }

    public static void main(String[] args) {
        new JTextPaneTest2();
    }
}

入力するたびにキーワードに色がつくので、最近のエディタっぽいです。
ここではキーワードの検索に、StringクラスのindexOf(String)を使っています。
一般の文章だと、これでOKなのですが、プログラムのソースだと、これだけでは不十分です。
大文字、小文字は区別されるので、SQLなどでは使えません。また、プログラムの場合、文字列やコメントは別の色で表示したいですし、その中にキーワードがあってもそれは無視すべきです。単語ごとに色分けすべきなので、Word のようになるべきではありません。
そうなってくると、秀丸のように正規表現を駆使したり、その言語ごとに特化した字句解析を行う必要があります。話がややこしくなるので、ここではそれ以降の議論は行いません。


上記のプログラムでは、ドキュメントが変更されるたびに、全ドキュメントのキーワードを検索しなおしているので、ドキュメントが長かったり、キー入力が速い人だと、効率が悪くてレスポンスに影響が出るかもしれません。その場合は、「入力中はチェックせずに、入力が終わった1秒後にチェックする」とか、「入力した行(の前後)のみをチェックする」などの方法を取るべきかもしれません。