/* * Syntax Highlighting Test using a JTextPane * * By: Frank Hale * Date: 12 November 2006 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * A full copy of this license is at: http://www.gnu.org/licenses/gpl.txt * */ import javax.swing.*; import javax.swing.event.*; import javax.swing.text.*; import javax.swing.plaf.ComponentUI; import java.awt.*; import java.util.*; import java.util.regex.*; public class Highlighter { class HighlightDocument extends DefaultStyledDocument { private Element rootElement; private HashMap keywords; private MutableAttributeSet style; private Color commentColor = Color.red; private Color quoteColor = Color.yellow; private Pattern singleLineCommentDelimter = Pattern.compile("//"); private Pattern multiLineCommentDelimiterStart = Pattern.compile("/\\*"); private Pattern multiLineCommentDelimiterEnd = Pattern.compile("\\*/"); private Pattern quoteDelimiter = Pattern.compile("\""); public HighlightDocument() { putProperty( DefaultEditorKit.EndOfLineStringProperty, "\n" ); rootElement = getDefaultRootElement(); keywords = new HashMap(); keywords.put( "abstract", Color.blue); keywords.put( "interface", Color.blue); keywords.put( "class", Color.blue); keywords.put( "extends", Color.blue); keywords.put( "implements", Color.blue); keywords.put( "package", new Color(0,200,0)); keywords.put( "import", new Color(0,200,0)); keywords.put( "private", new Color(0,200,0)); keywords.put( "protected", new Color(0,200,0)); keywords.put( "public", new Color(0,200,0)); keywords.put( "void", Color.orange); keywords.put( "boolean", Color.orange); keywords.put( "char", Color.orange); keywords.put( "byte", Color.orange); keywords.put( "float", Color.orange); keywords.put( "double", Color.orange); keywords.put( "long", Color.orange); keywords.put( "short", Color.orange); keywords.put( "int", Color.orange); keywords.put( "true", Color.red); keywords.put( "false", Color.red); keywords.put( "const", Color.red); keywords.put( "null", Color.red); keywords.put( "break", Color.blue); keywords.put( "case", Color.blue); keywords.put( "catch", Color.blue); keywords.put( "operator", Color.blue); keywords.put( "continue", Color.blue); keywords.put( "default", Color.blue); keywords.put( "do", Color.blue); keywords.put( "else", Color.blue); keywords.put( "final", Color.blue); keywords.put( "finally", Color.blue); keywords.put( "for", Color.blue); keywords.put( "if", Color.blue); keywords.put( "instanceof", Color.blue); keywords.put( "native", Color.blue); keywords.put( "new", Color.blue); keywords.put( "return", Color.blue); keywords.put( "static", Color.blue); keywords.put( "super", Color.blue); keywords.put( "switch", Color.blue); keywords.put( "synchronized", Color.blue); keywords.put( "this", Color.blue); keywords.put( "throw", Color.blue); keywords.put( "throws", Color.blue); keywords.put( "transient", Color.blue); keywords.put( "try", Color.blue); keywords.put( "volatile", Color.blue); keywords.put( "while", Color.blue); style = new SimpleAttributeSet(); } public void insertString(int offset, String str, AttributeSet attr) throws BadLocationException { super.insertString(offset, str, attr); processChangedLines(offset, str.length()); } public void remove(int offset, int length) throws BadLocationException { super.remove(offset, length); processChangedLines(offset, length); } public void processChangedLines(int offset, int length) throws BadLocationException { String text = getText(0, getLength()); highlightString(Color.black, 0, getLength(), true, false); Set keyw = keywords.keySet(); for (String keyword : keyw) { Color col = keywords.get(keyword); Pattern p = Pattern.compile("\\b" + keyword + "\\b"); Matcher m = p.matcher(text); while(m.find()) { highlightString(col, m.start(), keyword.length(), true, true); } } Matcher mlcStart = multiLineCommentDelimiterStart.matcher(text); Matcher mlcEnd = multiLineCommentDelimiterEnd.matcher(text); while(mlcStart.find()) { if(mlcEnd.find( mlcStart.end() )) highlightString(commentColor, mlcStart.start(), (mlcEnd.end()-mlcStart.start()), true, true); else highlightString(commentColor, mlcStart.start(), getLength(), true, true); } //Matcher quote = quoteDelimiter.matcher(text); //while(quote.find()) { // int start=quote.start(); // if(quote.find( start )) // highlightString(quoteColor, start, (quote.end()-start), true, true); //} // Single Line Comment Highligher (below) Matcher slc = singleLineCommentDelimter.matcher(text); while(slc.find()) { int line = rootElement.getElementIndex(slc.start()); int endOffset = rootElement.getElement(line).getEndOffset() - 1; highlightString(commentColor, slc.start(), (endOffset-slc.start()), true, true); } } public void highlightString(Color col, int begin, int length, boolean flag, boolean bold) { StyleConstants.setForeground(style, col); StyleConstants.setBold(style, bold); setCharacterAttributes(begin, length, style, flag); } public String getLineString(String content, int line) { Element lineElement = rootElement.getElement( line ); return content.substring(lineElement.getStartOffset(), lineElement.getEndOffset() - 1); } } class highlightKit extends StyledEditorKit { public Document createDefaultDocument() { return new HighlightDocument(); } } // Because Swing's JTextPane is retarded by default and doesn't allow you to just flip a switch and turn off the FUCKING line-wrapping // // This overridden method was coded by somebody much smarter than I, somebody probably with an EGO the size of SUN MICROSYSTEMS. // // At SUN our motto is "Over-engineer everything so that nobody understands it..." class NonWrappingTextPane extends JTextPane { // The method below is coutesy of Core Swing Advanced Programming by Kim Topley // // Override getScrollableTracksViewportWidth // to preserve the full width of the text public boolean getScrollableTracksViewportWidth() { Component parent = getParent(); ComponentUI ui = getUI(); return parent != null ? (ui.getPreferredSize(this).width <= parent.getSize().width) : true; } } public Highlighter(String args[]) { JFrame frame = new JFrame("Syntax Highlighting Test"); NonWrappingTextPane textPane = new NonWrappingTextPane(); textPane.setFont(new Font("Monospaced", Font.PLAIN, 20)); JScrollPane scroll = new JScrollPane(textPane); textPane.setEditorKit(new highlightKit()); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.getContentPane().add(scroll); frame.setSize(640,480); frame.setVisible(true); } public static void main(String args[]) { new Highlighter(args); } }