Changes:
1. refactoring the structure
2. fill missing documentation
3. input parameter checking
diff --git a/src/prettify/CombinePrefixPattern.java b/src/prettify/CombinePrefixPattern.java
deleted file mode 100644
index 98f95e6..0000000
--- a/src/prettify/CombinePrefixPattern.java
+++ /dev/null
@@ -1,297 +0,0 @@
-// Copyright (C) 2006 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-package prettify;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * This is similar to the combinePrefixPattern.js in JavaScript Prettify.
- * 
- * All comments are adapted from the JavaScript Prettify.
- * 
- * @author Chan Wai Shing <cws1989@gmail.com>
- */
-public class CombinePrefixPattern {
-
-    protected int capturedGroupIndex = 0;
-    protected boolean needToFoldCase = false;
-
-    public CombinePrefixPattern() {
-    }
-
-    /**
-     * Given a group of {@link java.util.regex.Pattern}s, returns a {@code RegExp} that globally
-     * matches the union of the sets of strings matched by the input RegExp.
-     * Since it matches globally, if the input strings have a start-of-input
-     * anchor (/^.../), it is ignored for the purposes of unioning.
-     * @param regexs non multiline, non-global regexs.
-     * @return Pattern a global regex.
-     */
-    public Pattern combinePrefixPattern(List<Pattern> regexs) throws Exception {
-        boolean ignoreCase = false;
-
-        for (int i = 0, n = regexs.size(); i < n; ++i) {
-            Pattern regex = regexs.get(i);
-            if ((regex.flags() & Pattern.CASE_INSENSITIVE) != 0) {
-                ignoreCase = true;
-            } else if (Util.test(Pattern.compile("[a-z]", Pattern.CASE_INSENSITIVE), regex.pattern().replaceAll("\\\\[Uu][0-9A-Fa-f]{4}|\\\\[Xx][0-9A-Fa-f]{2}|\\\\[^UuXx]", ""))) {
-                needToFoldCase = true;
-                ignoreCase = false;
-                break;
-            }
-        }
-
-        List<String> rewritten = new ArrayList<String>();
-        for (int i = 0, n = regexs.size(); i < n; ++i) {
-            Pattern regex = regexs.get(i);
-            if ((regex.flags() & Pattern.MULTILINE) != 0) {
-                throw new Exception(regex.pattern());
-            }
-            rewritten.add("(?:" + allowAnywhereFoldCaseAndRenumberGroups(regex) + ")");
-        }
-
-        return ignoreCase ? Pattern.compile(Util.join(rewritten, "|"), Pattern.CASE_INSENSITIVE) : Pattern.compile(Util.join(rewritten, "|"));
-    }
-    protected static final Map<Character, Integer> escapeCharToCodeUnit = new HashMap<Character, Integer>();
-
-    static {
-        escapeCharToCodeUnit.put('b', 8);
-        escapeCharToCodeUnit.put('t', 9);
-        escapeCharToCodeUnit.put('n', 0xa);
-        escapeCharToCodeUnit.put('v', 0xb);
-        escapeCharToCodeUnit.put('f', 0xc);
-        escapeCharToCodeUnit.put('r', 0xf);
-    }
-
-    protected static int decodeEscape(String charsetPart) {
-        Integer cc0 = charsetPart.codePointAt(0);
-        if (cc0 != 92 /* \\ */) {
-            return cc0;
-        }
-        char c1 = charsetPart.charAt(1);
-        cc0 = escapeCharToCodeUnit.get(c1);
-        if (cc0 != null) {
-            return cc0;
-        } else if ('0' <= c1 && c1 <= '7') {
-            return Integer.parseInt(charsetPart.substring(1), 8);
-        } else if (c1 == 'u' || c1 == 'x') {
-            return Integer.parseInt(charsetPart.substring(2), 16);
-        } else {
-            return charsetPart.codePointAt(1);
-        }
-    }
-
-    protected static String encodeEscape(int charCode) {
-        if (charCode < 0x20) {
-            return (charCode < 0x10 ? "\\x0" : "\\x") + Integer.toString(charCode, 16);
-        }
-
-        String ch = new String(Character.toChars(charCode));
-        return (charCode == '\\' || charCode == '-' || charCode == ']' || charCode == '^')
-                ? "\\" + ch : ch;
-    }
-
-    protected static String caseFoldCharset(String charSet) {
-        String[] charsetParts = Util.match(Pattern.compile("\\\\u[0-9A-Fa-f]{4}"
-                + "|\\\\x[0-9A-Fa-f]{2}"
-                + "|\\\\[0-3][0-7]{0,2}"
-                + "|\\\\[0-7]{1,2}"
-                + "|\\\\[\\s\\S]"
-                + "|-"
-                + "|[^-\\\\]"), charSet.substring(1, charSet.length() - 1), true);
-        List<List<Integer>> ranges = new ArrayList<List<Integer>>();
-        boolean inverse = charsetParts[0] != null && charsetParts[0].equals("^");
-
-        List<String> out = new ArrayList<String>(Arrays.asList(new String[]{"["}));
-        if (inverse) {
-            out.add("^");
-        }
-
-        for (int i = inverse ? 1 : 0, n = charsetParts.length; i < n; ++i) {
-            String p = charsetParts[i];
-            if (Util.test(Pattern.compile("\\\\[bdsw]", Pattern.CASE_INSENSITIVE), p)) {  // Don't muck with named groups.
-                out.add(p);
-            } else {
-                int start = decodeEscape(p);
-                int end;
-                if (i + 2 < n && "-".equals(charsetParts[i + 1])) {
-                    end = decodeEscape(charsetParts[i + 2]);
-                    i += 2;
-                } else {
-                    end = start;
-                }
-                ranges.add(Arrays.asList(new Integer[]{start, end}));
-                // If the range might intersect letters, then expand it.
-                // This case handling is too simplistic.
-                // It does not deal with non-latin case folding.
-                // It works for latin source code identifiers though.
-                if (!(end < 65 || start > 122)) {
-                    if (!(end < 65 || start > 90)) {
-                        ranges.add(Arrays.asList(new Integer[]{Math.max(65, start) | 32, Math.min(end, 90) | 32}));
-                    }
-                    if (!(end < 97 || start > 122)) {
-                        ranges.add(Arrays.asList(new Integer[]{Math.max(97, start) & ~32, Math.min(end, 122) & ~32}));
-                    }
-                }
-            }
-        }
-
-        // [[1, 10], [3, 4], [8, 12], [14, 14], [16, 16], [17, 17]]
-        // -> [[1, 12], [14, 14], [16, 17]]
-        Collections.sort(ranges, new Comparator<List<Integer>>() {
-
-            @Override
-            public int compare(List<Integer> a, List<Integer> b) {
-                return a.get(0) != b.get(0) ? (a.get(0) - b.get(0)) : (b.get(1) - a.get(1));
-            }
-        });
-        List<List<Integer>> consolidatedRanges = new ArrayList<List<Integer>>();
-//        List<Integer> lastRange = Arrays.asList(new Integer[]{0, 0});
-        List<Integer> lastRange = new ArrayList<Integer>(Arrays.asList(new Integer[]{0, 0}));
-        for (int i = 0; i < ranges.size(); ++i) {
-            List<Integer> range = ranges.get(i);
-            if (lastRange.get(1) != null && range.get(0) <= lastRange.get(1) + 1) {
-                lastRange.set(1, Math.max(lastRange.get(1), range.get(1)));
-            } else {
-                // reference of lastRange is added
-                consolidatedRanges.add(lastRange = range);
-            }
-        }
-
-        for (int i = 0; i < consolidatedRanges.size(); ++i) {
-            List<Integer> range = consolidatedRanges.get(i);
-            out.add(encodeEscape(range.get(0)));
-            if (range.get(1) > range.get(0)) {
-                if (range.get(1) + 1 > range.get(0)) {
-                    out.add("-");
-                }
-                out.add(encodeEscape(range.get(1)));
-            }
-        }
-        out.add("]");
-
-        return Util.join(out);
-    }
-
-    protected String allowAnywhereFoldCaseAndRenumberGroups(Pattern regex) {
-        // Split into character sets, escape sequences, punctuation strings
-        // like ('(', '(?:', ')', '^'), and runs of characters that do not
-        // include any of the above.
-        String[] parts = Util.match(Pattern.compile("(?:"
-                + "\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]" // a character set
-                + "|\\\\u[A-Fa-f0-9]{4}" // a unicode escape
-                + "|\\\\x[A-Fa-f0-9]{2}" // a hex escape
-                + "|\\\\[0-9]+" // a back-reference or octal escape
-                + "|\\\\[^ux0-9]" // other escape sequence
-                + "|\\(\\?[:!=]" // start of a non-capturing group
-                + "|[\\(\\)\\^]" // start/end of a group, or line start
-                + "|[^\\x5B\\x5C\\(\\)\\^]+" // run of other characters
-                + ")"), regex.pattern(), true);
-        int n = parts.length;
-
-        // Maps captured group numbers to the number they will occupy in
-        // the output or to -1 if that has not been determined, or to
-        // undefined if they need not be capturing in the output.
-        Map<Integer, Integer> capturedGroups = new HashMap<Integer, Integer>();
-
-        // Walk over and identify back references to build the capturedGroups
-        // mapping.
-        for (int i = 0, groupIndex = 0; i < n; ++i) {
-            String p = parts[i];
-            if (p.equals("(")) {
-                // groups are 1-indexed, so max group index is count of '('
-                ++groupIndex;
-            } else if ('\\' == p.charAt(0)) {
-                try {
-                    int decimalValue = Math.abs(Integer.parseInt(p.substring(1)));
-                    if (decimalValue <= groupIndex) {
-                        capturedGroups.put(decimalValue, -1);
-                    } else {
-                        // Replace with an unambiguous escape sequence so that
-                        // an octal escape sequence does not turn into a backreference
-                        // to a capturing group from an earlier regex.
-                        parts[i] = encodeEscape(decimalValue);
-                    }
-                } catch (NumberFormatException ex) {
-                }
-            }
-        }
-
-        // Renumber groups and reduce capturing groups to non-capturing groups
-        // where possible.
-        for (int i : capturedGroups.keySet()) {
-            if (-1 == capturedGroups.get(i)) {
-                capturedGroups.put(i, ++capturedGroupIndex);
-            }
-        }
-        for (int i = 0, groupIndex = 0; i < n; ++i) {
-            String p = parts[i];
-            if (p.equals("(")) {
-                ++groupIndex;
-                if (capturedGroups.get(groupIndex) == null) {
-                    parts[i] = "(?:";
-                }
-            } else if ('\\' == p.charAt(0)) {
-                try {
-                    int decimalValue = Math.abs(Integer.parseInt(p.substring(1)));
-                    if (decimalValue <= groupIndex) {
-                        parts[i] = "\\" + capturedGroups.get(decimalValue);
-                    }
-                } catch (NumberFormatException ex) {
-                }
-            }
-        }
-
-        // Remove any prefix anchors so that the output will match anywhere.
-        // ^^ really does mean an anchored match though.
-        for (int i = 0; i < n; ++i) {
-            if ("^".equals(parts[i]) && !"^".equals(parts[i + 1])) {
-                parts[i] = "";
-            }
-        }
-
-        // Expand letters to groups to handle mixing of case-sensitive and
-        // case-insensitive patterns if necessary.
-        if ((regex.flags() & Pattern.CASE_INSENSITIVE) != 0 && needToFoldCase) {
-            for (int i = 0; i < n; ++i) {
-                String p = parts[i];
-                char ch0 = p.length() > 0 ? p.charAt(0) : 0;
-                if (p.length() >= 2 && ch0 == '[') {
-                    parts[i] = caseFoldCharset(p);
-                } else if (ch0 != '\\') {
-                    // TODO: handle letters in numeric escapes.
-                    StringBuffer sb = new StringBuffer();
-                    Matcher _matcher = Pattern.compile("[a-zA-Z]").matcher(p);
-                    while (_matcher.find()) {
-                        int cc = _matcher.group(0).codePointAt(0);
-                        _matcher.appendReplacement(sb, "");
-                        sb.append("[").append(Character.toString((char) (cc & ~32))).append(Character.toString((char) (cc | 32))).append("]");
-                    }
-                    _matcher.appendTail(sb);
-                    parts[i] = sb.toString();
-                }
-            }
-        }
-
-        return Util.join(parts);
-    }
-}
diff --git a/src/prettify/JTextComponentRowHeader.java b/src/prettify/JTextComponentRowHeader.java
deleted file mode 100644
index 8a7b32d..0000000
--- a/src/prettify/JTextComponentRowHeader.java
+++ /dev/null
@@ -1,519 +0,0 @@
-// Copyright (C) 2011 Chan Wai Shing
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-package prettify;
-
-import java.awt.Color;
-import java.awt.Container;
-import java.awt.Dimension;
-import java.awt.Font;
-import java.awt.FontMetrics;
-import java.awt.Graphics;
-import java.awt.Graphics2D;
-import java.awt.Point;
-import java.awt.RenderingHints;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import javax.swing.JPanel;
-import javax.swing.JScrollPane;
-import javax.swing.JViewport;
-import javax.swing.event.DocumentEvent;
-import javax.swing.event.DocumentListener;
-import javax.swing.text.BadLocationException;
-import javax.swing.text.Document;
-import javax.swing.text.Element;
-import javax.swing.text.JTextComponent;
-
-/**
- * A row header panel for JScrollPane.
- * It is used with JTextComponent for line number displaying.<br />
- * Currently it only accept fixed-height line.<br />
- * <b>The usage is not limited to this syntax highlighter, it can be used on all JTextComponent.</b>
- * @author Chan Wai Shing <cws1989@gmail.com>
- */
-public class JTextComponentRowHeader extends JPanel {
-
-    /**
-     * Indicate whether it is in debug mode or not.
-     */
-    protected final static boolean debug;
-
-    static {
-        String debugMode = System.getProperty("PrettifyDebugMode");
-        debug = debugMode == null || !debugMode.equals("true") ? false : true;
-    }
-    private static final long serialVersionUID = 1L;
-    /**
-     * The anti-aliasing setting of the line number text. See {@link java.awt.RenderingHints}.
-     */
-    private Object textAntiAliasing = RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT;
-    /**
-     * The color of the border that joint the gutter and the script text panel.
-     */
-    private Color borderColor = new Color(184, 184, 184);
-    /**
-     * The background of the row when it is highlighted.
-     */
-    private Color highlightedColor = Color.black;
-    /**
-     * The minimum padding from 'the leftmost of the line number text' to 'the left margin'.
-     */
-    private int paddingLeft = 7;
-    /**
-     * The minimum padding from 'the rightmost of the line number text' to 'the right margin' (not to the gutter border).
-     */
-    private int paddingRight = 2;
-    /**
-     * The width of the border that joint the gutter and the script text panel.
-     */
-    private int borderWidth = 1;
-    /**
-     * The JScrollPane that it be added into.
-     */
-    protected JScrollPane scrollPane;
-    /**
-     * The text component to listen the change events on.
-     */
-    protected JTextComponent textComponent;
-    /**
-     * The document of the text component.
-     */
-    protected Document document;
-    /**
-     * The document listener for {@link #document}.
-     */
-    protected DocumentListener documentListener;
-    /**
-     * The cached panel width.
-     */
-    protected int panelWidth;
-    /**
-     * The cached largest row number (for determine panel width {@link #panelWidth}).
-     */
-    protected int largestRowNumber;
-    /**
-     * The cached text component height, for determine panel height.
-     */
-    protected int textComponentHeight;
-    /**
-     * The line number offset. E.g. set offset to 9 will make the first line number to appear at line 1 + 9 = 10
-     */
-    private int lineNumberOffset;
-    /**
-     * The list of line numbers that indicate which lines are needed to be highlighted.
-     */
-    private final List<Integer> highlightedLineList;
-    /**
-     * Indicator indicate whether it is listening to the document change events or not.
-     */
-    private boolean listenToDocumentUpdate;
-
-    /**
-     * Constructor.
-     * @param scrollPane the JScrollPane that it be added into
-     * @param textComponent the text component to listen the change events on
-     */
-    public JTextComponentRowHeader(JScrollPane scrollPane, JTextComponent textComponent) {
-        super();
-
-        if (scrollPane == null) {
-            throw new NullPointerException("argument 'scrollPane' cannot be null");
-        }
-        if (textComponent == null) {
-            throw new NullPointerException("argument 'textComponent' cannot be null");
-        }
-
-        setFont(new Font("Verdana", Font.PLAIN, 10));
-        setForeground(Color.black);
-        setBackground(new Color(233, 232, 226));
-
-        this.scrollPane = scrollPane;
-        this.textComponent = textComponent;
-
-        panelWidth = 0;
-        largestRowNumber = 1;
-        textComponentHeight = 0;
-
-        lineNumberOffset = 0;
-        highlightedLineList = Collections.synchronizedList(new ArrayList<Integer>());
-
-        listenToDocumentUpdate = true;
-
-        document = textComponent.getDocument();
-        documentListener = new DocumentListener() {
-
-            @Override
-            public void insertUpdate(DocumentEvent e) {
-                handleEvent(e);
-            }
-
-            @Override
-            public void removeUpdate(DocumentEvent e) {
-                handleEvent(e);
-            }
-
-            @Override
-            public void changedUpdate(DocumentEvent e) {
-                handleEvent(e);
-            }
-
-            public void handleEvent(DocumentEvent e) {
-                if (!listenToDocumentUpdate) {
-                    return;
-                }
-                Document _document = e.getDocument();
-                if (document == _document) {
-                    checkPanelSize();
-                } else {
-                    _document.removeDocumentListener(this);
-                }
-            }
-        };
-
-        document.addDocumentListener(documentListener);
-
-        checkPanelSize();
-    }
-
-    /**
-     * Check if the 'document of the textComponent' has changed to another document or not.
-     */
-    protected void validateTextComponentDocument() {
-        Document _currentDocument = textComponent.getDocument();
-        if (document != _currentDocument) {
-            document.removeDocumentListener(documentListener);
-            document = _currentDocument;
-            _currentDocument.addDocumentListener(documentListener);
-        }
-    }
-
-    /**
-     * Check whether the height of the row header panel match with the height of the text component or not.
-     * If not, it will invoke {@link #updatePanelSize()}.
-     */
-    protected void checkPanelSize() {
-        validateTextComponentDocument();
-        int _largestRowNumber = document.getDefaultRootElement().getElementCount() + lineNumberOffset;
-        int _panelWidth = getFontMetrics(getFont()).stringWidth(Integer.toString(_largestRowNumber)) + paddingLeft + paddingRight;
-        if (panelWidth != _panelWidth || largestRowNumber != _largestRowNumber) {
-            panelWidth = _panelWidth;
-            largestRowNumber = _largestRowNumber;
-            updatePanelSize();
-        }
-    }
-
-    /**
-     * Update the panel size.
-     */
-    protected void updatePanelSize() {
-        Container parent = getParent();
-        if (parent != null) {
-            parent.doLayout();
-            scrollPane.doLayout();
-            parent.repaint();
-        }
-    }
-
-    /**
-     * The font of the line number.
-     */
-    @Override
-    public void setFont(Font font) {
-        super.setFont(font);
-    }
-
-    /**
-     * The color of the line number.
-     */
-    @Override
-    public void setForeground(Color foreground) {
-        super.setForeground(foreground);
-    }
-
-    /**
-     * The background of the panel.
-     */
-    @Override
-    public void setBackground(Color background) {
-        super.setBackground(background);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public Dimension getPreferredSize() {
-        textComponentHeight = textComponent.getPreferredSize().height;
-        return new Dimension(panelWidth, textComponentHeight);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void paint(Graphics g) {
-        super.paint(g);
-
-        // check whether the height of this panel matches the height of the text component or not
-        Dimension textComponentPreferredSize = textComponent.getPreferredSize();
-        if (textComponentHeight != textComponentPreferredSize.height) {
-            textComponentHeight = textComponentPreferredSize.height;
-            updatePanelSize();
-        }
-
-        JViewport viewport = scrollPane.getViewport();
-        Point viewPosition = viewport.getViewPosition();
-        Dimension viewportSize = viewport.getSize();
-
-        validateTextComponentDocument();
-        Element defaultRootElement = document.getDefaultRootElement();
-
-
-        // maybe able to get the value when font changed and cache them
-        // however i'm not sure if there is any condition which will make the java.awt.FontMetrics get by getFontMetrics() from java.awt.Graphics is different from getFontMetrics() from java.awt.Component
-        FontMetrics fontMetrics = g.getFontMetrics(getFont());
-        int fontHeight = fontMetrics.getHeight();
-        int fontAscent = fontMetrics.getAscent();
-        int fontLeading = fontMetrics.getLeading();
-
-        FontMetrics textPaneFontMetrics = g.getFontMetrics(textComponent.getFont());
-        int textPaneFontHeight = textPaneFontMetrics.getHeight();
-
-
-        // get the location of the document of the left top and right bottom point of the visible part of the text component
-        int documentOffsetStart = textComponent.viewToModel(viewPosition);
-        int documentOffsetEnd = textComponent.viewToModel(new Point(viewPosition.x + viewportSize.width, viewPosition.y + viewportSize.height));
-
-        // convert the location to line number
-        int startLine = defaultRootElement.getElementIndex(documentOffsetStart) + 1 + lineNumberOffset;
-        int endLine = defaultRootElement.getElementIndex(documentOffsetEnd) + 1 + lineNumberOffset;
-
-
-        // draw right border
-        g.setColor(borderColor);
-        g.fillRect(panelWidth - borderWidth, viewPosition.y, borderWidth, viewportSize.height);
-
-        // draw line number
-        int startY = -1, baselineOffset = -1;
-        try {
-            startY = textComponent.modelToView(documentOffsetStart).y;
-            baselineOffset = (textPaneFontHeight / 2) + fontAscent - (fontHeight / 2) + fontLeading;
-        } catch (BadLocationException ex) {
-            if (debug) {
-                Logger.getLogger(JTextComponentRowHeader.class.getName()).log(Level.WARNING, null, ex);
-            }
-            return;
-        }
-
-        // text anti-aliasing
-        ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, textAntiAliasing);
-        // preserve the foreground color (for recover the color after highlighing the line)
-        Color foregroundColor = getForeground();
-
-        g.setColor(foregroundColor);
-        g.setFont(getFont());
-
-        for (int i = startLine, y = startY + baselineOffset; i <= endLine; y += textPaneFontHeight, i++) {
-            boolean highlighted = false;
-            if (highlightedLineList.indexOf((Integer) i) != -1) {
-                // highlight this line
-                g.setColor(borderColor);
-                g.fillRect(0, y - baselineOffset, panelWidth - borderWidth, textPaneFontHeight);
-                g.setColor(highlightedColor);
-                highlighted = true;
-            }
-
-            // draw the line number
-            String lineNumberString = Integer.toString(i);
-            int lineNumberStringWidth = fontMetrics.stringWidth(lineNumberString);
-            g.drawString(lineNumberString, panelWidth - lineNumberStringWidth - paddingRight, y);
-
-            // restore the line number text color
-            if (highlighted) {
-                g.setColor(foregroundColor);
-            }
-        }
-    }
-
-    /**
-     * The anti-aliasing setting of the line number text. See {@link java.awt.RenderingHints}.
-     */
-    public Object getTextAntiAliasing() {
-        return textAntiAliasing;
-    }
-
-    /**
-     * The anti-aliasing setting of the line number text. See {@link java.awt.RenderingHints}.
-     */
-    public void setTextAntiAliasing(Object textAntiAliasing) {
-        if (textAntiAliasing == null) {
-            throw new NullPointerException("argument 'textAntiAliasing' cannot be null");
-        }
-        this.textAntiAliasing = textAntiAliasing;
-        repaint();
-    }
-
-    /**
-     * The color of the border that joint the gutter and the script text panel.
-     */
-    public Color getBorderColor() {
-        return borderColor;
-    }
-
-    /**
-     * The color of the border that joint the gutter and the script text panel.
-     */
-    public void setBorderColor(Color borderColor) {
-        if (borderColor == null) {
-            throw new NullPointerException("argument 'borderColor' cannot be null");
-        }
-        this.borderColor = borderColor;
-        repaint();
-    }
-
-    /**
-     * The background of the highlighted row.
-     */
-    public Color getHighlightedColor() {
-        return highlightedColor;
-    }
-
-    /**
-     * The background of the highlighted row.
-     */
-    public void setHighlightedColor(Color highlightedColor) {
-        if (highlightedColor == null) {
-            throw new NullPointerException("argument 'highlightedColor' cannot be null");
-        }
-        this.highlightedColor = highlightedColor;
-        repaint();
-    }
-
-    /**
-     * The minimum padding from the 'leftmost of the line number text' to the 'left margin'.
-     */
-    public int getPaddingLeft() {
-        return paddingLeft;
-    }
-
-    /**
-     * The minimum padding from 'the leftmost of the line number text' to the 'left margin'.
-     */
-    public void setPaddingLeft(int paddingLeft) {
-        this.paddingLeft = paddingLeft;
-        checkPanelSize();
-    }
-
-    /**
-     * The minimum padding from the 'rightmost of the line number text' to the 'right margin' (not to the gutter border).
-     */
-    public int getPaddingRight() {
-        return paddingRight;
-    }
-
-    /**
-     * The minimum padding from the 'rightmost of the line number text' to the 'right margin' (not to the gutter border).
-     */
-    public void setPaddingRight(int paddingRight) {
-        this.paddingRight = paddingRight;
-        checkPanelSize();
-    }
-
-    /**
-     * The width of the border that joint the gutter and the script text panel.
-     */
-    public int getBorderWidth() {
-        return borderWidth;
-    }
-
-    /**
-     * The width of the border that joint the gutter and the script text panel.
-     */
-    public void setBorderWidth(int borderWidth) {
-        this.borderWidth = borderWidth;
-        repaint();
-    }
-
-    /**
-     * Get the line number offset
-     * @return the offset
-     */
-    public int getLineNumberOffset() {
-        return lineNumberOffset;
-    }
-
-    /**
-     * Set the line number offset. E.g. set offset to 9 will make the first line number to appear at line 1 + 9 = 10
-     * @param offset the offset
-     */
-    public void setLineNumberOffset(int offset) {
-        lineNumberOffset = Math.max(lineNumberOffset, offset);
-        checkPanelSize();
-        repaint();
-    }
-
-    /**
-     * Get the list of highlighted lines.
-     * @return a copy of the list
-     */
-    public List<Integer> getHighlightedLineList() {
-        List<Integer> returnList;
-        synchronized (highlightedLineList) {
-            returnList = new ArrayList<Integer>(highlightedLineList);
-        }
-        return returnList;
-    }
-
-    /**
-     * Set highlighted lines. Note that this will clear all previous recorded highlighted lines.
-     * @param highlightedLineList the list that contain the highlighted lines
-     */
-    public void setHighlightedLineList(List<Integer> highlightedLineList) {
-        synchronized (this.highlightedLineList) {
-            this.highlightedLineList.clear();
-            if (highlightedLineList != null) {
-                this.highlightedLineList.addAll(highlightedLineList);
-            }
-        }
-        repaint();
-    }
-
-    /**
-     * Add highlighted line.
-     * @param lineNumber the line number to highlight
-     */
-    public void addHighlightedLine(int lineNumber) {
-        highlightedLineList.add(lineNumber);
-        repaint();
-    }
-
-    /**
-     * Check if it is listening to the document change events.
-     * @return true if it is listening, false if not 
-     */
-    public boolean isListenToDocumentUpdate() {
-        return listenToDocumentUpdate;
-    }
-
-    /**
-     * Set to listen to document change events or not. It is useful when a number of updates are needed to be done to the text component.
-     * May invoke {@link #updatePanelSize()}.
-     * @param listenToDocumentUpdate true to listen on document change, false not
-     */
-    public void setListenToDocumentUpdate(boolean listenToDocumentUpdate) {
-        this.listenToDocumentUpdate = listenToDocumentUpdate;
-    }
-}
diff --git a/src/prettify/Job.java b/src/prettify/Job.java
deleted file mode 100644
index 3440348..0000000
--- a/src/prettify/Job.java
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright (C) 2006 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-package prettify;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * This is the job object that similar to those in JavaScript Prettify.
- * @author Chan Wai Shing <cws1989@gmail.com>
- */
-public class Job {
-
-    /**
-     * The starting point of the source code.
-     */
-    protected int basePos;
-    /**
-     * The source code.
-     */
-    protected String sourceCode;
-    /**
-     * The parsed results. n<sup>th</sup> items are starting position position, n+1<sup>th</sup> 
-     * items are the three-letter style keyword, where n start from 0.
-     */
-    protected List<Object> decorations;
-
-    /**
-     * Constructor.
-     */
-    public Job() {
-        this(0, "");
-    }
-
-    /**
-     * Constructor.
-     * @param basePos the starting point of the source code
-     * @param sourceCode the source code
-     */
-    public Job(int basePos, String sourceCode) {
-        this.basePos = basePos;
-        this.sourceCode = sourceCode;
-        decorations = new ArrayList<Object>();
-    }
-
-    /**
-     * Set the starting point of the source code.
-     * @return the position
-     */
-    public int getBasePos() {
-        return basePos;
-    }
-
-    /**
-     * Set the starting point of the source code.
-     * @param basePos the position
-     */
-    public void setBasePos(int basePos) {
-        this.basePos = basePos;
-    }
-
-    /**
-     * Get the source code.
-     * @return the source code
-     */
-    public String getSourceCode() {
-        return sourceCode;
-    }
-
-    /**
-     * Set the source code.
-     * @param sourceCode the source code 
-     */
-    public void setSourceCode(String sourceCode) {
-        this.sourceCode = sourceCode;
-    }
-
-    /**
-     * Get the parsed results. see {@link #decorations}.
-     * @return the parsed results
-     */
-    public List<Object> getDecorations() {
-        return new ArrayList<Object>(decorations);
-    }
-
-    /**
-     * Set the parsed results. see {@link #decorations}.
-     * @param decorations the parsed results
-     */
-    public void setDecorations(List<Object> decorations) {
-        if (decorations == null) {
-            this.decorations = new ArrayList<Object>();
-            return;
-        }
-        this.decorations = new ArrayList<Object>(decorations);
-    }
-}
diff --git a/src/prettify/Lang.java b/src/prettify/Lang.java
deleted file mode 100644
index 2db93d5..0000000
--- a/src/prettify/Lang.java
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright (C) 2011 Chan Wai Shing
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-package prettify;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Lang class for Java Prettify.
- * @author Chan Wai Shing <cws1989@gmail.com>
- */
-public abstract class Lang {
-
-    /**
-     * Similar to those in JavaScript prettify.js.
-     */
-    protected List<List<Object>> shortcutStylePatterns;
-    /**
-     * Similar to those in JavaScript prettify.js.
-     */
-    protected List<List<Object>> fallthroughStylePatterns;
-    /**
-     * See {@link prettify.lang.LangCss} for example.
-     */
-    protected List<Lang> extendedLangs;
-
-    /**
-     * Constructor.
-     */
-    public Lang() {
-        shortcutStylePatterns = new ArrayList<List<Object>>();
-        fallthroughStylePatterns = new ArrayList<List<Object>>();
-        extendedLangs = new ArrayList<Lang>();
-    }
-
-    /**
-     * This method should be overridden by the child class.
-     * This provide the file extensions list to help to determine which {@link Lang} to use.
-     * See JavaScript prettify.js.
-     * @return the list of file extensions
-     */
-    public static List<String> getFileExtensions() {
-        return new ArrayList<String>();
-    }
-
-    public List<List<Object>> getShortcutStylePatterns() {
-        List<List<Object>> returnList = new ArrayList<List<Object>>();
-        for (List<Object> shortcutStylePattern : shortcutStylePatterns) {
-            returnList.add(new ArrayList<Object>(shortcutStylePattern));
-        }
-        return returnList;
-    }
-
-    public void setShortcutStylePatterns(List<List<Object>> shortcutStylePatterns) {
-        if (shortcutStylePatterns == null) {
-            this.shortcutStylePatterns = new ArrayList<List<Object>>();
-            return;
-        }
-        List<List<Object>> cloneList = new ArrayList<List<Object>>();
-        for (List<Object> shortcutStylePattern : shortcutStylePatterns) {
-            cloneList.add(new ArrayList<Object>(shortcutStylePattern));
-        }
-        this.shortcutStylePatterns = cloneList;
-    }
-
-    public List<List<Object>> getFallthroughStylePatterns() {
-        List<List<Object>> returnList = new ArrayList<List<Object>>();
-        for (List<Object> fallthroughStylePattern : fallthroughStylePatterns) {
-            returnList.add(new ArrayList<Object>(fallthroughStylePattern));
-        }
-        return returnList;
-    }
-
-    public void setFallthroughStylePatterns(List<List<Object>> fallthroughStylePatterns) {
-        if (fallthroughStylePatterns == null) {
-            this.fallthroughStylePatterns = new ArrayList<List<Object>>();
-            return;
-        }
-        List<List<Object>> cloneList = new ArrayList<List<Object>>();
-        for (List<Object> fallthroughStylePattern : fallthroughStylePatterns) {
-            cloneList.add(new ArrayList<Object>(fallthroughStylePattern));
-        }
-        this.fallthroughStylePatterns = cloneList;
-    }
-
-    /**
-     * Get the extended languages list.
-     * @return the list
-     */
-    public List<Lang> getExtendedLangs() {
-        return new ArrayList<Lang>(extendedLangs);
-    }
-
-    /**
-     * Set extended languages. Because we cannot register multiple languages within 
-     * one {@link prettify.Lang}, so it is used as an solution.
-     * See {@link prettify.lang.LangCss} for example.
-     * @param extendedLangs the list of {@link prettify.Lang}s
-     */
-    public void setExtendedLangs(List<Lang> extendedLangs) {
-        this.extendedLangs = new ArrayList<Lang>(extendedLangs);
-    }
-}
diff --git a/src/prettify/Prettify.java b/src/prettify/Prettify.java
deleted file mode 100644
index 0532c92..0000000
--- a/src/prettify/Prettify.java
+++ /dev/null
@@ -1,825 +0,0 @@
-// Copyright (C) 2006 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-package prettify;
-
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import java.util.regex.Pattern;
-import prettify.lang.LangAppollo;
-import prettify.lang.LangClj;
-import prettify.lang.LangCss;
-import prettify.lang.LangGo;
-import prettify.lang.LangHs;
-import prettify.lang.LangLisp;
-import prettify.lang.LangLua;
-import prettify.lang.LangMl;
-import prettify.lang.LangN;
-import prettify.lang.LangScala;
-import prettify.lang.LangSql;
-import prettify.lang.LangTex;
-import prettify.lang.LangVb;
-import prettify.lang.LangVhdl;
-import prettify.lang.LangWiki;
-import prettify.lang.LangXq;
-import prettify.lang.LangYaml;
-
-/**
- * This is similar to the prettify.js in JavaScript Prettify.
- * 
- * All comments are adapted from the JavaScript Prettify.
- * 
- * <p>
- * Some functions for browser-side pretty printing of code contained in html.
- * </p>
- *
- * <p>
- * For a fairly comprehensive set of languages see the
- * <a href="http://google-code-prettify.googlecode.com/svn/trunk/README.html#langs">README</a>
- * file that came with this source.  At a minimum, the lexer should work on a
- * number of languages including C and friends, Java, Python, Bash, SQL, HTML,
- * XML, CSS, Javascript, and Makefiles.  It works passably on Ruby, PHP and Awk
- * and a subset of Perl, but, because of commenting conventions, doesn't work on
- * Smalltalk, Lisp-like, or CAML-like languages without an explicit lang class.
- * <p>
- * Usage: <ol>
- * <li> include this source file in an html page via
- *   {@code <script type="text/javascript" src="/path/to/prettify.js"></script>}
- * <li> define style rules.  See the example page for examples.
- * <li> mark the {@code <pre>} and {@code <code>} tags in your source with
- *    {@code class=prettyprint.}
- *    You can also use the (html deprecated) {@code <xmp>} tag, but the pretty
- *    printer needs to do more substantial DOM manipulations to support that, so
- *    some css styles may not be preserved.
- * </ol>
- * That's it.  I wanted to keep the API as simple as possible, so there's no
- * need to specify which language the code is in, but if you wish, you can add
- * another class to the {@code <pre>} or {@code <code>} element to specify the
- * language, as in {@code <pre class="prettyprint lang-java">}.  Any class that
- * starts with "lang-" followed by a file extension, specifies the file type.
- * See the "lang-*.js" files in this directory for code that implements
- * per-language file handlers.
- * <p>
- * Change log:<br>
- * cbeust, 2006/08/22
- * <blockquote>
- *   Java annotations (start with "@") are now captured as literals ("lit")
- * </blockquote>
- */
-public class Prettify {
-
-    /**
-     * Indicate whether it is in debug mode or not.
-     */
-    protected final static boolean debug;
-
-    static {
-        String debugMode = System.getProperty("PrettifyDebugMode");
-        debug = debugMode == null || !debugMode.equals("true") ? false : true;
-    }
-    // Keyword lists for various languages.
-    public static final String FLOW_CONTROL_KEYWORDS = "break,continue,do,else,for,if,return,while";
-    public static final String C_KEYWORDS = FLOW_CONTROL_KEYWORDS + "," + "auto,case,char,const,default,"
-            + "double,enum,extern,float,goto,int,long,register,short,signed,sizeof,"
-            + "static,struct,switch,typedef,union,unsigned,void,volatile";
-    public static final String COMMON_KEYWORDS = C_KEYWORDS + "," + "catch,class,delete,false,import,"
-            + "new,operator,private,protected,public,this,throw,true,try,typeof";
-    public static final String CPP_KEYWORDS = COMMON_KEYWORDS + "," + "alignof,align_union,asm,axiom,bool,"
-            + "concept,concept_map,const_cast,constexpr,decltype,"
-            + "dynamic_cast,explicit,export,friend,inline,late_check,"
-            + "mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,"
-            + "template,typeid,typename,using,virtual,where";
-    public static final String JAVA_KEYWORDS = COMMON_KEYWORDS + ","
-            + "abstract,boolean,byte,extends,final,finally,implements,import,"
-            + "instanceof,null,native,package,strictfp,super,synchronized,throws,"
-            + "transient";
-    public static final String CSHARP_KEYWORDS = JAVA_KEYWORDS + ","
-            + "as,base,by,checked,decimal,delegate,descending,dynamic,event,"
-            + "fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,"
-            + "object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,"
-            + "stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var";
-    public static final String COFFEE_KEYWORDS = "all,and,by,catch,class,else,extends,false,finally,"
-            + "for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,"
-            + "true,try,unless,until,when,while,yes";
-    public static final String JSCRIPT_KEYWORDS = COMMON_KEYWORDS + ","
-            + "debugger,eval,export,function,get,null,set,undefined,var,with,"
-            + "Infinity,NaN";
-    public static final String PERL_KEYWORDS = "caller,delete,die,do,dump,elsif,eval,exit,foreach,for,"
-            + "goto,if,import,last,local,my,next,no,our,print,package,redo,require,"
-            + "sub,undef,unless,until,use,wantarray,while,BEGIN,END";
-    public static final String PYTHON_KEYWORDS = FLOW_CONTROL_KEYWORDS + "," + "and,as,assert,class,def,del,"
-            + "elif,except,exec,finally,from,global,import,in,is,lambda,"
-            + "nonlocal,not,or,pass,print,raise,try,with,yield,"
-            + "False,True,None";
-    public static final String RUBY_KEYWORDS = FLOW_CONTROL_KEYWORDS + "," + "alias,and,begin,case,class,"
-            + "def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,"
-            + "rescue,retry,self,super,then,true,undef,unless,until,when,yield,"
-            + "BEGIN,END";
-    public static final String SH_KEYWORDS = FLOW_CONTROL_KEYWORDS + "," + "case,done,elif,esac,eval,fi,"
-            + "function,in,local,set,then,until";
-    public static final String ALL_KEYWORDS = CPP_KEYWORDS + "," + CSHARP_KEYWORDS + "," + JSCRIPT_KEYWORDS + "," + PERL_KEYWORDS + ","
-            + PYTHON_KEYWORDS + "," + RUBY_KEYWORDS + "," + SH_KEYWORDS;
-    public static final Pattern C_TYPES = Pattern.compile("^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\\d*)");
-    // token style names.  correspond to css classes
-    /**
-     * token style for a string literal
-     */
-    public static final String PR_STRING = "str";
-    /**
-     * token style for a keyword
-     */
-    public static final String PR_KEYWORD = "kwd";
-    /**
-     * token style for a comment
-     */
-    public static final String PR_COMMENT = "com";
-    /**
-     * token style for a type
-     */
-    public static final String PR_TYPE = "typ";
-    /**
-     * token style for a literal value.  e.g. 1, null, true.
-     */
-    public static final String PR_LITERAL = "lit";
-    /**
-     * token style for a punctuation string.
-     */
-    public static final String PR_PUNCTUATION = "pun";
-    /**
-     * token style for a plain text.
-     */
-    public static final String PR_PLAIN = "pln";
-    /**
-     * token style for an sgml tag.
-     */
-    public static final String PR_TAG = "tag";
-    /**
-     * token style for a markup declaration such as a DOCTYPE.
-     */
-    public static final String PR_DECLARATION = "dec";
-    /**
-     * token style for embedded source.
-     */
-    public static final String PR_SOURCE = "src";
-    /**
-     * token style for an sgml attribute name.
-     */
-    public static final String PR_ATTRIB_NAME = "atn";
-    /**
-     * token style for an sgml attribute value.
-     */
-    public static final String PR_ATTRIB_VALUE = "atv";
-    /**
-     * A class that indicates a section of markup that is not code, e.g. to allow
-     * embedding of line numbers within code listings.
-     */
-    public static final String PR_NOCODE = "nocode";
-    /**
-     * A set of tokens that can precede a regular expression literal in
-     * javascript
-     * http://web.archive.org/web/20070717142515/http://www.mozilla.org/js/language/js20/rationale/syntax.html
-     * has the full list, but I've removed ones that might be problematic when
-     * seen in languages that don't support regular expression literals.
-     *
-     * <p>Specifically, I've removed any keywords that can't precede a regexp
-     * literal in a syntactically legal javascript program, and I've removed the
-     * "in" keyword since it's not a keyword in many languages, and might be used
-     * as a count of inches.
-     *
-     * <p>The link above does not accurately describe EcmaScript rules since
-     * it fails to distinguish between (a=++/b/i) and (a++/b/i) but it works
-     * very well in practice.
-     */
-    private static final String REGEXP_PRECEDER_PATTERN = "(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[+\\-]=|->|\\/=?|::?|<<?=?|>>?>?=?|,|;|\\?|@|\\[|~|\\{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";
-    // CAVEAT: this does not properly handle the case where a regular
-    // expression immediately follows another since a regular expression may
-    // have flags for case-sensitivity and the like.  Having regexp tokens
-    // adjacent is not valid in any language I'm aware of, so I'm punting.
-    // TODO: maybe style special characters inside a regexp as punctuation.
-
-    public Prettify() {
-        try {
-            Map<String, Object> decorateSourceMap = new HashMap<String, Object>();
-            decorateSourceMap.put("keywords", ALL_KEYWORDS);
-            decorateSourceMap.put("hashComments", true);
-            decorateSourceMap.put("cStyleComments", true);
-            decorateSourceMap.put("multiLineStrings", true);
-            decorateSourceMap.put("regexLiterals", true);
-            registerLangHandler(sourceDecorator(decorateSourceMap), Arrays.asList(new String[]{"default-code"}));
-
-            List<List<Object>> shortcutStylePatterns, fallthroughStylePatterns;
-
-            shortcutStylePatterns = new ArrayList<List<Object>>();
-            fallthroughStylePatterns = new ArrayList<List<Object>>();
-            fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_PLAIN, Pattern.compile("^[^<?]+")}));
-            fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_DECLARATION, Pattern.compile("^<!\\w[^>]*(?:>|$)")}));
-            fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_COMMENT, Pattern.compile("^<\\!--[\\s\\S]*?(?:-\\->|$)")}));
-            // Unescaped content in an unknown language
-            fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-", Pattern.compile("^<\\?([\\s\\S]+?)(?:\\?>|$)")}));
-            fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-", Pattern.compile("^<%([\\s\\S]+?)(?:%>|$)")}));
-            fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_PUNCTUATION, Pattern.compile("^(?:<[%?]|[%?]>)")}));
-            fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-", Pattern.compile("^<xmp\\b[^>]*>([\\s\\S]+?)<\\/xmp\\b[^>]*>", Pattern.CASE_INSENSITIVE)}));
-            // Unescaped content in javascript.  (Or possibly vbscript).
-            fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-js", Pattern.compile("^<script\\b[^>]*>([\\s\\S]*?)(<\\/script\\b[^>]*>)", Pattern.CASE_INSENSITIVE)}));
-            // Contains unescaped stylesheet content
-            fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-css", Pattern.compile("^<style\\b[^>]*>([\\s\\S]*?)(<\\/style\\b[^>]*>)", Pattern.CASE_INSENSITIVE)}));
-            fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-in.tag", Pattern.compile("^(<\\/?[a-z][^<>]*>)", Pattern.CASE_INSENSITIVE)}));
-            registerLangHandler(new CreateSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns), Arrays.asList(new String[]{"default-markup", "htm", "html", "mxml", "xhtml", "xml", "xsl"}));
-
-            shortcutStylePatterns = new ArrayList<List<Object>>();
-            fallthroughStylePatterns = new ArrayList<List<Object>>();
-            shortcutStylePatterns.add(Arrays.asList(new Object[]{PR_PLAIN, Pattern.compile("^[\\s]+"), null, " \t\r\n"}));
-            shortcutStylePatterns.add(Arrays.asList(new Object[]{PR_ATTRIB_VALUE, Pattern.compile("^(?:\\\"[^\\\"]*\\\"?|\\'[^\\']*\\'?)"), null, "\"'"}));
-            fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_TAG, Pattern.compile("^^<\\/?[a-z](?:[\\w.:-]*\\w)?|\\/?>$", Pattern.CASE_INSENSITIVE)}));
-            fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_ATTRIB_NAME, Pattern.compile("^(?!style[\\s=]|on)[a-z](?:[\\w:-]*\\w)?", Pattern.CASE_INSENSITIVE)}));
-            fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-uq.val", Pattern.compile("^=\\s*([^>\\'\\\"\\s]*(?:[^>\\'\\\"\\s\\/]|\\/(?=\\s)))", Pattern.CASE_INSENSITIVE)}));
-            fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_PUNCTUATION, Pattern.compile("^[=<>\\/]+")}));
-            fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-js", Pattern.compile("^on\\w+\\s*=\\s*\\\"([^\\\"]+)\\\"", Pattern.CASE_INSENSITIVE)}));
-            fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-js", Pattern.compile("^on\\w+\\s*=\\s*\\'([^\\']+)\\'", Pattern.CASE_INSENSITIVE)}));
-            fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-js", Pattern.compile("^on\\w+\\s*=\\s*([^\\\"\\'>\\s]+)", Pattern.CASE_INSENSITIVE)}));
-            fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-css", Pattern.compile("^style\\s*=\\s*\\\"([^\\\"]+)\\\"", Pattern.CASE_INSENSITIVE)}));
-            fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-css", Pattern.compile("^style\\s*=\\s*\\'([^\\']+)\\'", Pattern.CASE_INSENSITIVE)}));
-            fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-css", Pattern.compile("^style\\s*=\\s\\*([^\\\"\\'>\\s]+)", Pattern.CASE_INSENSITIVE)}));
-            registerLangHandler(new CreateSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns), Arrays.asList(new String[]{"in.tag"}));
-
-            shortcutStylePatterns = new ArrayList<List<Object>>();
-            fallthroughStylePatterns = new ArrayList<List<Object>>();
-            fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_ATTRIB_VALUE, Pattern.compile("^[\\s\\S]+")}));
-            registerLangHandler(new CreateSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns), Arrays.asList(new String[]{"uq.val"}));
-
-            decorateSourceMap = new HashMap<String, Object>();
-            decorateSourceMap.put("keywords", CPP_KEYWORDS);
-            decorateSourceMap.put("hashComments", true);
-            decorateSourceMap.put("cStyleComments", true);
-            decorateSourceMap.put("types", C_TYPES);
-            registerLangHandler(sourceDecorator(decorateSourceMap), Arrays.asList(new String[]{"c", "cc", "cpp", "cxx", "cyc", "m"}));
-
-            decorateSourceMap = new HashMap<String, Object>();
-            decorateSourceMap.put("keywords", "null,true,false");
-            registerLangHandler(sourceDecorator(decorateSourceMap), Arrays.asList(new String[]{"json"}));
-
-            decorateSourceMap = new HashMap<String, Object>();
-            decorateSourceMap.put("keywords", CSHARP_KEYWORDS);
-            decorateSourceMap.put("hashComments", true);
-            decorateSourceMap.put("cStyleComments", true);
-            decorateSourceMap.put("verbatimStrings", true);
-            decorateSourceMap.put("types", C_TYPES);
-            registerLangHandler(sourceDecorator(decorateSourceMap), Arrays.asList(new String[]{"cs"}));
-
-            decorateSourceMap = new HashMap<String, Object>();
-            decorateSourceMap.put("keywords", JAVA_KEYWORDS);
-            decorateSourceMap.put("cStyleComments", true);
-            registerLangHandler(sourceDecorator(decorateSourceMap), Arrays.asList(new String[]{"java"}));
-
-            decorateSourceMap = new HashMap<String, Object>();
-            decorateSourceMap.put("keywords", SH_KEYWORDS);
-            decorateSourceMap.put("hashComments", true);
-            decorateSourceMap.put("multiLineStrings", true);
-            registerLangHandler(sourceDecorator(decorateSourceMap), Arrays.asList(new String[]{"bsh", "csh", "sh"}));
-
-            decorateSourceMap = new HashMap<String, Object>();
-            decorateSourceMap.put("keywords", PYTHON_KEYWORDS);
-            decorateSourceMap.put("hashComments", true);
-            decorateSourceMap.put("multiLineStrings", true);
-            decorateSourceMap.put("tripleQuotedStrings", true);
-            registerLangHandler(sourceDecorator(decorateSourceMap), Arrays.asList(new String[]{"cv", "py"}));
-
-            decorateSourceMap = new HashMap<String, Object>();
-            decorateSourceMap.put("keywords", PERL_KEYWORDS);
-            decorateSourceMap.put("hashComments", true);
-            decorateSourceMap.put("multiLineStrings", true);
-            decorateSourceMap.put("regexLiterals", true);
-            registerLangHandler(sourceDecorator(decorateSourceMap), Arrays.asList(new String[]{"perl", "pl", "pm"}));
-
-            decorateSourceMap = new HashMap<String, Object>();
-            decorateSourceMap.put("keywords", RUBY_KEYWORDS);
-            decorateSourceMap.put("hashComments", true);
-            decorateSourceMap.put("multiLineStrings", true);
-            decorateSourceMap.put("regexLiterals", true);
-            registerLangHandler(sourceDecorator(decorateSourceMap), Arrays.asList(new String[]{"rb"}));
-
-            decorateSourceMap = new HashMap<String, Object>();
-            decorateSourceMap.put("keywords", JSCRIPT_KEYWORDS);
-            decorateSourceMap.put("cStyleComments", true);
-            decorateSourceMap.put("regexLiterals", true);
-            registerLangHandler(sourceDecorator(decorateSourceMap), Arrays.asList(new String[]{"js"}));
-
-            decorateSourceMap = new HashMap<String, Object>();
-            decorateSourceMap.put("keywords", COFFEE_KEYWORDS);
-            decorateSourceMap.put("hashComments", 3); // ### style block comments
-            decorateSourceMap.put("cStyleComments", true);
-            decorateSourceMap.put("multilineStrings", true);
-            decorateSourceMap.put("tripleQuotedStrings", true);
-            decorateSourceMap.put("regexLiterals", true);
-            registerLangHandler(sourceDecorator(decorateSourceMap), Arrays.asList(new String[]{"coffee"}));
-
-            shortcutStylePatterns = new ArrayList<List<Object>>();
-            fallthroughStylePatterns = new ArrayList<List<Object>>();
-            fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_STRING, Pattern.compile("^[\\s\\S]+")}));
-            registerLangHandler(new CreateSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns), Arrays.asList(new String[]{"regex"}));
-
-            /**
-             * Registers a language handler for Protocol Buffers as described at
-             * http://code.google.com/p/protobuf/.
-             *
-             * Based on the lexical grammar at
-             * http://research.microsoft.com/fsharp/manual/spec2.aspx#_Toc202383715
-             *
-             * @author mikesamuel@gmail.com
-             */
-            decorateSourceMap = new HashMap<String, Object>();
-            decorateSourceMap.put("keywords", "bytes,default,double,enum,extend,extensions,false,"
-                    + "group,import,max,message,option,"
-                    + "optional,package,repeated,required,returns,rpc,service,"
-                    + "syntax,to,true");
-            decorateSourceMap.put("types", Pattern.compile("^(bool|(double|s?fixed|[su]?int)(32|64)|float|string)\\b"));
-            decorateSourceMap.put("cStyleComments", true);
-            registerLangHandler(sourceDecorator(decorateSourceMap), Arrays.asList(new String[]{"proto"}));
-
-            register(LangAppollo.class);
-            register(LangClj.class);
-            register(LangCss.class);
-            register(LangGo.class);
-            register(LangHs.class);
-            register(LangLisp.class);
-            register(LangLua.class);
-            register(LangMl.class);
-            register(LangN.class);
-            register(LangScala.class);
-            register(LangSql.class);
-            register(LangTex.class);
-            register(LangVb.class);
-            register(LangVhdl.class);
-            register(LangWiki.class);
-            register(LangXq.class);
-            register(LangYaml.class);
-        } catch (Exception ex) {
-            if (debug) {
-                Logger.getLogger(Prettify.class.getName()).log(Level.SEVERE, null, ex);
-            }
-        }
-    }
-
-    /**
-     * Apply the given language handler to sourceCode and add the resulting
-     * decorations to out.
-     * @param basePos the index of sourceCode within the chunk of source
-     *    whose decorations are already present on out.
-     */
-    protected static void appendDecorations(int basePos, String sourceCode, CreateSimpleLexer langHandler, List<Object> out) {
-        if (sourceCode == null) {
-            throw new NullPointerException("argument 'sourceCode' cannot be null");
-        }
-        Job job = new Job();
-        job.setSourceCode(sourceCode);
-        job.setBasePos(basePos);
-        langHandler.decorate(job);
-        out.addAll(job.getDecorations());
-    }
-
-    public class CreateSimpleLexer {
-
-        protected List<List<Object>> fallthroughStylePatterns;
-        protected Map<Character, List<Object>> shortcuts = new HashMap<Character, List<Object>>();
-        protected Pattern tokenizer;
-        protected int nPatterns;
-
-        /** Given triples of [style, pattern, context] returns a lexing function,
-         * The lexing function interprets the patterns to find token boundaries and
-         * returns a decoration list of the form
-         * [index_0, style_0, index_1, style_1, ..., index_n, style_n]
-         * where index_n is an index into the sourceCode, and style_n is a style
-         * constant like PR_PLAIN.  index_n-1 <= index_n, and style_n-1 applies to
-         * all characters in sourceCode[index_n-1:index_n].
-         *
-         * The stylePatterns is a list whose elements have the form
-         * [style : string, pattern : RegExp, DEPRECATED, shortcut : string].
-         *
-         * Style is a style constant like PR_PLAIN, or can be a string of the
-         * form 'lang-FOO', where FOO is a language extension describing the
-         * language of the portion of the token in $1 after pattern executes.
-         * E.g., if style is 'lang-lisp', and group 1 contains the text
-         * '(hello (world))', then that portion of the token will be passed to the
-         * registered lisp handler for formatting.
-         * The text before and after group 1 will be restyled using this decorator
-         * so decorators should take care that this doesn't result in infinite
-         * recursion.  For example, the HTML lexer rule for SCRIPT elements looks
-         * something like ['lang-js', /<[s]cript>(.+?)<\/script>/].  This may match
-         * '<script>foo()<\/script>', which would cause the current decorator to
-         * be called with '<script>' which would not match the same rule since
-         * group 1 must not be empty, so it would be instead styled as PR_TAG by
-         * the generic tag rule.  The handler registered for the 'js' extension would
-         * then be called with 'foo()', and finally, the current decorator would
-         * be called with '<\/script>' which would not match the original rule and
-         * so the generic tag rule would identify it as a tag.
-         *
-         * Pattern must only match prefixes, and if it matches a prefix, then that
-         * match is considered a token with the same style.
-         *
-         * Context is applied to the last non-whitespace, non-comment token
-         * recognized.
-         *
-         * Shortcut is an optional string of characters, any of which, if the first
-         * character, gurantee that this pattern and only this pattern matches.
-         *
-         * @param shortcutStylePatterns patterns that always start with
-         *   a known character.  Must have a shortcut string.
-         * @param fallthroughStylePatterns patterns that will be tried in
-         *   order if the shortcut ones fail.  May have shortcuts.
-         */
-        protected CreateSimpleLexer(List<List<Object>> shortcutStylePatterns, List<List<Object>> fallthroughStylePatterns) throws Exception {
-            this.fallthroughStylePatterns = fallthroughStylePatterns;
-
-            List<List<Object>> allPatterns = new ArrayList<List<Object>>(shortcutStylePatterns);
-            allPatterns.addAll(fallthroughStylePatterns);
-            List<Pattern> allRegexs = new ArrayList<Pattern>();
-            Map<String, Object> regexKeys = new HashMap<String, Object>();
-            for (int i = 0, n = allPatterns.size(); i < n; ++i) {
-                List<Object> patternParts = allPatterns.get(i);
-                String shortcutChars = patternParts.size() > 3 ? (String) patternParts.get(3) : null;
-                if (shortcutChars != null) {
-                    for (int c = shortcutChars.length(); --c >= 0;) {
-                        shortcuts.put(shortcutChars.charAt(c), patternParts);
-                    }
-                }
-                Pattern regex = (Pattern) patternParts.get(1);
-                String k = regex.pattern();
-                if (regexKeys.get(k) == null) {
-                    allRegexs.add(regex);
-                    regexKeys.put(k, new Object());
-                }
-            }
-            allRegexs.add(Pattern.compile("[\0-\\uffff]"));
-            tokenizer = new CombinePrefixPattern().combinePrefixPattern(allRegexs);
-
-            nPatterns = fallthroughStylePatterns.size();
-        }
-
-        /**
-         * Lexes job.sourceCode and produces an output array job.decorations of
-         * style classes preceded by the position at which they start in
-         * job.sourceCode in order.
-         *
-         * @param job an object like <pre>{
-         *    sourceCode: {string} sourceText plain text,
-         *    basePos: {int} position of job.sourceCode in the larger chunk of
-         *        sourceCode.
-         * }</pre>
-         */
-        public void decorate(Job job) {
-            String sourceCode = job.getSourceCode();
-            int basePos = job.getBasePos();
-            /** Even entries are positions in source in ascending order.  Odd enties
-             * are style markers (e.g., PR_COMMENT) that run from that position until
-             * the end.
-             * @type {Array.<number|string>}
-             */
-            List<Object> decorations = new ArrayList<Object>(Arrays.asList(new Object[]{basePos, PR_PLAIN}));
-            int pos = 0;  // index into sourceCode
-            String[] tokens = Util.match(tokenizer, sourceCode, true);
-            Map<String, String> styleCache = new HashMap<String, String>();
-
-            for (int ti = 0, nTokens = tokens.length; ti < nTokens; ++ti) {
-                String token = tokens[ti];
-                String style = styleCache.get(token);
-                String[] match = null;
-
-                boolean isEmbedded;
-                if (style != null) {
-                    isEmbedded = false;
-                } else {
-                    List<Object> patternParts = shortcuts.get(token.charAt(0));
-                    if (patternParts != null) {
-                        match = Util.match((Pattern) patternParts.get(1), token, false);
-                        style = (String) patternParts.get(0);
-                    } else {
-                        for (int i = 0; i < nPatterns; ++i) {
-                            patternParts = fallthroughStylePatterns.get(i);
-                            match = Util.match((Pattern) patternParts.get(1), token, false);
-                            if (match.length != 0) {
-                                style = (String) patternParts.get(0);
-                                break;
-                            }
-                        }
-
-                        if (match.length == 0) {  // make sure that we make progress
-                            style = PR_PLAIN;
-                        }
-                    }
-
-                    isEmbedded = style != null && style.length() >= 5 && style.startsWith("lang-");
-                    if (isEmbedded && !(match.length > 1 && match[1] != null)) {
-                        isEmbedded = false;
-                        style = PR_SOURCE;
-                    }
-
-                    if (!isEmbedded) {
-                        styleCache.put(token, style);
-                    }
-                }
-
-                int tokenStart = pos;
-                pos += token.length();
-
-                if (!isEmbedded) {
-                    decorations.add(basePos + tokenStart);
-                    decorations.add(style);
-                } else {  // Treat group 1 as an embedded block of source code.
-                    String embeddedSource = match[1];
-                    int embeddedSourceStart = token.indexOf(embeddedSource);
-                    int embeddedSourceEnd = embeddedSourceStart + embeddedSource.length();
-                    if (match.length > 2 && match[2] != null) {
-                        // If embeddedSource can be blank, then it would match at the
-                        // beginning which would cause us to infinitely recurse on the
-                        // entire token, so we catch the right context in match[2].
-                        embeddedSourceEnd = token.length() - match[2].length();
-                        embeddedSourceStart = embeddedSourceEnd - embeddedSource.length();
-                    }
-                    String lang = style.substring(5);
-                    // Decorate the left of the embedded source
-                    appendDecorations(basePos + tokenStart,
-                            token.substring(0, embeddedSourceStart),
-                            this, decorations);
-                    // Decorate the embedded source
-                    appendDecorations(basePos + tokenStart + embeddedSourceStart,
-                            embeddedSource,
-                            langHandlerForExtension(lang, embeddedSource),
-                            decorations);
-                    // Decorate the right of the embedded section
-                    appendDecorations(basePos + tokenStart + embeddedSourceEnd,
-                            token.substring(embeddedSourceEnd),
-                            this, decorations);
-                }
-            }
-
-            job.setDecorations(Util.removeDuplicates(decorations, job.getSourceCode()));
-        }
-    }
-
-    /** returns a function that produces a list of decorations from source text.
-     *
-     * This code treats ", ', and ` as string delimiters, and \ as a string
-     * escape.  It does not recognize perl's qq() style strings.
-     * It has no special handling for double delimiter escapes as in basic, or
-     * the tripled delimiters used in python, but should work on those regardless
-     * although in those cases a single string literal may be broken up into
-     * multiple adjacent string literals.
-     *
-     * It recognizes C, C++, and shell style comments.
-     *
-     * @param options a set of optional parameters.
-     * @return a function that examines the source code
-     *     in the input job and builds the decoration list.
-     */
-    protected CreateSimpleLexer sourceDecorator(Map<String, Object> options) throws Exception {
-        List<List<Object>> shortcutStylePatterns = new ArrayList<List<Object>>();
-        List<List<Object>> fallthroughStylePatterns = new ArrayList<List<Object>>();
-        if (options.get("tripleQuotedStrings") != null) {
-            // '''multi-line-string''', 'single-line-string', and double-quoted
-            shortcutStylePatterns.add(Arrays.asList(new Object[]{PR_STRING,
-                        Pattern.compile("^(?:\\'\\'\\'(?:[^\\'\\\\]|\\\\[\\s\\S]|\\'{1,2}(?=[^\\']))*(?:\\'\\'\\'|$)|\\\"\\\"\\\"(?:[^\\\"\\\\]|\\\\[\\s\\S]|\\\"{1,2}(?=[^\\\"]))*(?:\\\"\\\"\\\"|$)|\\'(?:[^\\\\\\']|\\\\[\\s\\S])*(?:\\'|$)|\\\"(?:[^\\\\\\\"]|\\\\[\\s\\S])*(?:\\\"|$))"),
-                        null,
-                        "'\""}));
-        } else if (options.get("multiLineStrings") != null) {
-            // 'multi-line-string', "multi-line-string"
-            shortcutStylePatterns.add(Arrays.asList(new Object[]{PR_STRING,
-                        Pattern.compile("^(?:\\'(?:[^\\\\\\']|\\\\[\\s\\S])*(?:\\'|$)|\\\"(?:[^\\\\\\\"]|\\\\[\\s\\S])*(?:\\\"|$)|\\`(?:[^\\\\\\`]|\\\\[\\s\\S])*(?:\\`|$))"),
-                        null,
-                        "'\"`"}));
-        } else {
-            // 'single-line-string', "single-line-string"
-            shortcutStylePatterns.add(Arrays.asList(new Object[]{PR_STRING,
-                        Pattern.compile("^(?:\\'(?:[^\\\\\\'\r\n]|\\\\.)*(?:\\'|$)|\\\"(?:[^\\\\\\\"\r\n]|\\\\.)*(?:\\\"|$))"),
-                        null,
-                        "\"'"}));
-        }
-        if (options.get("verbatimStrings") != null) {
-            // verbatim-string-literal production from the C# grammar.  See issue 93.
-            fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_STRING,
-                        Pattern.compile("^@\\\"(?:[^\\\"]|\\\"\\\")*(?:\\\"|$)"),
-                        null}));
-        }
-        Object hc = options.get("hashComments");
-        if (hc != null) {
-            if (options.get("cStyleComments") != null) {
-                if ((hc instanceof Integer) && (Integer) hc > 1) {  // multiline hash comments
-                    shortcutStylePatterns.add(Arrays.asList(new Object[]{PR_COMMENT,
-                                Pattern.compile("^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)"),
-                                null,
-                                "#"}));
-                } else {
-                    // Stop C preprocessor declarations at an unclosed open comment
-                    shortcutStylePatterns.add(Arrays.asList(new Object[]{PR_COMMENT,
-                                Pattern.compile("^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\\b|[^\r\n]*)"),
-                                null,
-                                "#"}));
-                }
-                fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_STRING,
-                            Pattern.compile("^<(?:(?:(?:\\.\\.\\/)*|\\/?)(?:[\\w-]+(?:\\/[\\w-]+)+)?[\\w-]+\\.h|[a-z]\\w*)>"),
-                            null}));
-            } else {
-                shortcutStylePatterns.add(Arrays.asList(new Object[]{PR_COMMENT,
-                            Pattern.compile("^#[^\r\n]*"),
-                            null,
-                            "#"}));
-            }
-        }
-        if (options.get("cStyleComments") != null) {
-            fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_COMMENT,
-                        Pattern.compile("^\\/\\/[^\r\n]*"),
-                        null}));
-
-            fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_COMMENT,
-                        Pattern.compile("^\\/\\*[\\s\\S]*?(?:\\*\\/|$)"),
-                        null}));
-        }
-        if (options.get("regexLiterals") != null) {
-            /**
-             * @const
-             */
-            String REGEX_LITERAL =
-                    // A regular expression literal starts with a slash that is
-                    // not followed by * or / so that it is not confused with
-                    // comments.
-                    "/(?=[^/*])"
-                    // and then contains any number of raw characters,
-                    + "(?:[^/\\x5B\\x5C]"
-                    // escape sequences (\x5C),
-                    + "|\\x5C[\\s\\S]"
-                    // or non-nesting character sets (\x5B\x5D);
-                    + "|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+"
-                    // finally closed by a /.
-                    + "/";
-            fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-regex",
-                        Pattern.compile("^" + REGEXP_PRECEDER_PATTERN + "(" + REGEX_LITERAL + ")")}));
-        }
-
-        Pattern types = (Pattern) options.get("types");
-        if (types != null) {
-            fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_TYPE, types}));
-        }
-
-        if (options.get("keywords") != null) {
-            String keywords = ((String) options.get("keywords")).replaceAll("^ | $", "");
-            if (keywords.length() != 0) {
-                fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_KEYWORD,
-                            Pattern.compile("^(?:" + keywords.replaceAll("[\\s,]+", "|") + ")\\b"),
-                            null}));
-            }
-        }
-
-        shortcutStylePatterns.add(Arrays.asList(new Object[]{PR_PLAIN,
-                    Pattern.compile("^\\s+"),
-                    null,
-                    " \r\n\t" + Character.toString((char) 0xA0)
-                }));
-
-        // TODO(mikesamuel): recognize non-latin letters and numerals in idents
-        fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_LITERAL,
-                    Pattern.compile("^@[a-z_$][a-z_$@0-9]*", Pattern.CASE_INSENSITIVE),
-                    null}));
-        fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_TYPE,
-                    Pattern.compile("^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\\w+_t\\b)"),
-                    null}));
-        fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_PLAIN,
-                    Pattern.compile("^[a-z_$][a-z_$@0-9]*", Pattern.CASE_INSENSITIVE),
-                    null}));
-        fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_LITERAL,
-                    Pattern.compile("^(?:"
-                    // A hex number
-                    + "0x[a-f0-9]+"
-                    // or an octal or decimal number,
-                    + "|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)"
-                    // possibly in scientific notation
-                    + "(?:e[+\\-]?\\d+)?"
-                    + ')'
-                    // with an optional modifier like UL for unsigned long
-                    + "[a-z]*", Pattern.CASE_INSENSITIVE),
-                    null,
-                    "0123456789"}));
-        // Don't treat escaped quotes in bash as starting strings.  See issue 144.
-        fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_PLAIN,
-                    Pattern.compile("^\\\\[\\s\\S]?"),
-                    null}));
-        fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_PUNCTUATION,
-                    Pattern.compile("^.[^\\s\\w\\.$@\\'\\\"\\`\\/\\#\\\\]*"),
-                    null}));
-
-        return new CreateSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns);
-    }
-    /** Maps language-specific file extensions to handlers. */
-    protected Map<String, Object> langHandlerRegistry = new HashMap<String, Object>();
-
-    /** Register a language handler for the given file extensions.
-     * @param handler a function from source code to a list
-     *      of decorations.  Takes a single argument job which describes the
-     *      state of the computation.   The single parameter has the form
-     *      {@code {
-     *        sourceCode: {string} as plain text.
-     *        decorations: {Array.<number|string>} an array of style classes
-     *                     preceded by the position at which they start in
-     *                     job.sourceCode in order.
-     *                     The language handler should assigned this field.
-     *        basePos: {int} the position of source in the larger source chunk.
-     *                 All positions in the output decorations array are relative
-     *                 to the larger source chunk.
-     *      } }
-     * @param fileExtensions
-     */
-    protected void registerLangHandler(CreateSimpleLexer handler, List<String> fileExtensions) throws Exception {
-        for (int i = fileExtensions.size(); --i >= 0;) {
-            String ext = fileExtensions.get(i);
-            if (langHandlerRegistry.get(ext) == null) {
-                langHandlerRegistry.put(ext, handler);
-            } else {
-                throw new Exception("cannot override language handler " + ext);
-            }
-        }
-    }
-
-    /**
-     * Register language handler. The clazz will not be instantiated
-     * @param clazz the class of the language
-     * @throws Exception cannot instantiate the object using the class,
-     * or language handler with specified extension exist already
-     */
-    public void register(Class<? extends Lang> clazz) throws Exception {
-        if (clazz == null) {
-            throw new NullPointerException("argument 'clazz' cannot be null");
-        }
-        List<String> fileExtensions = getFileExtensionsFromClass(clazz);
-        for (int i = fileExtensions.size(); --i >= 0;) {
-            String ext = fileExtensions.get(i);
-            if (langHandlerRegistry.get(ext) == null) {
-                langHandlerRegistry.put(ext, clazz);
-            } else {
-                throw new Exception("cannot override language handler " + ext);
-            }
-        }
-    }
-
-    protected List<String> getFileExtensionsFromClass(Class<? extends Lang> clazz) throws Exception {
-        Method getExtensionsMethod = clazz.getMethod("getFileExtensions", (Class<?>[]) null);
-        return (List<String>) getExtensionsMethod.invoke(null, null);
-    }
-
-    /**
-     * Get the parser for the extension specified. 
-     * @param extension the file extension, if null, default parser will be returned
-     * @param source the source code
-     * @return the parser
-     */
-    public CreateSimpleLexer langHandlerForExtension(String extension, String source) {
-        if (!(extension != null && langHandlerRegistry.get(extension) != null)) {
-            // Treat it as markup if the first non whitespace character is a < and
-            // the last non-whitespace character is a >.
-            extension = Util.test(Pattern.compile("^\\s*<"), source)
-                    ? "default-markup"
-                    : "default-code";
-        }
-
-        Object handler = langHandlerRegistry.get(extension);
-        if (handler instanceof CreateSimpleLexer) {
-            return (CreateSimpleLexer) handler;
-        } else {
-            CreateSimpleLexer _simpleLexer;
-            try {
-                Lang _lang = ((Class<Lang>) handler).newInstance();
-                _simpleLexer = new CreateSimpleLexer(_lang.getShortcutStylePatterns(), _lang.getFallthroughStylePatterns());
-
-                List<Lang> extendedLangs = _lang.getExtendedLangs();
-                for (Lang _extendedLang : extendedLangs) {
-                    register(_extendedLang.getClass());
-                }
-
-                List<String> fileExtensions = getFileExtensionsFromClass((Class<Lang>) handler);
-                for (String _extension : fileExtensions) {
-                    langHandlerRegistry.put(_extension, _simpleLexer);
-                }
-            } catch (Exception ex) {
-                if (debug) {
-                    Logger.getLogger(Prettify.class.getName()).log(Level.SEVERE, null, ex);
-                }
-                return null;
-            }
-
-            return _simpleLexer;
-        }
-    }
-}
diff --git a/src/prettify/SyntaxHighlighter.java b/src/prettify/SyntaxHighlighter.java
index edba9b2..8c109d4 100644
--- a/src/prettify/SyntaxHighlighter.java
+++ b/src/prettify/SyntaxHighlighter.java
@@ -13,6 +13,11 @@
 // limitations under the License.
 package prettify;
 
+import prettify.parser.Job;
+import prettify.parser.Prettify;
+import prettify.gui.SyntaxHighlighterPane;
+import prettify.gui.JTextComponentRowHeader;
+import prettify.theme.Theme;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
@@ -22,249 +27,282 @@
 
 /**
  * The syntax highlighter.
+ * 
  * @author Chan Wai Shing <cws1989@gmail.com>
  */
 public class SyntaxHighlighter extends JScrollPane {
 
-    static {
-        // set debug mode
-        System.setProperty("PrettifyDebugMode", "false");
-    }
-    private static final long serialVersionUID = 1L;
-    /**
-     * The script text panel.
-     */
-    protected SyntaxHighlighterPane highlighter;
-    /**
-     * The gutter panel (line number).
-     */
-    protected JTextComponentRowHeader highlighterRowHeader;
-    /**
-     * The theme.
-     */
-    protected Theme theme;
-    /**
-     * The Prettify object.
-     */
-    protected Prettify prettify;
-    /**
-     * The content of the syntax highlighter, null if there is no content so far.
-     */
-    protected String content;
+  private static final long serialVersionUID = 1L;
+  /**
+   * The script text panel.
+   */
+  protected SyntaxHighlighterPane highlighter;
+  /**
+   * The gutter panel (line number).
+   */
+  protected JTextComponentRowHeader highlighterRowHeader;
+  /**
+   * The theme.
+   */
+  protected Theme theme;
+  /**
+   * The Prettify object.
+   */
+  protected Prettify prettify;
+  /**
+   * The content of the syntax highlighter, null if there is no content so far.
+   */
+  protected String content;
 
-    /**
-     * Constructor.
-     * @param theme the theme for the syntax highlighter
-     */
-    public SyntaxHighlighter(Theme theme) {
-        this(theme, new SyntaxHighlighterPane());
+  /**
+   * Constructor.
+   * 
+   * @param theme the theme for the syntax highlighter
+   */
+  public SyntaxHighlighter(Theme theme) {
+    this(theme, new SyntaxHighlighterPane());
+  }
+
+  /**
+   * Constructor.
+   * 
+   * @param theme the theme for the syntax highlighter
+   * @param highlighterPane the script text pane of the syntax highlighter
+   */
+  public SyntaxHighlighter(Theme theme, SyntaxHighlighterPane highlighterPane) {
+    super();
+
+    if (theme == null) {
+      throw new NullPointerException("argument 'theme' cannot be null");
+    }
+    if (highlighterPane == null) {
+      throw new NullPointerException("argument 'highlighterPane' cannot be null");
     }
 
-    /**
-     * Constructor.
-     * @param theme the theme for the syntax highlighter
-     * @param highlighterPane the script text pane of the syntax highlighter
-     */
-    public SyntaxHighlighter(Theme theme, SyntaxHighlighterPane highlighterPane) {
-        super();
+    this.theme = theme;
+    prettify = new Prettify();
 
-        if (theme == null) {
-            throw new NullPointerException("argument 'theme' cannot be null");
+    setBorder(null);
+
+    highlighter = highlighterPane;
+    highlighter.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0));
+    highlighter.setTheme(theme);
+    setViewportView(highlighter);
+
+    highlighterRowHeader = new JTextComponentRowHeader(this, highlighter);
+    theme.setTheme(highlighterRowHeader);
+    setRowHeaderView(highlighterRowHeader);
+  }
+
+  /**
+   * Re-render the script text pane. Invoke this when any change of setting that 
+   * affect the rendering was made.
+   * This will re-parse the content and set the style.
+   */
+  protected void render() {
+    if (content != null) {
+      // stop the change listener on the row header to speed up rendering
+      highlighterRowHeader.setListenToDocumentUpdate(false);
+      Job job = new Job(0, content);
+      prettify.langHandlerForExtension(null, content).decorate(job);
+      highlighter.setStyle(job.getDecorations());
+      // resume the change listener on the row header
+      highlighterRowHeader.setListenToDocumentUpdate(true);
+      // notify the row header to update its information related to the SyntaxHighlighterPane
+      // need to call this because we have stopped the change listener of the row header in previous code
+      highlighterRowHeader.checkPanelSize();
+    }
+  }
+
+  /**
+   * Get the SyntaxHighlighterPane (the script text panel).
+   * <b>Note: Normally should not operate on the SyntaxHighlighterPane directly.</b>
+   * 
+   * @return the SyntaxHighlighterPane
+   */
+  public SyntaxHighlighterPane getHighlighter() {
+    return highlighter;
+  }
+
+  /**
+   * Get the JTextComponentRowHeader, the line number panel.
+   * <b>Note: Normally should not operate on the JTextComponentRowHeader 
+   * directly.</b>
+   * 
+   * @return the JTextComponentRowHeader
+   */
+  public JTextComponentRowHeader getHighlighterRowHeader() {
+    return highlighterRowHeader;
+  }
+
+  /**
+   * Get current theme.
+   * 
+   * @return the current theme
+   */
+  public Theme getTheme() {
+    return theme;
+  }
+
+  /**
+   * Set the theme.
+   * Setting the theme will not re-parse the content, but will clear and apply 
+   * the new theme on the script text pane.
+   * 
+   * @param theme the theme
+   */
+  public void setTheme(Theme theme) {
+    if (theme == null) {
+      throw new NullPointerException("argument 'theme' cannot be null");
+    }
+
+    if (!this.theme.equals(theme)) {
+      this.theme = theme;
+      highlighter.setTheme(theme);
+      theme.setTheme(highlighterRowHeader);
+    }
+  }
+
+  /**
+   * Set the line number of the first line. E.g. if set 10, the line number will 
+   * start count from 10 instead of 1.
+   * 
+   * @param firstLine the line number of the first line
+   */
+  public void setFirstLine(int firstLine) {
+    highlighterRowHeader.setLineNumberOffset(firstLine - 1);
+    highlighter.setLineNumberOffset(firstLine - 1);
+  }
+
+  /**
+   * Get the list of highlighted lines.
+   * 
+   * @return a copy of the list
+   */
+  public List<Integer> getHighlightedLineList() {
+    return highlighter.getHighlightedLineList();
+  }
+
+  /**
+   * Set highlighted lines. Note that this will clear all previous recorded 
+   * highlighted lines.
+   * 
+   * @param highlightedLineList the list that contain the highlighted lines, 
+   * null means highlight no lines
+   */
+  public void setHighlightedLineList(List<Integer> highlightedLineList) {
+    highlighterRowHeader.setHighlightedLineList(highlightedLineList);
+    highlighter.setHighlightedLineList(highlightedLineList);
+  }
+
+  /**
+   * Add highlighted line.
+   * 
+   * @param lineNumber the line number to highlight
+   */
+  public void addHighlightedLine(int lineNumber) {
+    highlighterRowHeader.addHighlightedLine(lineNumber);
+    highlighter.addHighlightedLine(lineNumber);
+  }
+
+  /**
+   * Check the visibility of the gutter.
+   * 
+   * @return true if the gutter is visible, false if not
+   */
+  public boolean isGutterVisible() {
+    return getRowHeader() != null && getRowHeader().getView() != null;
+  }
+
+  /**
+   * Set the visibility of the gutter.
+   * 
+   * @param visible true to make visible, false to hide it
+   */
+  public void setGutterVisible(boolean visible) {
+    if (visible) {
+      setRowHeaderView(highlighterRowHeader);
+      highlighter.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0));
+    } else {
+      setRowHeaderView(null);
+      highlighter.setBorder(null);
+    }
+  }
+
+  /**
+   * Check the status of the mouse-over highlight effect. Default is on.
+   * 
+   * @return true if turned on, false if turned off
+   */
+  public boolean isHighlightOnMouseOver() {
+    return highlighter.isHighlightOnMouseOver();
+  }
+
+  /**
+   * Set turn on the mouse-over highlight effect or not.
+   * If set true, there will be a highlight effect on the line that the mouse 
+   * cursor currently is pointing on (on the script text panel only, not on the 
+   * line number panel).
+   * 
+   * @param highlightWhenMouseOver true to turn on, false to turn off
+   */
+  public void setHighlightOnMouseOver(boolean highlightWhenMouseOver) {
+    highlighter.setHighlightOnMouseOver(highlightWhenMouseOver);
+  }
+
+  /**
+   * Set the content of the syntax highlighter. Better set it last after setting 
+   * all other settings.
+   * 
+   * @param file the file to read
+   * 
+   * @throws IOException error occurred when reading the file
+   */
+  public void setContent(File file) throws IOException {
+    setContent(readFile(file));
+  }
+
+  /**
+   * Set the content of the syntax highlighter. It is better to set other 
+   * settings first and set this the last.
+   * 
+   * @param content the content to set, null means no content
+   */
+  public void setContent(String content) {
+    this.content = content;
+    highlighter.setContent(content);
+    render();
+  }
+
+  /**
+   * Get the string content of a file.
+   * 
+   * @param file the file to retrieve the content from
+   * 
+   * @return the string content
+   * 
+   * @throws IOException error occured when reading the file
+   */
+  protected static String readFile(File file) throws IOException {
+    if (file == null) {
+      throw new IOException("argument 'file' cannot be null");
+    }
+
+    byte[] buffer = new byte[(int) file.length()];
+
+    FileInputStream fileIn = null;
+    try {
+      fileIn = new FileInputStream(file);
+      fileIn.read(buffer);
+    } finally {
+      if (fileIn != null) {
+        try {
+          fileIn.close();
+        } catch (IOException ex) {
         }
-        if (highlighterPane == null) {
-            throw new NullPointerException("argument 'highlighterPane' cannot be null");
-        }
-
-        this.theme = theme;
-        prettify = new Prettify();
-
-        setBorder(null);
-
-        highlighter = highlighterPane;
-        highlighter.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0));
-        highlighter.setTheme(theme);
-        setViewportView(highlighter);
-
-        highlighterRowHeader = new JTextComponentRowHeader(this, highlighter);
-        theme.setTheme(highlighterRowHeader);
-        setRowHeaderView(highlighterRowHeader);
+      }
     }
 
-    /**
-     * Re-render the script text pane. Invoke this when any change of setting that affect the rendering was made.
-     * This will re-parse the content and set the style.
-     */
-    protected void render() {
-        if (content != null) {
-            // stop the change listener on the row header to speed up rendering
-            highlighterRowHeader.setListenToDocumentUpdate(false);
-            Job job = new Job(0, content);
-            prettify.langHandlerForExtension(null, content).decorate(job);
-            highlighter.setStyle(job.getDecorations());
-            // resume the change listener on the row header
-            highlighterRowHeader.setListenToDocumentUpdate(true);
-            // notify the row header to update its information related to the SyntaxHighlighterPane
-            // need to call this because we have stopped the change listener of the row header in previous code
-            highlighterRowHeader.checkPanelSize();
-        }
-    }
-
-    /**
-     * Get the SyntaxHighlighterPane (the script text panel).
-     * <p><b>Note: Normally should not operate on the SyntaxHighlighterPane directly.</b></p>
-     * @return the SyntaxHighlighterPane
-     */
-    public SyntaxHighlighterPane getHighlighter() {
-        return highlighter;
-    }
-
-    /**
-     * Get the JTextComponentRowHeader, the line number panel.
-     * <p><b>Note: Normally should not operate on the JTextComponentRowHeader directly.</b></p>
-     * @return the JTextComponentRowHeader
-     */
-    public JTextComponentRowHeader getHighlighterRowHeader() {
-        return highlighterRowHeader;
-    }
-
-    /**
-     * Get current theme.
-     * @return the current theme
-     */
-    public Theme getTheme() {
-        return theme;
-    }
-
-    /**
-     * Set the theme.
-     * <p>
-     * Setting the theme will not re-parse the content, but will clear and apply the new theme on the script text pane.
-     * </p>
-     * @param theme the theme
-     */
-    public void setTheme(Theme theme) {
-        if (theme == null) {
-            throw new NullPointerException("argument 'theme' cannot be null");
-        }
-        if (!this.theme.equals(theme)) {
-            this.theme = theme;
-            highlighter.setTheme(theme);
-            theme.setTheme(highlighterRowHeader);
-        }
-    }
-
-    /**
-     * Set the line number of the first line. E.g. if set 10, the line number will start count from 10 instead of 1.
-     * @param firstLine the line number of the first line
-     */
-    public void setFirstLine(int firstLine) {
-        highlighterRowHeader.setLineNumberOffset(firstLine - 1);
-        highlighter.setLineNumberOffset(firstLine - 1);
-    }
-
-    /**
-     * Get the list of highlighted lines.
-     * @return a copy of the list
-     */
-    public List<Integer> getHighlightedLineList() {
-        return highlighter.getHighlightedLineList();
-    }
-
-    /**
-     * Set highlighted lines. Note that this will clear all previous recorded highlighted lines.
-     * @param highlightedLineList the list that contain the highlighted lines
-     */
-    public void setHighlightedLineList(List<Integer> highlightedLineList) {
-        highlighterRowHeader.setHighlightedLineList(highlightedLineList);
-        highlighter.setHighlightedLineList(highlightedLineList);
-    }
-
-    /**
-     * Add highlighted line.
-     * @param lineNumber the line number to highlight
-     */
-    public void addHighlightedLine(int lineNumber) {
-        highlighterRowHeader.addHighlightedLine(lineNumber);
-        highlighter.addHighlightedLine(lineNumber);
-    }
-
-    /**
-     * Get the visibility of the gutter.
-     * @return true if the gutter is visible, false if not
-     */
-    public boolean isGutterVisible() {
-        return getRowHeader() != null && getRowHeader().getView() != null;
-    }
-
-    /**
-     * Set the visibility of the gutter.
-     * @param visible true to make visible, false to hide it
-     */
-    public void setGutterVisible(boolean visible) {
-        if (visible) {
-            setRowHeaderView(highlighterRowHeader);
-            highlighter.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0));
-        } else {
-            setRowHeaderView(null);
-            highlighter.setBorder(null);
-        }
-    }
-
-    /**
-     * Get the status of the mouse-over highlight effect. Default is on.
-     * @return true if turned on, false if turned off
-     */
-    public boolean isHighlightOnMouseOver() {
-        return highlighter.isHighlightOnMouseOver();
-    }
-
-    /**
-     * Set turn on the mouse-over highlight effect or not.
-     * If set true, there will be a highlight effect on the line that the mouse cursor currently is pointing on (on the script text panel only, not on the line number panel).
-     * @param highlightWhenMouseOver true to turn on, false to turn off
-     */
-    public void setHighlightOnMouseOver(boolean highlightWhenMouseOver) {
-        highlighter.setHighlightOnMouseOver(highlightWhenMouseOver);
-    }
-
-    /**
-     * Set the content of the syntax highlighter. Better set it last after setting all other settings.
-     * @param file the file to read 
-     */
-    public void setContent(File file) throws IOException {
-        setContent(readFile(file));
-    }
-
-    /**
-     * Set the content of the syntax highlighter. It is better to set other settings first and set this the last.
-     * @param content the content to set
-     */
-    public void setContent(String content) {
-        this.content = content;
-        highlighter.setContent(content);
-        render();
-    }
-
-    /**
-     * Get the string content of a file.
-     * @param file the file to retrieve the content from
-     * @return the string content
-     * @throws IOException error occured, either it is not a valid file or failed to read the file
-     */
-    protected static String readFile(File file) throws IOException {
-        if (file == null) {
-            throw new IOException("argument 'file' cannot be null");
-        }
-        if (!file.isFile()) {
-            throw new IOException("It is not a file.");
-        }
-
-        byte[] buffer = new byte[(int) file.length()];
-
-        FileInputStream fileIn = new FileInputStream(file);
-        fileIn.read(buffer);
-        fileIn.close();
-
-        return new String(buffer);
-    }
+    return new String(buffer);
+  }
 }
diff --git a/src/prettify/SyntaxHighlighterPane.java b/src/prettify/SyntaxHighlighterPane.java
deleted file mode 100644
index 0ad5d3d..0000000
--- a/src/prettify/SyntaxHighlighterPane.java
+++ /dev/null
@@ -1,502 +0,0 @@
-// Copyright (C) 2011 Chan Wai Shing
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-package prettify;
-
-import java.awt.Color;
-import java.awt.Font;
-import java.awt.FontMetrics;
-import java.awt.Graphics;
-import java.awt.Rectangle;
-import java.awt.Shape;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-import java.awt.event.MouseMotionListener;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import javax.swing.JTextPane;
-import javax.swing.text.AbstractDocument;
-import javax.swing.text.BadLocationException;
-import javax.swing.text.BoxView;
-import javax.swing.text.ComponentView;
-import javax.swing.text.DefaultStyledDocument;
-import javax.swing.text.Element;
-import javax.swing.text.Highlighter;
-import javax.swing.text.IconView;
-import javax.swing.text.JTextComponent;
-import javax.swing.text.LabelView;
-import javax.swing.text.ParagraphView;
-import javax.swing.text.SimpleAttributeSet;
-import javax.swing.text.StyleConstants;
-import javax.swing.text.StyledEditorKit;
-import javax.swing.text.View;
-import javax.swing.text.ViewFactory;
-
-/**
- * The text pane for displaying the script text.
- * @author Chan Wai Shing <cws1989@gmail.com>
- */
-public class SyntaxHighlighterPane extends JTextPane {
-
-    /**
-     * Indicate whether it is in debug mode or not.
-     */
-    protected final static boolean debug;
-
-    static {
-        String debugMode = System.getProperty("PrettifyDebugMode");
-        debug = debugMode == null || !debugMode.equals("true") ? false : true;
-    }
-    private static final long serialVersionUID = 1L;
-    /**
-     * The line number offset. E.g. set offset to 9 will make the first line number to appear at line 1 + 9 = 10
-     */
-    private int lineNumberOffset;
-    /**
-     * The background color of the highlighted line. Default is black.
-     */
-    private Color highlightedBackground;
-    /**
-     * Indicator that indicate to turn on the mouse-over highlight effect or not. See {@link #setHighlightOnMouseOver(boolean)}.
-     */
-    private boolean highlightWhenMouseOver;
-    /**
-     * The list of line numbers that indicate which lines are needed to be highlighted.
-     */
-    protected final List<Integer> highlightedLineList;
-    /**
-     * The highlighter painter used to do the highlight line effect.
-     */
-    protected Highlighter.HighlightPainter highlightPainter;
-    /**
-     * The theme.
-     */
-    protected Theme theme;
-    /**
-     * The style list. see {@link #setStyle(java.util.List)}.
-     */
-    protected List<Object> styleList;
-    /**
-     * Record the mouse cursor is currently pointing at which line of the document. -1 means not any line.
-     * It is used internally.
-     */
-    protected int mouseOnLine;
-
-    public SyntaxHighlighterPane() {
-        super();
-
-        setEditable(false);
-        //<editor-fold defaultstate="collapsed" desc="editor kit">
-        setEditorKit(new StyledEditorKit() {
-
-            private static final long serialVersionUID = 1L;
-
-            @Override
-            public ViewFactory getViewFactory() {
-                return new ViewFactory() {
-
-                    @Override
-                    public View create(Element elem) {
-                        String kind = elem.getName();
-                        if (kind != null) {
-                            if (kind.equals(AbstractDocument.ContentElementName)) {
-                                return new LabelView(elem) {
-
-                                    @Override
-                                    public int getBreakWeight(int axis, float pos, float len) {
-                                        return 0;
-                                    }
-                                };
-                            } else if (kind.equals(AbstractDocument.ParagraphElementName)) {
-                                return new ParagraphView(elem) {
-
-                                    @Override
-                                    public int getBreakWeight(int axis, float pos, float len) {
-                                        return 0;
-                                    }
-                                };
-                            } else if (kind.equals(AbstractDocument.SectionElementName)) {
-                                return new BoxView(elem, View.Y_AXIS);
-                            } else if (kind.equals(StyleConstants.ComponentElementName)) {
-                                return new ComponentView(elem) {
-
-                                    @Override
-                                    public int getBreakWeight(int axis, float pos, float len) {
-                                        return 0;
-                                    }
-                                };
-                            } else if (kind.equals(StyleConstants.IconElementName)) {
-                                return new IconView(elem);
-                            }
-                        }
-                        return new LabelView(elem) {
-
-                            @Override
-                            public int getBreakWeight(int axis, float pos, float len) {
-                                return 0;
-                            }
-                        };
-                    }
-                };
-            }
-        });
-        //</editor-fold>
-
-        lineNumberOffset = 0;
-
-        //<editor-fold defaultstate="collapsed" desc="highlighter painter">
-        highlightedBackground = Color.black;
-        highlightWhenMouseOver = true;
-        highlightedLineList = new ArrayList<Integer>();
-
-        highlightPainter = new Highlighter.HighlightPainter() {
-
-            @Override
-            public void paint(Graphics g, int p0, int p1, Shape bounds, JTextComponent c) {
-                if (c.getParent() == null) {
-                    return;
-                }
-
-                // get the Y-axis value of the visible area of the text component
-                int startY = Math.abs(c.getY());
-                int endY = startY + c.getParent().getHeight();
-
-                FontMetrics textPaneFontMetrics = g.getFontMetrics(getFont());
-                int textPaneFontHeight = textPaneFontMetrics.getHeight();
-
-                int largerestLineNumber = c.getDocument().getDefaultRootElement().getElementCount();
-
-                g.setColor(highlightedBackground);
-
-                // draw the highlight background to the highlighted line
-                synchronized (highlightedLineList) {
-                    for (Integer lineNumber : highlightedLineList) {
-                        if (lineNumber > largerestLineNumber + lineNumberOffset) {
-                            // skip those line number that out of range
-                            continue;
-                        }
-                        // get the Y-axis value of this highlighted line
-                        int _y = Math.max(0, textPaneFontHeight * (lineNumber - lineNumberOffset - 1));
-                        if (_y > endY || _y + textPaneFontHeight < startY) {
-                            // this line is out of visible area, skip it
-                            continue;
-                        }
-                        // draw the highlighted background
-                        g.fillRect(0, _y, c.getWidth(), textPaneFontHeight);
-                    }
-                }
-
-                // draw the mouse-over-highlight effect
-                if (mouseOnLine != -1) {
-                    if (mouseOnLine <= largerestLineNumber + lineNumberOffset) {
-                        int _y = Math.max(0, textPaneFontHeight * (mouseOnLine - lineNumberOffset - 1));
-                        if (_y < endY && _y + textPaneFontHeight > startY) {
-                            // the line is within the range of visible area
-                            g.fillRect(0, _y, c.getWidth(), textPaneFontHeight);
-                        }
-                    }
-                }
-            }
-        };
-        try {
-            getHighlighter().addHighlight(0, 0, highlightPainter);
-        } catch (BadLocationException ex) {
-            if (debug) {
-                Logger.getLogger(SyntaxHighlighterPane.class.getName()).log(Level.SEVERE, null, ex);
-            }
-        }
-        //</editor-fold>
-
-        mouseOnLine = -1;
-
-        //<editor-fold defaultstate="collapsed" desc="mouse listener">
-        addMouseListener(new MouseAdapter() {
-
-            @Override
-            public void mouseExited(MouseEvent e) {
-                if (!highlightWhenMouseOver) {
-                    return;
-                }
-                mouseOnLine = -1;
-                repaint();
-            }
-        });
-        addMouseMotionListener(new MouseMotionListener() {
-
-            @Override
-            public void mouseDragged(MouseEvent e) {
-            }
-
-            @Override
-            public void mouseMoved(MouseEvent e) {
-                if (!highlightWhenMouseOver) {
-                    return;
-                }
-
-                Element defaultRootElement = getDocument().getDefaultRootElement();
-                // get the position of the document the mouse cursor is pointing
-                int documentOffsetStart = viewToModel(e.getPoint());
-
-                // the line number that the mouse cursor is currently pointing
-                int lineNumber = documentOffsetStart == -1 ? -1 : defaultRootElement.getElementIndex(documentOffsetStart) + 1 + lineNumberOffset;
-                if (lineNumber == defaultRootElement.getElementCount()) {
-                    // if the line number got is the last line, check if the cursor is actually on the line or already below the line
-                    try {
-                        Rectangle rectangle = modelToView(documentOffsetStart);
-                        if (e.getY() > rectangle.y + rectangle.height) {
-                            lineNumber = -1;
-                        }
-                    } catch (BadLocationException ex) {
-                        if (debug) {
-                            Logger.getLogger(SyntaxHighlighterPane.class.getName()).log(Level.SEVERE, null, ex);
-                        }
-                    }
-                }
-
-                // only repaint when the line number changed
-                if (mouseOnLine != lineNumber) {
-                    mouseOnLine = lineNumber;
-                    repaint();
-                }
-            }
-        });
-        //</editor-fold>
-    }
-
-    @Override
-    public void setHighlighter(Highlighter highlighter) {
-        if (highlightPainter != null) {
-            getHighlighter().removeHighlight(highlightPainter);
-            try {
-                highlighter.addHighlight(0, 0, highlightPainter);
-            } catch (BadLocationException ex) {
-                if (debug) {
-                    Logger.getLogger(SyntaxHighlighterPane.class.getName()).log(Level.SEVERE, null, ex);
-                }
-            }
-        }
-        super.setHighlighter(highlighter);
-    }
-
-    /**
-     * Set the content of the syntax highlighter. It is better to set other settings first and set this the last.
-     * @param content the content to set
-     */
-    public void setContent(String content) {
-        String newContent = content == null ? "" : content;
-        DefaultStyledDocument document = (DefaultStyledDocument) getDocument();
-
-        try {
-            document.remove(0, document.getLength());
-            if (theme != null) {
-                document.insertString(0, newContent, theme.getPlain().getAttributeSet());
-            } else {
-                document.insertString(0, newContent, new SimpleAttributeSet());
-            }
-        } catch (BadLocationException ex) {
-            if (debug) {
-                Logger.getLogger(SyntaxHighlighterPane.class.getName()).log(Level.SEVERE, null, ex);
-            }
-        }
-
-        setCaretPosition(0);
-
-        // clear the style list
-        styleList = null;
-    }
-
-    /**
-     * Apply the list of style to the script text pane.
-     * @param styleList the style list
-     */
-    public void setStyle(List<Object> styleList) {
-        if (styleList == null) {
-            this.styleList = new ArrayList<Object>();
-            return;
-        }
-        this.styleList = new ArrayList<Object>(styleList);
-
-        if (theme == null) {
-            return;
-        }
-
-        DefaultStyledDocument document = (DefaultStyledDocument) getDocument();
-        // clear all the existing style
-        document.setCharacterAttributes(0, document.getLength(), theme.getPlain().getAttributeSet(), true);
-
-        Integer startPos = 0, endPos = 0;
-        // apply style according to the style list
-        for (int i = 0, iEnd = styleList.size(); i < iEnd; i += 2) {
-            SimpleAttributeSet attributeSet = theme.getStyle((String) styleList.get(i + 1)).getAttributeSet();
-            endPos = i + 2 < iEnd ? (Integer) styleList.get(i + 2) : document.getLength();
-            startPos = (Integer) styleList.get(i);
-            document.setCharacterAttributes(startPos, endPos - startPos, attributeSet, true);
-        }
-
-        repaint();
-    }
-
-    /**
-     * Get current theme.
-     * @return the current theme
-     */
-    public Theme getTheme() {
-        return theme;
-    }
-
-    /**
-     * Set the theme.
-     * @param theme the theme
-     */
-    public void setTheme(Theme theme) {
-        if (theme == null) {
-            throw new NullPointerException("argument 'theme' cannot be null");
-        }
-        this.theme = theme;
-
-        setFont(theme.getFont());
-        setBackground(theme.getBackground());
-        setHighlightedBackground(theme.getHighlightedBackground());
-
-        if (styleList != null) {
-            setStyle(styleList);
-        }
-    }
-
-    /**
-     * Get the line number offset. E.g. set offset to 9 will make the first line number to appear at line 1 + 9 = 10
-     * @return the offset
-     */
-    public int getLineNumberOffset() {
-        return lineNumberOffset;
-    }
-
-    /**
-     * Set the line number offset. E.g. set offset to 9 will make the first line number to appear at line 1 + 9 = 10
-     * @param offset the offset
-     */
-    public void setLineNumberOffset(int offset) {
-        lineNumberOffset = Math.max(lineNumberOffset, offset);
-        repaint();
-    }
-
-    /**
-     * Get the color of the highlighted background. Default is black.
-     * @return the color
-     */
-    public Color getHighlightedBackground() {
-        return highlightedBackground;
-    }
-
-    /**
-     * Set the color of the highlighted background. Default is black.
-     * @param highlightedBackground the color
-     */
-    public void setHighlightedBackground(Color highlightedBackground) {
-        if (highlightedBackground == null) {
-            throw new NullPointerException("argument 'highlightedBackground' cannot be null");
-        }
-        this.highlightedBackground = highlightedBackground;
-        repaint();
-    }
-
-    /**
-     * Get the status of the mouse-over highlight effect. Default is on.
-     * @return true if turned on, false if turned off
-     */
-    public boolean isHighlightOnMouseOver() {
-        return highlightWhenMouseOver;
-    }
-
-    /**
-     * Set turn on the mouse-over highlight effect or not. Default is on.
-     * If set true, there will be a highlight effect on the line that the mouse cursor currently is pointing on (on the script text panel only, not on the line number panel).
-     * @param highlightWhenMouseOver true to turn on, false to turn off
-     */
-    public void setHighlightOnMouseOver(boolean highlightWhenMouseOver) {
-        this.highlightWhenMouseOver = highlightWhenMouseOver;
-        if (!highlightWhenMouseOver) {
-            mouseOnLine = -1;
-        }
-        repaint();
-    }
-
-    /**
-     * Get the list of highlighted lines.
-     * @return a copy of the list
-     */
-    public List<Integer> getHighlightedLineList() {
-        List<Integer> returnList;
-        synchronized (highlightedLineList) {
-            returnList = new ArrayList<Integer>(highlightedLineList);
-        }
-        return returnList;
-    }
-
-    /**
-     * Set highlighted lines. Note that this will clear all previous recorded highlighted lines.
-     * @param highlightedLineList the list that contain the highlighted lines
-     */
-    public void setHighlightedLineList(List<Integer> highlightedLineList) {
-        synchronized (this.highlightedLineList) {
-            this.highlightedLineList.clear();
-            if (highlightedLineList != null) {
-                this.highlightedLineList.addAll(highlightedLineList);
-            }
-        }
-        repaint();
-    }
-
-    /**
-     * Add highlighted line.
-     * @param lineNumber the line number to highlight
-     */
-    public void addHighlightedLine(int lineNumber) {
-        highlightedLineList.add(lineNumber);
-        repaint();
-    }
-
-    /**
-     * Set the <code>font</code> according to <code>bold</code> and <code>italic</code>.
-     * @param font the font to set
-     * @param bold true to set bold, false not
-     * @param italic true to set italic, false not
-     * @return the font with bold and italic changed, or null if the input <code>font</code> is null
-     */
-    protected static Font setFont(Font font, boolean bold, boolean italic) {
-        if (font == null) {
-            return null;
-        }
-        if ((font.getStyle() & Font.ITALIC) != 0) {
-            if (!bold) {
-                return font.deriveFont(font.getStyle() ^ Font.BOLD);
-            }
-        } else {
-            if (bold) {
-                return font.deriveFont(font.getStyle() | Font.BOLD);
-            }
-        }
-        if ((font.getStyle() & Font.ITALIC) != 0) {
-            if (!italic) {
-                return font.deriveFont(font.getStyle() ^ Font.ITALIC);
-            }
-        } else {
-            if (italic) {
-                return font.deriveFont(font.getStyle() | Font.ITALIC);
-            }
-        }
-        return font;
-    }
-}
diff --git a/src/prettify/Theme.java b/src/prettify/Theme.java
deleted file mode 100644
index 5c85903..0000000
--- a/src/prettify/Theme.java
+++ /dev/null
@@ -1,775 +0,0 @@
-// Copyright (C) 2011 Chan Wai Shing
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-package prettify;
-
-import java.awt.Color;
-import java.awt.Font;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import javax.swing.text.SimpleAttributeSet;
-import javax.swing.text.StyleConstants;
-
-/**
- * Theme for the Java Prettify.
- * <p>To make a new theme, either extending this class of initiate this class and set the parameter by the setter.
- * For the default value, please refer to the constructor.</p>
- * @author Chan Wai Shing <cws1989@gmail.com>
- */
-public class Theme {
-
-    /**
-     * Indicate whether it is in debug mode or not.
-     */
-    protected final static boolean debug;
-
-    static {
-        String debugMode = System.getProperty("PrettifyDebugMode");
-        debug = debugMode == null || !debugMode.equals("true") ? false : true;
-    }
-    /**
-     * The font of the script text.
-     */
-    protected Font font;
-    /**
-     * The background color of the script text area.
-     */
-    protected Color background;
-    /**
-     * The background color of the highlighted line of script text.
-     */
-    protected Color highlightedBackground;
-    /**
-     * Gutter (line number column on the left)
-     */
-    /**
-     * The color of the gutter text.
-     */
-    protected Color gutterText;
-    /**
-     * The color of the border that joint the gutter and the script text panel.
-     */
-    protected Color gutterBorderColor;
-    /**
-     * The width of the border that joint the gutter and the script text panel.
-     */
-    protected int gutterBorderWidth;
-    /**
-     * The font of the gutter text.
-     */
-    protected Font gutterTextFont;
-    /**
-     * The minimum padding from 'the leftmost of the line number text' to 'the left margin'.
-     */
-    protected int gutterTextPaddingLeft;
-    /**
-     * The minimum padding from 'the rightmost of the line number text' to 'the right margin' (not to the gutter border).
-     */
-    protected int gutterTextPaddingRight;
-    protected Style string;
-    protected Style keyword;
-    protected Style comment;
-    protected Style type;
-    protected Style literal;
-    protected Style punctuation;
-    protected Style plain;
-    protected Style tag;
-    protected Style declaration;
-    protected Style source;
-    protected Style attributeName;
-    protected Style attributeValue;
-    protected Style noCode;
-    protected Style openBracket;
-    protected Style closeBracket;
-    protected Style variable;
-    protected Style function;
-
-    /**
-     * Constructor.<br />
-     * <p>
-     * <b>Default value:</b><br />
-     * <ul>
-     * <li>font: Consolas 12pt</li>
-     * <li>background: white</li>
-     * <li>gutter text: black</li>
-     * <li>gutter border: R: 184, G: 184, B: 184</li>
-     * <li>gutter border width: 3px</li>
-     * <li>gutter text font: Consolas 12pt</li>
-     * <li>gutter text padding-left: 7px</li>
-     * <li>gutter text padding-right: 7px</li>
-     * <li>plain, comments, string, keyword, preprocessor, variable, value, functions, constants, script, scriptBackground, color1, color2, color3: no style set</li>
-     * </ul>
-     * </p>
-     */
-    public Theme() {
-        font = new Font("Consolas", Font.PLAIN, 12);
-        background = Color.white;
-
-        highlightedBackground = Color.gray;
-
-        gutterText = Color.black;
-        gutterBorderColor = new Color(184, 184, 184);
-        gutterBorderWidth = 3;
-        gutterTextFont = new Font("Consolas", Font.PLAIN, 12);
-        gutterTextPaddingLeft = 7;
-        gutterTextPaddingRight = 7;
-
-        string = new Style();
-        keyword = new Style();
-        comment = new Style();
-        type = new Style();
-        literal = new Style();
-        punctuation = new Style();
-        plain = new Style();
-        tag = new Style();
-        declaration = new Style();
-        source = new Style();
-        attributeName = new Style();
-        attributeValue = new Style();
-        noCode = new Style();
-        openBracket = new Style();
-        closeBracket = new Style();
-        variable = new Style();
-        function = new Style();
-    }
-
-    /**
-     * Apply the theme to the row header panel.
-     * @param rowHeader the row header to apply theme on
-     */
-    public void setTheme(JTextComponentRowHeader rowHeader) {
-        rowHeader.setBackground(background);
-        rowHeader.setHighlightedColor(background);
-
-        rowHeader.setForeground(gutterText);
-        rowHeader.setBorderColor(gutterBorderColor);
-        rowHeader.setBorderWidth(gutterBorderWidth);
-        rowHeader.setFont(gutterTextFont);
-        rowHeader.setPaddingLeft(gutterTextPaddingLeft);
-        rowHeader.setPaddingRight(gutterTextPaddingRight);
-    }
-
-    /**
-     * Get the {@link prettify.Theme.Style} by keyword.
-     * @param key the keyword, possible values: plain, comments, string, keyword, preprocessor, variable, value, functions, constants, script, scriptBackground, color1, color2 or color3
-     * @return the {@link prettify.Theme.Style} related to the {@code key}; if the style related to the <code>key</code> not exist, the style of 'plain' will return.
-     */
-    public Style getStyle(String key) {
-        if (key.equals("str")) {
-            return string;
-        } else if (key.equals("kwd")) {
-            return keyword;
-        } else if (key.equals("com")) {
-            return comment;
-        } else if (key.equals("typ")) {
-            return type;
-        } else if (key.equals("lit")) {
-            return literal;
-        } else if (key.equals("pun")) {
-            return punctuation;
-        } else if (key.equals("pln")) {
-            return plain;
-        } else if (key.equals("tag")) {
-            return tag;
-        } else if (key.equals("dec")) {
-            return declaration;
-        } else if (key.equals("src")) {
-            return source;
-        } else if (key.equals("atn")) {
-            return attributeName;
-        } else if (key.equals("atv")) {
-            return attributeValue;
-        } else if (key.equals("nocode")) {
-            return noCode;
-        } else if (key.equals("opn")) {
-            return openBracket;
-        } else if (key.equals("clo")) {
-            return closeBracket;
-        } else if (key.equals("var")) {
-            return variable;
-        } else if (key.equals("fun")) {
-            return function;
-        } else {
-            // key not exist
-            return plain;
-        }
-    }
-
-    /**
-     * The font of the script text.
-     */
-    public Font getFont() {
-        return font;
-    }
-
-    /**
-     * The font of the script text.
-     */
-    public void setFont(Font font) {
-        if (font == null) {
-            throw new NullPointerException("argument 'font' cannot be null");
-        }
-        this.font = font;
-    }
-
-    /**
-     * The background color of the script text area.
-     */
-    public Color getBackground() {
-        return background;
-    }
-
-    /**
-     * The background color of the script text area.
-     */
-    public void setBackground(Color background) {
-        if (background == null) {
-            throw new NullPointerException("argument 'background' cannot be null");
-        }
-        this.background = background;
-    }
-
-    /**
-     * The background color of the highlighted line of script text.
-     */
-    public Color getHighlightedBackground() {
-        return highlightedBackground;
-    }
-
-    /**
-     * The background color of the highlighted line of script text.
-     */
-    public void setHighlightedBackground(Color highlightedBackground) {
-        if (highlightedBackground == null) {
-            throw new NullPointerException("argument 'highlightedBackground' cannot be null");
-        }
-        this.highlightedBackground = highlightedBackground;
-    }
-
-    /**
-     * The color of the gutter text.
-     */
-    public Color getGutterText() {
-        return gutterText;
-    }
-
-    /**
-     * The color of the gutter text.
-     */
-    public void setGutterText(Color gutterText) {
-        if (gutterText == null) {
-            throw new NullPointerException("argument 'gutterText' cannot be null");
-        }
-        this.gutterText = gutterText;
-    }
-
-    /**
-     * The color of the border that joint the gutter and the script text panel.
-     */
-    public Color getGutterBorderColor() {
-        return gutterBorderColor;
-    }
-
-    /**
-     * The color of the border that joint the gutter and the script text panel.
-     */
-    public void setGutterBorderColor(Color gutterBorderColor) {
-        if (gutterBorderColor == null) {
-            throw new NullPointerException("argument 'gutterBorderColor' cannot be null");
-        }
-        this.gutterBorderColor = gutterBorderColor;
-    }
-
-    /**
-     * The width of the border that joint the gutter and the script text panel.
-     */
-    public int getGutterBorderWidth() {
-        return gutterBorderWidth;
-    }
-
-    /**
-     * The width of the border that joint the gutter and the script text panel.
-     */
-    public void setGutterBorderWidth(int gutterBorderWidth) {
-        this.gutterBorderWidth = gutterBorderWidth;
-    }
-
-    /**
-     * The font of the gutter text.
-     */
-    public Font getGutterTextFont() {
-        return gutterTextFont;
-    }
-
-    /**
-     * The font of the gutter text.
-     */
-    public void setGutterTextFont(Font gutterTextFont) {
-        if (gutterTextFont == null) {
-            throw new NullPointerException("argument 'gutterTextFont' cannot be null");
-        }
-        this.gutterTextFont = gutterTextFont;
-    }
-
-    /**
-     * The minimum padding from 'the leftmost of the line number text' to 'the left margin'.
-     */
-    public int getGutterTextPaddingLeft() {
-        return gutterTextPaddingLeft;
-    }
-
-    /**
-     * The minimum padding from 'the leftmost of the line number text' to 'the left margin'.
-     */
-    public void setGutterTextPaddingLeft(int gutterTextPaddingLeft) {
-        this.gutterTextPaddingLeft = gutterTextPaddingLeft;
-    }
-
-    /**
-     * The minimum padding from 'the rightmost of the line number text' to 'the right margin' (not to the gutter border).
-     */
-    public int getGutterTextPaddingRight() {
-        return gutterTextPaddingRight;
-    }
-
-    /**
-     * The minimum padding from 'the rightmost of the line number text' to 'the right margin' (not to the gutter border).
-     */
-    public void setGutterTextPaddingRight(int gutterTextPaddingRight) {
-        this.gutterTextPaddingRight = gutterTextPaddingRight;
-    }
-
-    public Style getString() {
-        return string;
-    }
-
-    public void setString(Style string) {
-        this.string = string;
-    }
-
-    public Style getKeyword() {
-        return keyword;
-    }
-
-    public void setKeyword(Style keyword) {
-        this.keyword = keyword;
-    }
-
-    public Style getComment() {
-        return comment;
-    }
-
-    public void setComment(Style comment) {
-        this.comment = comment;
-    }
-
-    public Style getType() {
-        return type;
-    }
-
-    public void setType(Style type) {
-        this.type = type;
-    }
-
-    public Style getLiteral() {
-        return literal;
-    }
-
-    public void setLiteral(Style literal) {
-        this.literal = literal;
-    }
-
-    public Style getPunctuation() {
-        return punctuation;
-    }
-
-    public void setPunctuation(Style punctuation) {
-        this.punctuation = punctuation;
-    }
-
-    public Style getPlain() {
-        return plain;
-    }
-
-    public void setPlain(Style plain) {
-        this.plain = plain;
-    }
-
-    public Style getTag() {
-        return tag;
-    }
-
-    public void setTag(Style tag) {
-        this.tag = tag;
-    }
-
-    public Style getDeclaration() {
-        return declaration;
-    }
-
-    public void setDeclaration(Style declaration) {
-        this.declaration = declaration;
-    }
-
-    public Style getSource() {
-        return source;
-    }
-
-    public void setSource(Style source) {
-        this.source = source;
-    }
-
-    public Style getAttributeName() {
-        return attributeName;
-    }
-
-    public void setAttributeName(Style attributeName) {
-        this.attributeName = attributeName;
-    }
-
-    public Style getAttributeValue() {
-        return attributeValue;
-    }
-
-    public void setAttributeValue(Style attributeValue) {
-        this.attributeValue = attributeValue;
-    }
-
-    public Style getNoCode() {
-        return noCode;
-    }
-
-    public void setNoCode(Style noCode) {
-        this.noCode = noCode;
-    }
-
-    public Style getOpenBracket() {
-        return openBracket;
-    }
-
-    public void setOpenBracket(Style openBracket) {
-        this.openBracket = openBracket;
-    }
-
-    public Style getCloseBracket() {
-        return closeBracket;
-    }
-
-    public void setCloseBracket(Style closeBracket) {
-        this.closeBracket = closeBracket;
-    }
-
-    public Style getVariable() {
-        return variable;
-    }
-
-    public void setVariable(Style variable) {
-        this.variable = variable;
-    }
-
-    public Style getFunction() {
-        return function;
-    }
-
-    public void setFunction(Style function) {
-        this.function = function;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public Theme clone() {
-        Theme object = null;
-        try {
-            object = (Theme) super.clone();
-            object.font = font;
-            object.background = background;
-            object.highlightedBackground = highlightedBackground;
-            object.gutterText = gutterText;
-            object.gutterBorderColor = gutterBorderColor;
-            object.gutterBorderWidth = gutterBorderWidth;
-            object.gutterTextFont = gutterTextFont;
-            object.gutterTextPaddingLeft = gutterTextPaddingLeft;
-            object.gutterTextPaddingRight = gutterTextPaddingRight;
-            object.string = string.clone();
-            object.keyword = keyword.clone();
-            object.comment = comment.clone();
-            object.type = type.clone();
-            object.literal = literal.clone();
-            object.punctuation = punctuation.clone();
-            object.plain = plain.clone();
-            object.tag = tag.clone();
-            object.declaration = declaration.clone();
-            object.source = source.clone();
-            object.attributeName = attributeName.clone();
-            object.attributeValue = attributeValue.clone();
-            object.noCode = noCode.clone();
-            object.openBracket = openBracket.clone();
-            object.closeBracket = closeBracket.clone();
-            object.variable = variable.clone();
-            object.function = function.clone();
-        } catch (CloneNotSupportedException ex) {
-            if (debug) {
-                Logger.getLogger(Theme.class.getName()).log(Level.WARNING, null, ex);
-            }
-        }
-        return object;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-
-        sb.append(getClass().getName());
-        sb.append(": ");
-        sb.append("font: ").append(getFont());
-        sb.append(", ");
-        sb.append("background: ").append(getBackground());
-        sb.append(", ");
-        sb.append("highlightedBackground: ").append(getHighlightedBackground());
-        sb.append(", ");
-        sb.append("gutterText: ").append(getGutterText());
-        sb.append(", ");
-        sb.append("gutterBorderColor: ").append(getGutterBorderColor());
-        sb.append(", ");
-        sb.append("gutterBorderWidth: ").append(getGutterBorderWidth());
-        sb.append(", ");
-        sb.append("gutterTextFont: ").append(getGutterTextFont());
-        sb.append(", ");
-        sb.append("gutterTextPaddingLeft: ").append(getGutterTextPaddingLeft());
-        sb.append(", ");
-        sb.append("gutterTextPaddingRight: ").append(getGutterTextPaddingRight());
-        sb.append(", ");
-        sb.append("string: ").append(getString());
-        sb.append(", ");
-        sb.append("keyword: ").append(getKeyword());
-        sb.append(", ");
-        sb.append("comment: ").append(getComment());
-        sb.append(", ");
-        sb.append("type: ").append(getType());
-        sb.append(", ");
-        sb.append("literal: ").append(getLiteral());
-        sb.append(", ");
-        sb.append("punctuation: ").append(getPunctuation());
-        sb.append(", ");
-        sb.append("plain: ").append(getPlain());
-        sb.append(", ");
-        sb.append("tag: ").append(getTag());
-        sb.append(", ");
-        sb.append("declaration: ").append(getDeclaration());
-        sb.append(", ");
-        sb.append("source: ").append(getSource());
-        sb.append(", ");
-        sb.append("attributeName: ").append(getAttributeName());
-        sb.append(", ");
-        sb.append("attributeValue: ").append(getAttributeValue());
-        sb.append(", ");
-        sb.append("noCode: ").append(getNoCode());
-        sb.append(", ");
-        sb.append("openBracket: ").append(getOpenBracket());
-        sb.append(", ");
-        sb.append("closeBracket: ").append(getCloseBracket());
-        sb.append(", ");
-        sb.append("variable: ").append(getVariable());
-        sb.append(", ");
-        sb.append("function: ").append(getFunction());
-
-        return sb.toString();
-    }
-
-    /**
-     * The style used by {@link prettify.theme} as those of CSS styles.
-     */
-    public static class Style {
-
-        protected boolean bold;
-        protected Color color;
-        /**
-         * The background color, null means no background color is set.
-         */
-        protected Color background;
-        protected boolean underline;
-        protected boolean italic;
-
-        /**
-         * Constructor.
-         * <p>
-         * <b>Default values:</b><br />
-         * <ul>
-         * <li>bold: false;</li>
-         * <li>color: black;</li>
-         * <li>background: null;</li>
-         * <li>underline: false;</li>
-         * <li>italic: false;</li>
-         * </ul>
-         * </p>
-         */
-        public Style() {
-            bold = false;
-            color = Color.black;
-            background = null;
-            underline = false;
-            italic = false;
-        }
-
-        /**
-         * Apply the style to the AttributeSet.
-         * Note that the AttributeSet should only be set by this function once or some unexpected style may appear.
-         * @param attributeSet the AttributeSet to set the style on
-         */
-        public void setAttributeSet(SimpleAttributeSet attributeSet) {
-            if (attributeSet == null) {
-                return;
-            }
-            StyleConstants.setBold(attributeSet, bold);
-            StyleConstants.setForeground(attributeSet, color);
-            if (background != null) {
-                StyleConstants.setBackground(attributeSet, background);
-            } else {
-                attributeSet.removeAttribute(StyleConstants.Background);
-            }
-            StyleConstants.setUnderline(attributeSet, underline);
-            StyleConstants.setItalic(attributeSet, italic);
-        }
-
-        /**
-         * Get the AttributeSet from this style.
-         * @return the AttributeSet
-         */
-        public SimpleAttributeSet getAttributeSet() {
-            SimpleAttributeSet attributeSet = new SimpleAttributeSet();
-            StyleConstants.setBold(attributeSet, bold);
-            StyleConstants.setForeground(attributeSet, color);
-            if (background != null) {
-                StyleConstants.setBackground(attributeSet, background);
-            }
-            StyleConstants.setUnderline(attributeSet, underline);
-            StyleConstants.setItalic(attributeSet, italic);
-            return attributeSet;
-        }
-
-        /**
-         * Get the background color.
-         * @return the background color or null if no color is set
-         */
-        public Color getBackground() {
-            return background;
-        }
-
-        /**
-         * Set the background color.
-         * @param background input null means do not set the background
-         */
-        public void setBackground(Color background) {
-            if (background == null) {
-                throw new NullPointerException("argument 'background' cannot be null");
-            }
-            this.background = background;
-        }
-
-        public boolean isBold() {
-            return bold;
-        }
-
-        public void setBold(boolean bold) {
-            this.bold = bold;
-        }
-
-        public Color getColor() {
-            return color;
-        }
-
-        public void setColor(Color color) {
-            if (color == null) {
-                throw new NullPointerException("argument 'color' cannot be null");
-            }
-            this.color = color;
-        }
-
-        public boolean isItalic() {
-            return italic;
-        }
-
-        public void setItalic(boolean italic) {
-            this.italic = italic;
-        }
-
-        public boolean isUnderline() {
-            return underline;
-        }
-
-        public void setUnderline(boolean underline) {
-            this.underline = underline;
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public boolean equals(Object obj) {
-            if (obj == null || !(obj instanceof Style)) {
-                return false;
-            }
-            if (obj == this) {
-                return true;
-            }
-            Style _object = (Style) obj;
-            return _object.bold == bold && _object.color.equals(color) && _object.background.equals(background)
-                    && _object.underline == underline && _object.italic == italic;
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public Style clone() {
-            Style object = null;
-            try {
-                object = (Style) super.clone();
-                object.bold = bold;
-                object.color = color;
-                object.background = background;
-                object.underline = underline;
-                object.italic = italic;
-            } catch (CloneNotSupportedException ex) {
-            }
-            return object;
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public String toString() {
-            StringBuilder sb = new StringBuilder();
-
-            sb.append(getClass().getName());
-            sb.append(": ");
-            sb.append("bold: ").append(bold);
-            sb.append(", ");
-            sb.append("color: ").append(color);
-            sb.append(", ");
-            sb.append("bg: ").append(background);
-            sb.append(", ");
-            sb.append("underline: ").append(underline);
-            sb.append(", ");
-            sb.append("italic: ").append(italic);
-
-            return sb.toString();
-        }
-    }
-}
diff --git a/src/prettify/Util.java b/src/prettify/Util.java
index d374644..8701f22 100644
--- a/src/prettify/Util.java
+++ b/src/prettify/Util.java
@@ -22,122 +22,187 @@
 
 /**
  * Common Utilities.
+ * Some of the functions are port from JavaScript.
+ * 
  * @author Chan Wai Shing <cws1989@gmail.com>
  */
 public class Util {
 
-    /**
-     * Get all the matches for {@code string} compiled by {@code pattern}. If {@code isGlobal}
-     * is true, the return results will only include the group 0 match.
-     * @param pattern the regexp
-     * @param string the string
-     * @param isGlobal similar to JavaScript /g flag
-     * @return all matches
-     */
-    public static String[] match(Pattern pattern, String string, boolean isGlobal) {
-        List<String> matchesList = new ArrayList<String>();
+  protected Util() {
+  }
 
-        Matcher matcher = pattern.matcher(string);
-        while (matcher.find()) {
-            matchesList.add(matcher.group(0));
-            if (!isGlobal) {
-                for (int i = 1, iEnd = matcher.groupCount(); i <= iEnd; i++) {
-                    matchesList.add(matcher.group(i));
-                }
-            }
+  /**
+   * Get all the matches for {@code string} compiled by {@code pattern}. If 
+   * {@code isGlobal} is true, the return results will only include the 
+   * group 0 matches. It is similar to string.match(regexp) in JavaScript.
+   * 
+   * @param pattern the regexp
+   * @param string the string
+   * @param isGlobal similar to JavaScript /g flag
+   * 
+   * @return all matches
+   */
+  public static String[] match(Pattern pattern, String string, boolean isGlobal) {
+    if (pattern == null) {
+      throw new NullPointerException("argument 'pattern' cannot be null");
+    }
+    if (string == null) {
+      throw new NullPointerException("argument 'string' cannot be null");
+    }
+
+    List<String> matchesList = new ArrayList<String>();
+
+    Matcher matcher = pattern.matcher(string);
+    while (matcher.find()) {
+      matchesList.add(matcher.group(0));
+      if (!isGlobal) {
+        for (int i = 1, iEnd = matcher.groupCount(); i <= iEnd; i++) {
+          matchesList.add(matcher.group(i));
         }
-
-        return matchesList.toArray(new String[matchesList.size()]);
+      }
     }
 
-    /**
-     * Test whether the {@code string} has at least one match by {@code pattern}.
-     * @param pattern the regexp to test
-     * @param string the string to test
-     * @return true if at least one match, false if not any
-     */
-    public static boolean test(Pattern pattern, String string) {
-        return pattern.matcher(string).find();
+    return matchesList.toArray(new String[matchesList.size()]);
+  }
+
+  /**
+   * Test whether the {@code string} has at least one match by 
+   * {@code pattern}.
+   * 
+   * @param pattern the regexp
+   * @param string the string to test
+   * 
+   * @return true if at least one match, false if no match
+   */
+  public static boolean test(Pattern pattern, String string) {
+    if (pattern == null) {
+      throw new NullPointerException("argument 'pattern' cannot be null");
+    }
+    if (string == null) {
+      throw new NullPointerException("argument 'string' cannot be null");
+    }
+    return pattern.matcher(string).find();
+  }
+
+  /**
+   * Join the {@code strings} into one string.
+   * 
+   * @param strings the string list to join
+   * 
+   * @return the joined string
+   */
+  public static String join(List<String> strings) {
+    if (strings == null) {
+      throw new NullPointerException("argument 'strings' cannot be null");
+    }
+    return join(strings.toArray(new String[strings.size()]));
+  }
+
+  /**
+   * Join the {@code strings} into one string with {@code delimiter} in 
+   * between.
+   * 
+   * @param strings the string list to join
+   * @param delimiter the delimiter
+   * 
+   * @return the joined string
+   */
+  public static String join(List<String> strings, String delimiter) {
+    if (strings == null) {
+      throw new NullPointerException("argument 'strings' cannot be null");
+    }
+    return join(strings.toArray(new String[strings.size()]), delimiter);
+  }
+
+  /**
+   * Join the {@code strings} into one string.
+   * 
+   * @param strings the string list to join
+   * 
+   * @return the joined string
+   */
+  public static String join(String[] strings) {
+    return join(strings, null);
+  }
+
+  /**
+   * Join the {@code strings} into one string with {@code delimiter} in 
+   * between. It is similar to RegExpObject.test(string) in JavaScript.
+   * 
+   * @param strings the string list to join
+   * @param delimiter the delimiter
+   * 
+   * @return the joined string
+   */
+  public static String join(String[] strings, String delimiter) {
+    if (strings == null) {
+      throw new NullPointerException("argument 'strings' cannot be null");
     }
 
-    /**
-     * Join the {@code strings} into one string.
-     * @param strings the string objects
-     * @return the joined string
-     */
-    public static String join(List<String> strings) {
-        return join(strings.toArray(new String[strings.size()]));
-    }
+    StringBuilder sb = new StringBuilder();
 
-    /**
-     * Join the {@code strings} into one string with {@code delimiter} in between.
-     * @param strings the string objects
-     * @return the joined string
-     */
-    public static String join(List<String> strings, String delimiter) {
-        return join(strings.toArray(new String[strings.size()]), delimiter);
-    }
-
-    /**
-     * Join the {@code strings} into one string.
-     * @param strings the string objects
-     * @return the joined string
-     */
-    public static String join(String[] strings) {
-        return join(strings, null);
-    }
-
-    /**
-     * Join the {@code strings} into one string with {@code delimiter} in between.
-     * @param strings the string objects
-     * @return the joined string
-     */
-    public static String join(String[] strings, String delimiter) {
-        StringBuilder sb = new StringBuilder();
-        for (String string : strings) {
-            if (delimiter != null && sb.length() != 0) {
-                sb.append(delimiter);
-            }
-            sb.append(string);
+    if (strings.length != 0) {
+      sb.append(strings[0]);
+      for (int i = 1, iEnd = strings.length; i < iEnd; i++) {
+        if (delimiter != null) {
+          sb.append(delimiter);
         }
-        return sb.toString();
+        sb.append(strings[i]);
+      }
     }
 
-    /**
-     * Remove identical adjacent tags from {@code decorations}.
-     * @param decorations see {@link prettify.Job#decorations}
-     * @param source the source code
-     * @return the {@code decorations} after treatment
-     */
-    public static List<Object> removeDuplicates(List<Object> decorations, String source) {
-        List<Object> returnList = new ArrayList<Object>();
+    return sb.toString();
+  }
 
-        // use TreeMap to remove entrys with same pos
-        Map<Integer, Object> orderedMap = new TreeMap<Integer, Object>();
-        for (int i = 0, iEnd = decorations.size(); i < iEnd; i++) {
-            orderedMap.put((Integer) decorations.get(i), decorations.get(i + 1));
-            i++;
-        }
-
-        // remove adjacent style
-        String previousStyle = null;
-        for (Integer _pos : orderedMap.keySet()) {
-            String style = (String) orderedMap.get(_pos);
-            if (previousStyle != null && previousStyle.equals(style)) {
-                continue;
-            }
-            returnList.add(_pos);
-            returnList.add(style);
-            previousStyle = style;
-        }
-
-        // remove last zero length tag
-        int returnListSize = returnList.size();
-        if (returnListSize >= 2 && returnList.get(returnListSize - 2).equals(source.length())) {
-            returnList.remove(returnListSize - 2);
-            returnList.remove(returnListSize - 2);
-        }
-
-        return returnList;
+  /**
+   * Remove identical adjacent tags from {@code decorations}.
+   * 
+   * @param decorations see {@link prettify.Job#decorations}
+   * @param source the source code
+   * 
+   * @return the {@code decorations} after treatment
+   * 
+   * @throws IllegalArgumentException the size of {@code decoration} is not
+   * a multiple of 2
+   */
+  public static List<Object> removeDuplicates(List<Object> decorations, String source) {
+    if (decorations == null) {
+      throw new NullPointerException("argument 'decorations' cannot be null");
     }
+    if (source == null) {
+      throw new NullPointerException("argument 'source' cannot be null");
+    }
+    if ((decorations.size() & 0x1) != 0) {
+      throw new IllegalArgumentException("the size of argument 'decorations' should be a multiple of 2");
+    }
+
+    List<Object> returnList = new ArrayList<Object>();
+
+    // use TreeMap to remove entrys with same pos
+    Map<Integer, Object> orderedMap = new TreeMap<Integer, Object>();
+    for (int i = 0, iEnd = decorations.size(); i < iEnd; i += 2) {
+      orderedMap.put((Integer) decorations.get(i), decorations.get(i + 1));
+    }
+
+    // remove adjacent style
+    String previousStyle = null;
+    for (Integer pos : orderedMap.keySet()) {
+      String style = (String) orderedMap.get(pos);
+      if (previousStyle != null && previousStyle.equals(style)) {
+        continue;
+      }
+      returnList.add(pos);
+      returnList.add(style);
+      previousStyle = style;
+    }
+
+    // remove last zero length tag
+    int returnListSize = returnList.size();
+    if (returnListSize >= 4 && returnList.get(returnListSize - 2).equals(source.length())) {
+      returnList.remove(returnListSize - 2);
+      returnList.remove(returnListSize - 2);
+    }
+
+    return returnList;
+  }
 }
diff --git a/src/prettify/example/Example.java b/src/prettify/example/Example.java
index 652dc55..366f217 100644
--- a/src/prettify/example/Example.java
+++ b/src/prettify/example/Example.java
@@ -26,90 +26,94 @@
 import prettify.theme.ThemeDefault;
 
 /**
- * Usage example. This will just cover some of the functions. To know other available functions, please read the JavaDoc.
+ * Usage example. This will just cover some of the functions. To know other 
+ * available functions, please read the JavaDoc.
+ * 
  * @author Chan Wai Shing <cws1989@gmail.com>
  */
 public class Example {
 
-    /**
-     * Read the resource file from the jar.
-     * @param path the resource path
-     * @return the content of the resource file in byte array
-     * @throws IOException error occurred when reading the content from the file
-     */
-    public static byte[] readResourceFile(String path) throws IOException {
-        if (path == null) {
-            throw new NullPointerException("argument 'path' cannot be null");
-        }
+  private static final Logger LOG = Logger.getLogger(Example.class.getName());
 
-        ByteArrayOutputStream bout = new ByteArrayOutputStream();
-        InputStream in = null;
-        try {
-            in = Example.class.getResourceAsStream(path);
-            if (in == null) {
-                throw new IOException("Resources not found: " + path);
-            }
-
-            int byteRead = 0;
-            byte[] b = new byte[8096];
-
-            while ((byteRead = in.read(b)) != -1) {
-                bout.write(b, 0, byteRead);
-            }
-        } finally {
-            if (in != null) {
-                try {
-                    in.close();
-                } catch (IOException ex) {
-                }
-            }
-        }
-
-        return bout.toByteArray();
+  /**
+   * Read the resource file from the jar.
+   * @param path the resource path
+   * @return the content of the resource file in byte array
+   * @throws IOException error occurred when reading the content from the file
+   */
+  public static byte[] readResourceFile(String path) throws IOException {
+    if (path == null) {
+      throw new NullPointerException("argument 'path' cannot be null");
     }
 
-    public static void main(String[] args) {
-        // set look & feel
+    ByteArrayOutputStream bout = new ByteArrayOutputStream();
+    InputStream in = null;
+    try {
+      in = Example.class.getResourceAsStream(path);
+      if (in == null) {
+        throw new IOException("Resources not found: " + path);
+      }
+
+      int byteRead = 0;
+      byte[] b = new byte[8096];
+
+      while ((byteRead = in.read(b)) != -1) {
+        bout.write(b, 0, byteRead);
+      }
+    } finally {
+      if (in != null) {
         try {
-            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
-        } catch (Exception ex) {
-            Logger.getLogger(Example.class.getName()).log(Level.INFO, "Failed to set system look and feel.", ex);
+          in.close();
+        } catch (IOException ex) {
+        }
+      }
+    }
+
+    return bout.toByteArray();
+  }
+
+  public static void main(String[] args) {
+    // set look & feel
+    try {
+      UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+    } catch (Exception ex) {
+      LOG.log(Level.INFO, "Failed to set system look and feel.", ex);
+    }
+
+    SwingUtilities.invokeLater(new Runnable() {
+
+      @Override
+      public void run() {
+        // timer start
+        long start, end;
+        start = System.currentTimeMillis();
+
+        // use Default theme
+        SyntaxHighlighter highlighter = new SyntaxHighlighter(new ThemeDefault());
+        // set the line number count from 10 instead of 1
+        highlighter.setFirstLine(10);
+        // set to highlight line 13, 27, 28, 38, 42, 43 and 53
+        highlighter.setHighlightedLineList(Arrays.asList(13, 27, 28, 38, 42, 43, 53));
+        try {
+          // set the content of the script, the example.html is located in the jar: /prettify/example/example.html
+          highlighter.setContent(new String(readResourceFile("/prettify/example/example.html")));
+        } catch (IOException ex) {
+          LOG.log(Level.SEVERE, null, ex);
         }
 
-        SwingUtilities.invokeLater(new Runnable() {
+        // timer end
+        end = System.currentTimeMillis();
+        System.out.println("time elapsed: " + (end - start) + "ms");
 
-            @Override
-            public void run() {
-                try {
-                    // timer start
-                    long start, end;
-                    start = System.currentTimeMillis();
-
-                    // use Default theme
-                    SyntaxHighlighter highlighter = new SyntaxHighlighter(new ThemeDefault());
-                    // set the line number count from 10 instead of 1
-                    highlighter.setFirstLine(10);
-                    // set to highlight line 13, 27, 28, 38, 42, 43 and 53
-                    highlighter.setHighlightedLineList(Arrays.asList(13, 27, 28, 38, 42, 43, 53));
-                    // set the content of the script, the example.html is located in the jar: /prettify/example/example.html
-                    highlighter.setContent(new String(readResourceFile("/prettify/example/example.html")));
-
-                    // timer end
-                    end = System.currentTimeMillis();
-                    System.out.println("time elapsed: " + (end - start) + "ms");
-
-                    // initiate a frame and display
-                    JFrame frame = new JFrame();
-                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
-                    // SyntaxHighlighter is actually a JScrollPane
-                    frame.setContentPane(highlighter);
-                    frame.setLocationByPlatform(true);
-                    frame.pack();
-                    frame.setVisible(true);
-                } catch (Exception ex) {
-                    Logger.getLogger(Example.class.getName()).log(Level.SEVERE, null, ex);
-                }
-            }
-        });
-    }
+        // initiate a frame and display
+        JFrame frame = new JFrame();
+        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+        // SyntaxHighlighter is actually a JScrollPane
+        frame.setContentPane(highlighter);
+        frame.setLocationByPlatform(true);
+        frame.pack();
+        frame.setVisible(true);
+      }
+    });
+  }
 }
diff --git a/src/prettify/example/example.html b/src/prettify/example/example.html
index d378884..1ef22ae 100644
--- a/src/prettify/example/example.html
+++ b/src/prettify/example/example.html
@@ -1,4 +1,4 @@
-<html var="value">

+<html>

 <head>

     <title> HTML Script Example </title>

     <style type="text/css">

diff --git a/src/prettify/gui/JTextComponentRowHeader.java b/src/prettify/gui/JTextComponentRowHeader.java
new file mode 100644
index 0000000..0e660f4
--- /dev/null
+++ b/src/prettify/gui/JTextComponentRowHeader.java
@@ -0,0 +1,554 @@
+// Copyright (C) 2011 Chan Wai Shing
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package prettify.gui;
+
+import java.awt.Color;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.RenderingHints;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JViewport;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+import javax.swing.text.Element;
+import javax.swing.text.JTextComponent;
+
+/**
+ * A row header panel for JScrollPane.
+ * It is used with JTextComponent for line number displaying.
+ * Currently it only accept fixed-height line.
+ * The usage of this class is not limited to this syntax highlighter, it can be 
+ * used on all JTextComponent.
+ * 
+ * @author Chan Wai Shing <cws1989@gmail.com>
+ */
+public class JTextComponentRowHeader extends JPanel {
+
+  private static final Logger LOG = Logger.getLogger(JTextComponentRowHeader.class.getName());
+  private static final long serialVersionUID = 1L;
+  /**
+   * The anti-aliasing setting of the line number text. See
+   * {@link java.awt.RenderingHints}.
+   */
+  private Object textAntiAliasing = RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT;
+  /**
+   * The color of the border that joint the gutter and the script text panel.
+   */
+  private Color borderColor = new Color(184, 184, 184);
+  /**
+   * The background of the row when it is highlighted.
+   */
+  private Color highlightedColor = Color.black;
+  /**
+   * The minimum padding from 'the leftmost of the line number text' to 
+   * 'the left margin'.
+   */
+  private int paddingLeft = 7;
+  /**
+   * The minimum padding from 'the rightmost of the line number text' to 
+   * 'the right margin' (not to the gutter border).
+   */
+  private int paddingRight = 2;
+  /**
+   * The width of the border that joint the gutter and the script text panel.
+   */
+  private int borderWidth = 1;
+  /**
+   * The JScrollPane that it be added into.
+   */
+  protected JScrollPane scrollPane;
+  /**
+   * The text component to listen the change events on.
+   */
+  protected JTextComponent textComponent;
+  /**
+   * The document of the text component.
+   */
+  protected Document document;
+  /**
+   * The document listener for {@link #document}.
+   */
+  protected DocumentListener documentListener;
+  /**
+   * The cached panel width.
+   */
+  protected int panelWidth;
+  /**
+   * The cached largest row number (for determine panel width 
+   * {@link #panelWidth}).
+   */
+  protected int largestRowNumber;
+  /**
+   * The cached text component height, for determine panel height.
+   */
+  protected int textComponentHeight;
+  /**
+   * The line number offset. E.g. set offset to 9 will make the first line 
+   * number to appear at line 1 + 9 = 10
+   */
+  private int lineNumberOffset;
+  /**
+   * The list of line numbers that indicate which lines are needed to be 
+   * highlighted.
+   */
+  private final List<Integer> highlightedLineList;
+  /**
+   * Indicator indicate whether it is listening to the document change events 
+   * or not.
+   */
+  private boolean listenToDocumentUpdate;
+
+  /**
+   * Constructor.
+   * 
+   * @param scrollPane the JScrollPane that it be added into
+   * @param textComponent the text component to listen the change events on
+   */
+  public JTextComponentRowHeader(JScrollPane scrollPane, JTextComponent textComponent) {
+    super();
+
+    if (scrollPane == null) {
+      throw new NullPointerException("argument 'scrollPane' cannot be null");
+    }
+    if (textComponent == null) {
+      throw new NullPointerException("argument 'textComponent' cannot be null");
+    }
+
+    setFont(new Font("Verdana", Font.PLAIN, 10));
+    setForeground(Color.black);
+    setBackground(new Color(233, 232, 226));
+
+    this.scrollPane = scrollPane;
+    this.textComponent = textComponent;
+
+    panelWidth = 0;
+    largestRowNumber = 1;
+    textComponentHeight = 0;
+
+    lineNumberOffset = 0;
+    highlightedLineList = Collections.synchronizedList(new ArrayList<Integer>());
+
+    listenToDocumentUpdate = true;
+
+    document = textComponent.getDocument();
+    documentListener = new DocumentListener() {
+
+      @Override
+      public void insertUpdate(DocumentEvent e) {
+        handleEvent(e);
+      }
+
+      @Override
+      public void removeUpdate(DocumentEvent e) {
+        handleEvent(e);
+      }
+
+      @Override
+      public void changedUpdate(DocumentEvent e) {
+        handleEvent(e);
+      }
+
+      public void handleEvent(DocumentEvent e) {
+        if (!listenToDocumentUpdate) {
+          return;
+        }
+        Document _document = e.getDocument();
+        if (document == _document) {
+          checkPanelSize();
+        } else {
+          _document.removeDocumentListener(this);
+        }
+      }
+    };
+
+    document.addDocumentListener(documentListener);
+
+    checkPanelSize();
+  }
+
+  /**
+   * Check if the 'document of the textComponent' has changed to another 
+   * document or not.
+   */
+  protected void validateTextComponentDocument() {
+    Document _currentDocument = textComponent.getDocument();
+    if (document != _currentDocument) {
+      document.removeDocumentListener(documentListener);
+      document = _currentDocument;
+      _currentDocument.addDocumentListener(documentListener);
+    }
+  }
+
+  /**
+   * Check whether the height of the row header panel match with the height of 
+   * the text component or not. If not, it will invoke 
+   * {@link #updatePanelSize()}.
+   */
+  public void checkPanelSize() {
+    validateTextComponentDocument();
+    int _largestRowNumber = document.getDefaultRootElement().getElementCount() + lineNumberOffset;
+    int _panelWidth = getFontMetrics(getFont()).stringWidth(Integer.toString(_largestRowNumber)) + paddingLeft + paddingRight;
+    if (panelWidth != _panelWidth || largestRowNumber != _largestRowNumber) {
+      panelWidth = _panelWidth;
+      largestRowNumber = _largestRowNumber;
+      updatePanelSize();
+    }
+  }
+
+  /**
+   * Update the panel size.
+   */
+  protected void updatePanelSize() {
+    Container parent = getParent();
+    if (parent != null) {
+      parent.doLayout();
+      scrollPane.doLayout();
+      parent.repaint();
+    }
+  }
+
+  /**
+   * The font of the line number.
+   */
+  @Override
+  public void setFont(Font font) {
+    super.setFont(font);
+  }
+
+  /**
+   * The color of the line number.
+   * @param foreground the color
+   */
+  @Override
+  public void setForeground(Color foreground) {
+    super.setForeground(foreground);
+  }
+
+  /**
+   * The background of the panel.
+   * @param background the color
+   */
+  @Override
+  public void setBackground(Color background) {
+    super.setBackground(background);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Dimension getPreferredSize() {
+    textComponentHeight = textComponent.getPreferredSize().height;
+    return new Dimension(panelWidth, textComponentHeight);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void paint(Graphics g) {
+    super.paint(g);
+
+    // check whether the height of this panel matches the height of the text component or not
+    Dimension textComponentPreferredSize = textComponent.getPreferredSize();
+    if (textComponentHeight != textComponentPreferredSize.height) {
+      textComponentHeight = textComponentPreferredSize.height;
+      updatePanelSize();
+    }
+
+    JViewport viewport = scrollPane.getViewport();
+    Point viewPosition = viewport.getViewPosition();
+    Dimension viewportSize = viewport.getSize();
+
+    validateTextComponentDocument();
+    Element defaultRootElement = document.getDefaultRootElement();
+
+
+    // maybe able to get the value when font changed and cache them
+    // however i'm not sure if there is any condition which will make the java.awt.FontMetrics get by getFontMetrics() from java.awt.Graphics is different from getFontMetrics() from java.awt.Component
+    FontMetrics fontMetrics = g.getFontMetrics(getFont());
+    int fontHeight = fontMetrics.getHeight();
+    int fontAscent = fontMetrics.getAscent();
+    int fontLeading = fontMetrics.getLeading();
+
+    FontMetrics textPaneFontMetrics = g.getFontMetrics(textComponent.getFont());
+    int textPaneFontHeight = textPaneFontMetrics.getHeight();
+
+
+    // get the location of the document of the left top and right bottom point of the visible part of the text component
+    int documentOffsetStart = textComponent.viewToModel(viewPosition);
+    int documentOffsetEnd = textComponent.viewToModel(new Point(viewPosition.x + viewportSize.width, viewPosition.y + viewportSize.height));
+
+    // convert the location to line number
+    int startLine = defaultRootElement.getElementIndex(documentOffsetStart) + 1 + lineNumberOffset;
+    int endLine = defaultRootElement.getElementIndex(documentOffsetEnd) + 1 + lineNumberOffset;
+
+
+    // draw right border
+    g.setColor(borderColor);
+    g.fillRect(panelWidth - borderWidth, viewPosition.y, borderWidth, viewportSize.height);
+
+    // draw line number
+    int startY = -1, baselineOffset = -1;
+    try {
+      startY = textComponent.modelToView(documentOffsetStart).y;
+      baselineOffset = (textPaneFontHeight / 2) + fontAscent - (fontHeight / 2) + fontLeading;
+    } catch (BadLocationException ex) {
+      LOG.log(Level.WARNING, null, ex);
+      return;
+    }
+
+    // text anti-aliasing
+    ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, textAntiAliasing);
+    // preserve the foreground color (for recover the color after highlighing the line)
+    Color foregroundColor = getForeground();
+
+    g.setColor(foregroundColor);
+    g.setFont(getFont());
+
+    for (int i = startLine, y = startY + baselineOffset; i <= endLine; y += textPaneFontHeight, i++) {
+      boolean highlighted = false;
+      if (highlightedLineList.indexOf((Integer) i) != -1) {
+        // highlight this line
+        g.setColor(borderColor);
+        g.fillRect(0, y - baselineOffset, panelWidth - borderWidth, textPaneFontHeight);
+        g.setColor(highlightedColor);
+        highlighted = true;
+      }
+
+      // draw the line number
+      String lineNumberString = Integer.toString(i);
+      int lineNumberStringWidth = fontMetrics.stringWidth(lineNumberString);
+      g.drawString(lineNumberString, panelWidth - lineNumberStringWidth - paddingRight, y);
+
+      // restore the line number text color
+      if (highlighted) {
+        g.setColor(foregroundColor);
+      }
+    }
+  }
+
+  /**
+   * The anti-aliasing setting of the line number text. See 
+   * {@link java.awt.RenderingHints}.
+   * 
+   * @return the setting
+   */
+  public Object getTextAntiAliasing() {
+    return textAntiAliasing;
+  }
+
+  /**
+   * The anti-aliasing setting of the line number text. See 
+   * {@link java.awt.RenderingHints}.
+   * 
+   * @param textAntiAliasing the setting
+   */
+  public void setTextAntiAliasing(Object textAntiAliasing) {
+    if (textAntiAliasing == null) {
+      throw new NullPointerException("argument 'textAntiAliasing' cannot be null");
+    }
+    this.textAntiAliasing = textAntiAliasing;
+    repaint();
+  }
+
+  /**
+   * The color of the border that joint the gutter and the script text panel.
+   * @return the color
+   */
+  public Color getBorderColor() {
+    return borderColor;
+  }
+
+  /**
+   * The color of the border that joint the gutter and the script text panel.
+   * @param borderColor the color
+   */
+  public void setBorderColor(Color borderColor) {
+    if (borderColor == null) {
+      throw new NullPointerException("argument 'borderColor' cannot be null");
+    }
+    this.borderColor = borderColor;
+    repaint();
+  }
+
+  /**
+   * The background of the highlighted row.
+   * @return the color
+   */
+  public Color getHighlightedColor() {
+    return highlightedColor;
+  }
+
+  /**
+   * The background of the highlighted row.
+   * @param highlightedColor the color
+   */
+  public void setHighlightedColor(Color highlightedColor) {
+    if (highlightedColor == null) {
+      throw new NullPointerException("argument 'highlightedColor' cannot be null");
+    }
+    this.highlightedColor = highlightedColor;
+    repaint();
+  }
+
+  /**
+   * The minimum padding from the 'leftmost of the line number text' to the 
+   * 'left margin'.
+   * 
+   * @return the padding in pixel
+   */
+  public int getPaddingLeft() {
+    return paddingLeft;
+  }
+
+  /**
+   * The minimum padding from 'the leftmost of the line number text' to the 
+   * 'left margin'.
+   * 
+   * @param paddingLeft the padding in pixel
+   */
+  public void setPaddingLeft(int paddingLeft) {
+    this.paddingLeft = paddingLeft;
+    checkPanelSize();
+  }
+
+  /**
+   * The minimum padding from the 'rightmost of the line number text' to the 
+   * 'right margin' (not to the gutter border).
+   * 
+   * @return the padding in pixel
+   */
+  public int getPaddingRight() {
+    return paddingRight;
+  }
+
+  /**
+   * The minimum padding from the 'rightmost of the line number text' to the 
+   * 'right margin' (not to the gutter border).
+   * 
+   * @param paddingRight the padding in pixel
+   */
+  public void setPaddingRight(int paddingRight) {
+    this.paddingRight = paddingRight;
+    checkPanelSize();
+  }
+
+  /**
+   * The width of the border that joint the gutter and the script text panel.
+   * 
+   * @return the width in pixel
+   */
+  public int getBorderWidth() {
+    return borderWidth;
+  }
+
+  /**
+   * The width of the border that joint the gutter and the script text panel.
+   * 
+   * @param borderWidth the width in pixel
+   */
+  public void setBorderWidth(int borderWidth) {
+    this.borderWidth = borderWidth;
+    repaint();
+  }
+
+  /**
+   * Get the line number offset
+   * @return the offset
+   */
+  public int getLineNumberOffset() {
+    return lineNumberOffset;
+  }
+
+  /**
+   * Set the line number offset. E.g. set offset to 9 will make the first line 
+   * number to appear at line 1 + 9 = 10
+   * 
+   * @param offset the offset
+   */
+  public void setLineNumberOffset(int offset) {
+    lineNumberOffset = Math.max(lineNumberOffset, offset);
+    checkPanelSize();
+    repaint();
+  }
+
+  /**
+   * Get the list of highlighted lines.
+   * @return a copy of the list
+   */
+  public List<Integer> getHighlightedLineList() {
+    List<Integer> returnList;
+    synchronized (highlightedLineList) {
+      returnList = new ArrayList<Integer>(highlightedLineList);
+    }
+    return returnList;
+  }
+
+  /**
+   * Set highlighted lines. Note that this will clear all previous recorded 
+   * highlighted lines.
+   * @param highlightedLineList the list that contain the highlighted lines
+   */
+  public void setHighlightedLineList(List<Integer> highlightedLineList) {
+    synchronized (this.highlightedLineList) {
+      this.highlightedLineList.clear();
+      if (highlightedLineList != null) {
+        this.highlightedLineList.addAll(highlightedLineList);
+      }
+    }
+    repaint();
+  }
+
+  /**
+   * Add highlighted line.
+   * @param lineNumber the line number to highlight
+   */
+  public void addHighlightedLine(int lineNumber) {
+    highlightedLineList.add(lineNumber);
+    repaint();
+  }
+
+  /**
+   * Check if it is listening to the document change events.
+   * @return true if it is listening, false if not 
+   */
+  public boolean isListenToDocumentUpdate() {
+    return listenToDocumentUpdate;
+  }
+
+  /**
+   * Set to listen to document change events or not. It is useful when a number 
+   * of updates are needed to be done to the text component. May invoke 
+   * {@link #checkPanelSize() ()} after setting this to true.
+   * 
+   * @param listenToDocumentUpdate true to listen on document change, false not
+   */
+  public void setListenToDocumentUpdate(boolean listenToDocumentUpdate) {
+    this.listenToDocumentUpdate = listenToDocumentUpdate;
+  }
+}
diff --git a/src/prettify/gui/SyntaxHighlighterPane.java b/src/prettify/gui/SyntaxHighlighterPane.java
new file mode 100644
index 0000000..9c32dcc
--- /dev/null
+++ b/src/prettify/gui/SyntaxHighlighterPane.java
@@ -0,0 +1,518 @@
+// Copyright (C) 2011 Chan Wai Shing
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package prettify.gui;
+
+import prettify.theme.Theme;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseMotionListener;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.swing.JTextPane;
+import javax.swing.text.AbstractDocument;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.BoxView;
+import javax.swing.text.ComponentView;
+import javax.swing.text.DefaultStyledDocument;
+import javax.swing.text.Element;
+import javax.swing.text.Highlighter;
+import javax.swing.text.IconView;
+import javax.swing.text.JTextComponent;
+import javax.swing.text.LabelView;
+import javax.swing.text.ParagraphView;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.StyledEditorKit;
+import javax.swing.text.View;
+import javax.swing.text.ViewFactory;
+
+/**
+ * The text pane for displaying the script text.
+ * @author Chan Wai Shing <cws1989@gmail.com>
+ */
+public class SyntaxHighlighterPane extends JTextPane {
+
+  private static final Logger LOG = Logger.getLogger(SyntaxHighlighterPane.class.getName());
+  private static final long serialVersionUID = 1L;
+  /**
+   * The line number offset. E.g. set offset to 9 will make the first line 
+   * number to appear at line 1 + 9 = 10
+   */
+  private int lineNumberOffset;
+  /**
+   * The background color of the highlighted line. Default is black.
+   */
+  private Color highlightedBackground;
+  /**
+   * Indicator that indicate to turn on the mouse-over highlight effect or not. 
+   * See {@link #setHighlightOnMouseOver(boolean)}.
+   */
+  private boolean highlightWhenMouseOver;
+  /**
+   * The list of line numbers that indicate which lines are needed to be 
+   * highlighted.
+   */
+  protected final List<Integer> highlightedLineList;
+  /**
+   * The highlighter painter used to do the highlight line effect.
+   */
+  protected Highlighter.HighlightPainter highlightPainter;
+  /**
+   * The theme.
+   */
+  protected Theme theme;
+  /**
+   * The style list. see {@link #setStyle(java.util.List)}.
+   */
+  protected List<Object> styleList;
+  /**
+   * Record the mouse cursor is currently pointing at which line of the 
+   * document. -1 means not any line.
+   * It is used internally.
+   */
+  protected int mouseOnLine;
+
+  /**
+   * Constructor.
+   */
+  public SyntaxHighlighterPane() {
+    super();
+
+    setEditable(false);
+    //<editor-fold defaultstate="collapsed" desc="editor kit">
+    setEditorKit(new StyledEditorKit() {
+
+      private static final long serialVersionUID = 1L;
+
+      @Override
+      public ViewFactory getViewFactory() {
+        return new ViewFactory() {
+
+          @Override
+          public View create(Element elem) {
+            String kind = elem.getName();
+            if (kind != null) {
+              if (kind.equals(AbstractDocument.ContentElementName)) {
+                return new LabelView(elem) {
+
+                  @Override
+                  public int getBreakWeight(int axis, float pos, float len) {
+                    return 0;
+                  }
+                };
+              } else if (kind.equals(AbstractDocument.ParagraphElementName)) {
+                return new ParagraphView(elem) {
+
+                  @Override
+                  public int getBreakWeight(int axis, float pos, float len) {
+                    return 0;
+                  }
+                };
+              } else if (kind.equals(AbstractDocument.SectionElementName)) {
+                return new BoxView(elem, View.Y_AXIS);
+              } else if (kind.equals(StyleConstants.ComponentElementName)) {
+                return new ComponentView(elem) {
+
+                  @Override
+                  public int getBreakWeight(int axis, float pos, float len) {
+                    return 0;
+                  }
+                };
+              } else if (kind.equals(StyleConstants.IconElementName)) {
+                return new IconView(elem);
+              }
+            }
+            return new LabelView(elem) {
+
+              @Override
+              public int getBreakWeight(int axis, float pos, float len) {
+                return 0;
+              }
+            };
+          }
+        };
+      }
+    });
+    //</editor-fold>
+
+    lineNumberOffset = 0;
+
+    //<editor-fold defaultstate="collapsed" desc="highlighter painter">
+    highlightedBackground = Color.black;
+    highlightWhenMouseOver = true;
+    highlightedLineList = new ArrayList<Integer>();
+
+    highlightPainter = new Highlighter.HighlightPainter() {
+
+      @Override
+      public void paint(Graphics g, int p0, int p1, Shape bounds, JTextComponent c) {
+        if (c.getParent() == null) {
+          return;
+        }
+
+        // get the Y-axis value of the visible area of the text component
+        int startY = Math.abs(c.getY());
+        int endY = startY + c.getParent().getHeight();
+
+        FontMetrics textPaneFontMetrics = g.getFontMetrics(getFont());
+        int textPaneFontHeight = textPaneFontMetrics.getHeight();
+
+        int largerestLineNumber = c.getDocument().getDefaultRootElement().getElementCount();
+
+        g.setColor(highlightedBackground);
+
+        // draw the highlight background to the highlighted line
+        synchronized (highlightedLineList) {
+          for (Integer lineNumber : highlightedLineList) {
+            if (lineNumber > largerestLineNumber + lineNumberOffset) {
+              // skip those line number that out of range
+              continue;
+            }
+            // get the Y-axis value of this highlighted line
+            int _y = Math.max(0, textPaneFontHeight * (lineNumber - lineNumberOffset - 1));
+            if (_y > endY || _y + textPaneFontHeight < startY) {
+              // this line is out of visible area, skip it
+              continue;
+            }
+            // draw the highlighted background
+            g.fillRect(0, _y, c.getWidth(), textPaneFontHeight);
+          }
+        }
+
+        // draw the mouse-over-highlight effect
+        if (mouseOnLine != -1) {
+          if (mouseOnLine <= largerestLineNumber + lineNumberOffset) {
+            int _y = Math.max(0, textPaneFontHeight * (mouseOnLine - lineNumberOffset - 1));
+            if (_y < endY && _y + textPaneFontHeight > startY) {
+              // the line is within the range of visible area
+              g.fillRect(0, _y, c.getWidth(), textPaneFontHeight);
+            }
+          }
+        }
+      }
+    };
+    try {
+      getHighlighter().addHighlight(0, 0, highlightPainter);
+    } catch (BadLocationException ex) {
+      LOG.log(Level.SEVERE, null, ex);
+    }
+    //</editor-fold>
+
+    mouseOnLine = -1;
+
+    //<editor-fold defaultstate="collapsed" desc="mouse listener">
+    addMouseListener(new MouseAdapter() {
+
+      @Override
+      public void mouseExited(MouseEvent e) {
+        if (!highlightWhenMouseOver) {
+          return;
+        }
+        mouseOnLine = -1;
+        repaint();
+      }
+    });
+    addMouseMotionListener(new MouseMotionListener() {
+
+      @Override
+      public void mouseDragged(MouseEvent e) {
+      }
+
+      @Override
+      public void mouseMoved(MouseEvent e) {
+        if (!highlightWhenMouseOver) {
+          return;
+        }
+
+        Element defaultRootElement = getDocument().getDefaultRootElement();
+        // get the position of the document the mouse cursor is pointing
+        int documentOffsetStart = viewToModel(e.getPoint());
+
+        // the line number that the mouse cursor is currently pointing
+        int lineNumber = documentOffsetStart == -1 ? -1 : defaultRootElement.getElementIndex(documentOffsetStart) + 1 + lineNumberOffset;
+        if (lineNumber == defaultRootElement.getElementCount()) {
+          // if the line number got is the last line, check if the cursor is actually on the line or already below the line
+          try {
+            Rectangle rectangle = modelToView(documentOffsetStart);
+            if (e.getY() > rectangle.y + rectangle.height) {
+              lineNumber = -1;
+            }
+          } catch (BadLocationException ex) {
+            LOG.log(Level.SEVERE, null, ex);
+          }
+        }
+
+        // only repaint when the line number changed
+        if (mouseOnLine != lineNumber) {
+          mouseOnLine = lineNumber;
+          repaint();
+        }
+      }
+    });
+    //</editor-fold>
+  }
+
+  @Override
+  public void setHighlighter(Highlighter highlighter) {
+    if (highlightPainter != null) {
+      getHighlighter().removeHighlight(highlightPainter);
+      try {
+        highlighter.addHighlight(0, 0, highlightPainter);
+      } catch (BadLocationException ex) {
+        LOG.log(Level.SEVERE, null, ex);
+      }
+    }
+    super.setHighlighter(highlighter);
+  }
+
+  /**
+   * Set the content of the syntax highlighter. It is better to set other 
+   * settings first and set this the last.
+   * 
+   * @param content the content to set
+   */
+  public void setContent(String content) {
+    String newContent = content == null ? "" : content;
+    DefaultStyledDocument document = (DefaultStyledDocument) getDocument();
+
+    try {
+      document.remove(0, document.getLength());
+      if (theme != null) {
+        document.insertString(0, newContent, theme.getPlain().getAttributeSet());
+      } else {
+        document.insertString(0, newContent, new SimpleAttributeSet());
+      }
+    } catch (BadLocationException ex) {
+      LOG.log(Level.SEVERE, null, ex);
+    }
+
+    setCaretPosition(0);
+
+    // clear the style list
+    styleList = null;
+  }
+
+  /**
+   * Apply the list of style to the script text pane.
+   * 
+   * @param styleList the style list
+   */
+  public void setStyle(List<Object> styleList) {
+    if (styleList == null) {
+      this.styleList = new ArrayList<Object>();
+      return;
+    }
+    this.styleList = new ArrayList<Object>(styleList);
+
+    if (theme == null) {
+      return;
+    }
+
+    DefaultStyledDocument document = (DefaultStyledDocument) getDocument();
+    // clear all the existing style
+    document.setCharacterAttributes(0, document.getLength(), theme.getPlain().getAttributeSet(), true);
+
+    Integer startPos = 0, endPos = 0;
+    // apply style according to the style list
+    for (int i = 0, iEnd = styleList.size(); i < iEnd; i += 2) {
+      SimpleAttributeSet attributeSet = theme.getStyle((String) styleList.get(i + 1)).getAttributeSet();
+      endPos = i + 2 < iEnd ? (Integer) styleList.get(i + 2) : document.getLength();
+      startPos = (Integer) styleList.get(i);
+      document.setCharacterAttributes(startPos, endPos - startPos, attributeSet, true);
+    }
+
+    repaint();
+  }
+
+  /**
+   * Get current theme.
+   * 
+   * @return the current theme
+   */
+  public Theme getTheme() {
+    return theme;
+  }
+
+  /**
+   * Set the theme.
+   * 
+   * @param theme the theme
+   */
+  public void setTheme(Theme theme) {
+    if (theme == null) {
+      throw new NullPointerException("argument 'theme' cannot be null");
+    }
+    this.theme = theme;
+
+    setFont(theme.getFont());
+    setBackground(theme.getBackground());
+    setHighlightedBackground(theme.getHighlightedBackground());
+
+    if (styleList != null) {
+      setStyle(styleList);
+    }
+  }
+
+  /**
+   * Get the line number offset. E.g. set offset to 9 will make the first line 
+   * number to appear at line 1 + 9 = 10
+   * 
+   * @return the offset
+   */
+  public int getLineNumberOffset() {
+    return lineNumberOffset;
+  }
+
+  /**
+   * Set the line number offset. E.g. set offset to 9 will make the first line 
+   * number to appear at line 1 + 9 = 10
+   * 
+   * @param offset the offset
+   */
+  public void setLineNumberOffset(int offset) {
+    lineNumberOffset = Math.max(lineNumberOffset, offset);
+    repaint();
+  }
+
+  /**
+   * Get the color of the highlighted background. Default is black.
+   * 
+   * @return the color
+   */
+  public Color getHighlightedBackground() {
+    return highlightedBackground;
+  }
+
+  /**
+   * Set the color of the highlighted background. Default is black.
+   * 
+   * @param highlightedBackground the color
+   */
+  public void setHighlightedBackground(Color highlightedBackground) {
+    if (highlightedBackground == null) {
+      throw new NullPointerException("argument 'highlightedBackground' cannot be null");
+    }
+    this.highlightedBackground = highlightedBackground;
+    repaint();
+  }
+
+  /**
+   * Check the status of the mouse-over highlight effect. Default is on.
+   * 
+   * @return true if turned on, false if turned off
+   */
+  public boolean isHighlightOnMouseOver() {
+    return highlightWhenMouseOver;
+  }
+
+  /**
+   * Set turn on the mouse-over highlight effect or not. Default is on.
+   * If set true, there will be a highlight effect on the line that the mouse 
+   * cursor currently is pointing on (on the script text panel only, not on the 
+   * line number panel).
+   * 
+   * @param highlightWhenMouseOver true to turn on, false to turn off
+   */
+  public void setHighlightOnMouseOver(boolean highlightWhenMouseOver) {
+    this.highlightWhenMouseOver = highlightWhenMouseOver;
+    if (!highlightWhenMouseOver) {
+      mouseOnLine = -1;
+    }
+    repaint();
+  }
+
+  /**
+   * Get the list of highlighted lines.
+   * 
+   * @return a copy of the list
+   */
+  public List<Integer> getHighlightedLineList() {
+    List<Integer> returnList;
+    synchronized (highlightedLineList) {
+      returnList = new ArrayList<Integer>(highlightedLineList);
+    }
+    return returnList;
+  }
+
+  /**
+   * Set highlighted lines. Note that this will clear all previous recorded 
+   * highlighted lines.
+   * 
+   * @param highlightedLineList the list that contain the highlighted lines
+   */
+  public void setHighlightedLineList(List<Integer> highlightedLineList) {
+    synchronized (this.highlightedLineList) {
+      this.highlightedLineList.clear();
+      if (highlightedLineList != null) {
+        this.highlightedLineList.addAll(highlightedLineList);
+      }
+    }
+    repaint();
+  }
+
+  /**
+   * Add highlighted line.
+   * 
+   * @param lineNumber the line number to highlight
+   */
+  public void addHighlightedLine(int lineNumber) {
+    highlightedLineList.add(lineNumber);
+    repaint();
+  }
+
+  /**
+   * Set the {@code font} according to {@code bold} and {@code italic}.
+   * 
+   * @param font the font to set
+   * @param bold true to set bold, false not
+   * @param italic true to set italic, false not
+   * 
+   * @return the font with bold and italic changed, or null if the input 
+   * {@code font} is null
+   */
+  protected static Font setFont(Font font, boolean bold, boolean italic) {
+    if (font == null) {
+      return null;
+    }
+
+    if ((font.getStyle() & Font.BOLD) != 0) {
+      if (!bold) {
+        return font.deriveFont(font.getStyle() ^ Font.BOLD);
+      }
+    } else {
+      if (bold) {
+        return font.deriveFont(font.getStyle() | Font.BOLD);
+      }
+    }
+    if ((font.getStyle() & Font.ITALIC) != 0) {
+      if (!italic) {
+        return font.deriveFont(font.getStyle() ^ Font.ITALIC);
+      }
+    } else {
+      if (italic) {
+        return font.deriveFont(font.getStyle() | Font.ITALIC);
+      }
+    }
+
+    return font;
+  }
+}
diff --git a/src/prettify/gui/package-info.java b/src/prettify/gui/package-info.java
new file mode 100644
index 0000000..cdbe674
--- /dev/null
+++ b/src/prettify/gui/package-info.java
@@ -0,0 +1,4 @@
+/**

+ * GUI.

+ */

+package prettify.gui;
\ No newline at end of file
diff --git a/src/prettify/lang/Lang.java b/src/prettify/lang/Lang.java
new file mode 100644
index 0000000..f82ec18
--- /dev/null
+++ b/src/prettify/lang/Lang.java
@@ -0,0 +1,118 @@
+// Copyright (C) 2011 Chan Wai Shing
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package prettify.lang;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Lang class for Java Prettify.
+ * Note that the method {@link #getFileExtensions()} should be overridden.
+ * 
+ * @author Chan Wai Shing <cws1989@gmail.com>
+ */
+public abstract class Lang {
+
+  /**
+   * Similar to those in JavaScript prettify.js.
+   */
+  protected List<List<Object>> shortcutStylePatterns;
+  /**
+   * Similar to those in JavaScript prettify.js.
+   */
+  protected List<List<Object>> fallthroughStylePatterns;
+  /**
+   * See {@link prettify.lang.LangCss} for example.
+   */
+  protected List<Lang> extendedLangs;
+
+  /**
+   * Constructor.
+   */
+  public Lang() {
+    shortcutStylePatterns = new ArrayList<List<Object>>();
+    fallthroughStylePatterns = new ArrayList<List<Object>>();
+    extendedLangs = new ArrayList<Lang>();
+  }
+
+  /**
+   * This method should be overridden by the child class.
+   * This provide the file extensions list to help the parser to determine which 
+   * {@link Lang} to use. See JavaScript prettify.js.
+   * 
+   * @return the list of file extensions
+   */
+  public static List<String> getFileExtensions() {
+    return new ArrayList<String>();
+  }
+
+  public List<List<Object>> getShortcutStylePatterns() {
+    List<List<Object>> returnList = new ArrayList<List<Object>>();
+    for (List<Object> shortcutStylePattern : shortcutStylePatterns) {
+      returnList.add(new ArrayList<Object>(shortcutStylePattern));
+    }
+    return returnList;
+  }
+
+  public void setShortcutStylePatterns(List<List<Object>> shortcutStylePatterns) {
+    if (shortcutStylePatterns == null) {
+      this.shortcutStylePatterns = new ArrayList<List<Object>>();
+      return;
+    }
+    List<List<Object>> cloneList = new ArrayList<List<Object>>();
+    for (List<Object> shortcutStylePattern : shortcutStylePatterns) {
+      cloneList.add(new ArrayList<Object>(shortcutStylePattern));
+    }
+    this.shortcutStylePatterns = cloneList;
+  }
+
+  public List<List<Object>> getFallthroughStylePatterns() {
+    List<List<Object>> returnList = new ArrayList<List<Object>>();
+    for (List<Object> fallthroughStylePattern : fallthroughStylePatterns) {
+      returnList.add(new ArrayList<Object>(fallthroughStylePattern));
+    }
+    return returnList;
+  }
+
+  public void setFallthroughStylePatterns(List<List<Object>> fallthroughStylePatterns) {
+    if (fallthroughStylePatterns == null) {
+      this.fallthroughStylePatterns = new ArrayList<List<Object>>();
+      return;
+    }
+    List<List<Object>> cloneList = new ArrayList<List<Object>>();
+    for (List<Object> fallthroughStylePattern : fallthroughStylePatterns) {
+      cloneList.add(new ArrayList<Object>(fallthroughStylePattern));
+    }
+    this.fallthroughStylePatterns = cloneList;
+  }
+
+  /**
+   * Get the extended languages list.
+   * @return the list
+   */
+  public List<Lang> getExtendedLangs() {
+    return new ArrayList<Lang>(extendedLangs);
+  }
+
+  /**
+   * Set extended languages. Because we cannot register multiple languages 
+   * within one {@link prettify.Lang}, so it is used as an solution. See 
+   * {@link prettify.lang.LangCss} for example.
+   * 
+   * @param extendedLangs the list of {@link prettify.Lang}s
+   */
+  public void setExtendedLangs(List<Lang> extendedLangs) {
+    this.extendedLangs = new ArrayList<Lang>(extendedLangs);
+  }
+}
diff --git a/src/prettify/lang/LangAppollo.java b/src/prettify/lang/LangAppollo.java
index 45d33a8..8c1ad7e 100644
--- a/src/prettify/lang/LangAppollo.java
+++ b/src/prettify/lang/LangAppollo.java
@@ -17,8 +17,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.regex.Pattern;
-import prettify.Lang;
-import prettify.Prettify;
+import prettify.parser.Prettify;
 
 /**
  * This is similar to the lang-appollo.js in JavaScript Prettify.
@@ -37,31 +36,31 @@
  */
 public class LangAppollo extends Lang {
 
-    public LangAppollo() {
-        List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
-        List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
+  public LangAppollo() {
+    List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
+    List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
 
-        // A line comment that starts with ;
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^#[^\r\n]*"), null, "#"}));
-        // Whitespace
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[\t\n\r \\xA0]+"), null, "\t\n\r " + Character.toString((char) 0xA0)}));
-        // A double quoted, possibly multi-line, string.
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^\\\"(?:[^\\\"\\\\]|\\\\[\\s\\S])*(?:\\\"|$)"), null, "\""}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_KEYWORD, Pattern.compile("^(?:ADS|AD|AUG|BZF|BZMF|CAE|CAF|CA|CCS|COM|CS|DAS|DCA|DCOM|DCS|DDOUBL|DIM|DOUBLE|DTCB|DTCF|DV|DXCH|EDRUPT|EXTEND|INCR|INDEX|NDX|INHINT|LXCH|MASK|MSK|MP|MSU|NOOP|OVSK|QXCH|RAND|READ|RELINT|RESUME|RETURN|ROR|RXOR|SQUARE|SU|TCR|TCAA|OVSK|TCF|TC|TS|WAND|WOR|WRITE|XCH|XLQ|XXALQ|ZL|ZQ|ADD|ADZ|SUB|SUZ|MPY|MPR|MPZ|DVP|COM|ABS|CLA|CLZ|LDQ|STO|STQ|ALS|LLS|LRS|TRA|TSQ|TMI|TOV|AXT|TIX|DLY|INP|OUT)\\s"), null}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_TYPE, Pattern.compile("^(?:-?GENADR|=MINUS|2BCADR|VN|BOF|MM|-?2CADR|-?[1-6]DNADR|ADRES|BBCON|[SE]?BANK\\=?|BLOCK|BNKSUM|E?CADR|COUNT\\*?|2?DEC\\*?|-?DNCHAN|-?DNPTR|EQUALS|ERASE|MEMORY|2?OCT|REMADR|SETLOC|SUBRO|ORG|BSS|BES|SYN|EQU|DEFINE|END)\\s"), null}));
-        // A single quote possibly followed by a word that optionally ends with
-        // = ! or ?.
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^\\'(?:-*(?:\\w|\\\\[\\x21-\\x7e])(?:[\\w-]*|\\\\[\\x21-\\x7e])[=!?]?)?")}));
-        // Any word including labels that optionally ends with = ! or ?.
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^-*(?:[!-z_]|\\\\[\\x21-\\x7e])(?:[\\w-]*|\\\\[\\x21-\\x7e])[=!?]?", Pattern.CASE_INSENSITIVE)}));
-        // A printable non-space non-special character
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PUNCTUATION, Pattern.compile("^[^\\w\\t\\n\\r \\xA0()\\\"\\\\\\';]+")}));
+    // A line comment that starts with ;
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^#[^\r\n]*"), null, "#"}));
+    // Whitespace
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[\t\n\r \\xA0]+"), null, "\t\n\r " + Character.toString((char) 0xA0)}));
+    // A double quoted, possibly multi-line, string.
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^\\\"(?:[^\\\"\\\\]|\\\\[\\s\\S])*(?:\\\"|$)"), null, "\""}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_KEYWORD, Pattern.compile("^(?:ADS|AD|AUG|BZF|BZMF|CAE|CAF|CA|CCS|COM|CS|DAS|DCA|DCOM|DCS|DDOUBL|DIM|DOUBLE|DTCB|DTCF|DV|DXCH|EDRUPT|EXTEND|INCR|INDEX|NDX|INHINT|LXCH|MASK|MSK|MP|MSU|NOOP|OVSK|QXCH|RAND|READ|RELINT|RESUME|RETURN|ROR|RXOR|SQUARE|SU|TCR|TCAA|OVSK|TCF|TC|TS|WAND|WOR|WRITE|XCH|XLQ|XXALQ|ZL|ZQ|ADD|ADZ|SUB|SUZ|MPY|MPR|MPZ|DVP|COM|ABS|CLA|CLZ|LDQ|STO|STQ|ALS|LLS|LRS|TRA|TSQ|TMI|TOV|AXT|TIX|DLY|INP|OUT)\\s"), null}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_TYPE, Pattern.compile("^(?:-?GENADR|=MINUS|2BCADR|VN|BOF|MM|-?2CADR|-?[1-6]DNADR|ADRES|BBCON|[SE]?BANK\\=?|BLOCK|BNKSUM|E?CADR|COUNT\\*?|2?DEC\\*?|-?DNCHAN|-?DNPTR|EQUALS|ERASE|MEMORY|2?OCT|REMADR|SETLOC|SUBRO|ORG|BSS|BES|SYN|EQU|DEFINE|END)\\s"), null}));
+    // A single quote possibly followed by a word that optionally ends with
+    // = ! or ?.
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^\\'(?:-*(?:\\w|\\\\[\\x21-\\x7e])(?:[\\w-]*|\\\\[\\x21-\\x7e])[=!?]?)?")}));
+    // Any word including labels that optionally ends with = ! or ?.
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^-*(?:[!-z_]|\\\\[\\x21-\\x7e])(?:[\\w-]*|\\\\[\\x21-\\x7e])[=!?]?", Pattern.CASE_INSENSITIVE)}));
+    // A printable non-space non-special character
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PUNCTUATION, Pattern.compile("^[^\\w\\t\\n\\r \\xA0()\\\"\\\\\\';]+")}));
 
-        setShortcutStylePatterns(_shortcutStylePatterns);
-        setFallthroughStylePatterns(_fallthroughStylePatterns);
-    }
+    setShortcutStylePatterns(_shortcutStylePatterns);
+    setFallthroughStylePatterns(_fallthroughStylePatterns);
+  }
 
-    public static List<String> getFileExtensions() {
-        return Arrays.asList(new String[]{"apollo", "agc", "aea"});
-    }
+  public static List<String> getFileExtensions() {
+    return Arrays.asList(new String[]{"apollo", "agc", "aea"});
+  }
 }
diff --git a/src/prettify/lang/LangClj.java b/src/prettify/lang/LangClj.java
index cf4d124..db13b78 100644
--- a/src/prettify/lang/LangClj.java
+++ b/src/prettify/lang/LangClj.java
@@ -19,8 +19,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.regex.Pattern;
-import prettify.Lang;
-import prettify.Prettify;
+import prettify.parser.Prettify;
 
 /**
  * This is similar to the lang-clj.js in JavaScript Prettify.
@@ -56,28 +55,28 @@
  */
 public class LangClj extends Lang {
 
-    public LangClj() {
-        List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
-        List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
+  public LangClj() {
+    List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
+    List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
 
-        // clojure has more paren types than minimal lisp.
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{"opn", Pattern.compile("^[\\(\\{\\[]+"), null, "([{"}));
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{"clo", Pattern.compile("^[\\)\\}\\]]+"), null, ")]}"}));
-        // A line comment that starts with ;
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^;[^\r\n]*"), null, ";"}));
-        // Whitespace
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[\t\n\r \\xA0]+"), null, "\t\n\r " + Character.toString((char) 0xA0)}));
-        // A double quoted, possibly multi-line, string.
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^\\\"(?:[^\\\"\\\\]|\\\\[\\s\\S])*(?:\\\"|$)"), null, "\""}));
-        // clojure has a much larger set of keywords
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_KEYWORD, Pattern.compile("^(?:def|if|do|let|quote|var|fn|loop|recur|throw|try|monitor-enter|monitor-exit|defmacro|defn|defn-|macroexpand|macroexpand-1|for|doseq|dosync|dotimes|and|or|when|not|assert|doto|proxy|defstruct|first|rest|cons|defprotocol|deftype|defrecord|reify|defmulti|defmethod|meta|with-meta|ns|in-ns|create-ns|import|intern|refer|alias|namespace|resolve|ref|deref|refset|new|set!|memfn|to-array|into-array|aset|gen-class|reduce|map|filter|find|nil?|empty?|hash-map|hash-set|vec|vector|seq|flatten|reverse|assoc|dissoc|list|list?|disj|get|union|difference|intersection|extend|extend-type|extend-protocol|prn)\\b"), null}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_TYPE, Pattern.compile("^:[0-9a-zA-Z\\-]+")}));
+    // clojure has more paren types than minimal lisp.
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{"opn", Pattern.compile("^[\\(\\{\\[]+"), null, "([{"}));
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{"clo", Pattern.compile("^[\\)\\}\\]]+"), null, ")]}"}));
+    // A line comment that starts with ;
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^;[^\r\n]*"), null, ";"}));
+    // Whitespace
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[\t\n\r \\xA0]+"), null, "\t\n\r " + Character.toString((char) 0xA0)}));
+    // A double quoted, possibly multi-line, string.
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^\\\"(?:[^\\\"\\\\]|\\\\[\\s\\S])*(?:\\\"|$)"), null, "\""}));
+    // clojure has a much larger set of keywords
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_KEYWORD, Pattern.compile("^(?:def|if|do|let|quote|var|fn|loop|recur|throw|try|monitor-enter|monitor-exit|defmacro|defn|defn-|macroexpand|macroexpand-1|for|doseq|dosync|dotimes|and|or|when|not|assert|doto|proxy|defstruct|first|rest|cons|defprotocol|deftype|defrecord|reify|defmulti|defmethod|meta|with-meta|ns|in-ns|create-ns|import|intern|refer|alias|namespace|resolve|ref|deref|refset|new|set!|memfn|to-array|into-array|aset|gen-class|reduce|map|filter|find|nil?|empty?|hash-map|hash-set|vec|vector|seq|flatten|reverse|assoc|dissoc|list|list?|disj|get|union|difference|intersection|extend|extend-type|extend-protocol|prn)\\b"), null}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_TYPE, Pattern.compile("^:[0-9a-zA-Z\\-]+")}));
 
-        setShortcutStylePatterns(_shortcutStylePatterns);
-        setFallthroughStylePatterns(_fallthroughStylePatterns);
-    }
+    setShortcutStylePatterns(_shortcutStylePatterns);
+    setFallthroughStylePatterns(_fallthroughStylePatterns);
+  }
 
-    public static List<String> getFileExtensions() {
-        return Arrays.asList(new String[]{"clj"});
-    }
+  public static List<String> getFileExtensions() {
+    return Arrays.asList(new String[]{"clj"});
+  }
 }
diff --git a/src/prettify/lang/LangCss.java b/src/prettify/lang/LangCss.java
index c292656..71f1d32 100644
--- a/src/prettify/lang/LangCss.java
+++ b/src/prettify/lang/LangCss.java
@@ -17,8 +17,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.regex.Pattern;
-import prettify.Lang;
-import prettify.Prettify;
+import prettify.parser.Prettify;
 
 /**
  * This is similar to the lang-css.js in JavaScript Prettify.
@@ -41,73 +40,73 @@
  */
 public class LangCss extends Lang {
 
-    public LangCss() {
-        List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
-        List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
+  public LangCss() {
+    List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
+    List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
 
-        // The space production <s>
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[ \t\r\n\f]+"), null, " \t\r\n\f"}));
-        // Quoted strings.  <string1> and <string2>
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^\\\"(?:[^\n\r\f\\\\\\\"]|\\\\(?:\r\n?|\n|\f)|\\\\[\\s\\S])*\\\""), null}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^\\'(?:[^\n\r\f\\\\\\']|\\\\(?:\r\n?|\n|\f)|\\\\[\\s\\S])*\\'"), null}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-css-str", Pattern.compile("^url\\(([^\\)\\\"\\']*)\\)", Pattern.CASE_INSENSITIVE)}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_KEYWORD, Pattern.compile("^(?:url|rgb|\\!important|@import|@page|@media|@charset|inherit)(?=[^\\-\\w]|$)", Pattern.CASE_INSENSITIVE), null}));
-        // A property name -- an identifier followed by a colon.
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-css-kw", Pattern.compile("^(-?(?:[_a-z]|(?:\\\\[0-9a-f]+ ?))(?:[_a-z0-9\\-]|\\\\(?:\\\\[0-9a-f]+ ?))*)\\s*:", Pattern.CASE_INSENSITIVE)}));
-        // A C style block comment.  The <comment> production.
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^\\/\\*[^*]*\\*+(?:[^\\/*][^*]*\\*+)*\\/")}));
-        // Escaping text spans
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^(?:<!--|-->)")}));
-        // A number possibly containing a suffix.
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^(?:\\d+|\\d*\\.\\d+)(?:%|[a-z]+)?", Pattern.CASE_INSENSITIVE)}));
-        // A hex color
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^#(?:[0-9a-f]{3}){1,2}", Pattern.CASE_INSENSITIVE)}));
-        // An identifier
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^-?(?:[_a-z]|(?:\\\\[\\da-f]+ ?))(?:[_a-z\\d\\-]|\\\\(?:\\\\[\\da-f]+ ?))*", Pattern.CASE_INSENSITIVE)}));
-        // A run of punctuation
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PUNCTUATION, Pattern.compile("^[^\\s\\w\\'\\\"]+", Pattern.CASE_INSENSITIVE)}));
+    // The space production <s>
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[ \t\r\n\f]+"), null, " \t\r\n\f"}));
+    // Quoted strings.  <string1> and <string2>
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^\\\"(?:[^\n\r\f\\\\\\\"]|\\\\(?:\r\n?|\n|\f)|\\\\[\\s\\S])*\\\""), null}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^\\'(?:[^\n\r\f\\\\\\']|\\\\(?:\r\n?|\n|\f)|\\\\[\\s\\S])*\\'"), null}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-css-str", Pattern.compile("^url\\(([^\\)\\\"\\']*)\\)", Pattern.CASE_INSENSITIVE)}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_KEYWORD, Pattern.compile("^(?:url|rgb|\\!important|@import|@page|@media|@charset|inherit)(?=[^\\-\\w]|$)", Pattern.CASE_INSENSITIVE), null}));
+    // A property name -- an identifier followed by a colon.
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-css-kw", Pattern.compile("^(-?(?:[_a-z]|(?:\\\\[0-9a-f]+ ?))(?:[_a-z0-9\\-]|\\\\(?:\\\\[0-9a-f]+ ?))*)\\s*:", Pattern.CASE_INSENSITIVE)}));
+    // A C style block comment.  The <comment> production.
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^\\/\\*[^*]*\\*+(?:[^\\/*][^*]*\\*+)*\\/")}));
+    // Escaping text spans
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^(?:<!--|-->)")}));
+    // A number possibly containing a suffix.
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^(?:\\d+|\\d*\\.\\d+)(?:%|[a-z]+)?", Pattern.CASE_INSENSITIVE)}));
+    // A hex color
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^#(?:[0-9a-f]{3}){1,2}", Pattern.CASE_INSENSITIVE)}));
+    // An identifier
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^-?(?:[_a-z]|(?:\\\\[\\da-f]+ ?))(?:[_a-z\\d\\-]|\\\\(?:\\\\[\\da-f]+ ?))*", Pattern.CASE_INSENSITIVE)}));
+    // A run of punctuation
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PUNCTUATION, Pattern.compile("^[^\\s\\w\\'\\\"]+", Pattern.CASE_INSENSITIVE)}));
 
-        setShortcutStylePatterns(_shortcutStylePatterns);
-        setFallthroughStylePatterns(_fallthroughStylePatterns);
+    setShortcutStylePatterns(_shortcutStylePatterns);
+    setFallthroughStylePatterns(_fallthroughStylePatterns);
 
-        setExtendedLangs(Arrays.asList(new Lang[]{new LangCssKeyword(), new LangCssString()}));
+    setExtendedLangs(Arrays.asList(new Lang[]{new LangCssKeyword(), new LangCssString()}));
+  }
+
+  public static List<String> getFileExtensions() {
+    return Arrays.asList(new String[]{"css"});
+  }
+
+  protected static class LangCssKeyword extends Lang {
+
+    public LangCssKeyword() {
+      List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
+      List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
+
+      _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_KEYWORD, Pattern.compile("^-?(?:[_a-z]|(?:\\\\[\\da-f]+ ?))(?:[_a-z\\d\\-]|\\\\(?:\\\\[\\da-f]+ ?))*", Pattern.CASE_INSENSITIVE)}));
+
+      setShortcutStylePatterns(_shortcutStylePatterns);
+      setFallthroughStylePatterns(_fallthroughStylePatterns);
     }
 
     public static List<String> getFileExtensions() {
-        return Arrays.asList(new String[]{"css"});
+      return Arrays.asList(new String[]{"css-kw"});
+    }
+  }
+
+  protected static class LangCssString extends Lang {
+
+    public LangCssString() {
+      List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
+      List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
+
+      _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^[^\\)\\\"\\']+")}));
+
+      setShortcutStylePatterns(_shortcutStylePatterns);
+      setFallthroughStylePatterns(_fallthroughStylePatterns);
     }
 
-    protected static class LangCssKeyword extends Lang {
-
-        public LangCssKeyword() {
-            List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
-            List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
-
-            _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_KEYWORD, Pattern.compile("^-?(?:[_a-z]|(?:\\\\[\\da-f]+ ?))(?:[_a-z\\d\\-]|\\\\(?:\\\\[\\da-f]+ ?))*", Pattern.CASE_INSENSITIVE)}));
-
-            setShortcutStylePatterns(_shortcutStylePatterns);
-            setFallthroughStylePatterns(_fallthroughStylePatterns);
-        }
-
-        public static List<String> getFileExtensions() {
-            return Arrays.asList(new String[]{"css-kw"});
-        }
+    public static List<String> getFileExtensions() {
+      return Arrays.asList(new String[]{"css-str"});
     }
-
-    protected static class LangCssString extends Lang {
-
-        public LangCssString() {
-            List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
-            List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
-
-            _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^[^\\)\\\"\\']+")}));
-
-            setShortcutStylePatterns(_shortcutStylePatterns);
-            setFallthroughStylePatterns(_fallthroughStylePatterns);
-        }
-
-        public static List<String> getFileExtensions() {
-            return Arrays.asList(new String[]{"css-str"});
-        }
-    }
+  }
 }
diff --git a/src/prettify/lang/LangGo.java b/src/prettify/lang/LangGo.java
index a551a79..0448bae 100644
--- a/src/prettify/lang/LangGo.java
+++ b/src/prettify/lang/LangGo.java
@@ -17,8 +17,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.regex.Pattern;
-import prettify.Lang;
-import prettify.Prettify;
+import prettify.parser.Prettify;
 
 /**
  * This is similar to the lang-go.js in JavaScript Prettify.
@@ -52,24 +51,24 @@
  */
 public class LangGo extends Lang {
 
-    public LangGo() {
-        List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
-        List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
+  public LangGo() {
+    List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
+    List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
 
-        // Whitespace is made up of spaces, tabs and newline characters.
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[\\t\\n\\r \\xA0]+"), null, "\t\n\r " + Character.toString((char) 0xA0)}));
-        // Not escaped as a string.  See note on minimalism above.
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^(?:\\\"(?:[^\\\"\\\\]|\\\\[\\s\\S])*(?:\\\"|$)|\\'(?:[^\\'\\\\]|\\\\[\\s\\S])+(?:\\'|$)|`[^`]*(?:`|$))"), null, "\"'"}));
-        // Block comments are delimited by /* and */.
-        // Single-line comments begin with // and extend to the end of a line.
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^(?:\\/\\/[^\\r\\n]*|\\/\\*[\\s\\S]*?\\*\\/)")}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^(?:[^\\/\\\"\\'`]|\\/(?![\\/\\*]))+", Pattern.CASE_INSENSITIVE)}));
+    // Whitespace is made up of spaces, tabs and newline characters.
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[\\t\\n\\r \\xA0]+"), null, "\t\n\r " + Character.toString((char) 0xA0)}));
+    // Not escaped as a string.  See note on minimalism above.
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^(?:\\\"(?:[^\\\"\\\\]|\\\\[\\s\\S])*(?:\\\"|$)|\\'(?:[^\\'\\\\]|\\\\[\\s\\S])+(?:\\'|$)|`[^`]*(?:`|$))"), null, "\"'"}));
+    // Block comments are delimited by /* and */.
+    // Single-line comments begin with // and extend to the end of a line.
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^(?:\\/\\/[^\\r\\n]*|\\/\\*[\\s\\S]*?\\*\\/)")}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^(?:[^\\/\\\"\\'`]|\\/(?![\\/\\*]))+", Pattern.CASE_INSENSITIVE)}));
 
-        setShortcutStylePatterns(_shortcutStylePatterns);
-        setFallthroughStylePatterns(_fallthroughStylePatterns);
-    }
+    setShortcutStylePatterns(_shortcutStylePatterns);
+    setFallthroughStylePatterns(_fallthroughStylePatterns);
+  }
 
-    public static List<String> getFileExtensions() {
-        return Arrays.asList(new String[]{"go"});
-    }
+  public static List<String> getFileExtensions() {
+    return Arrays.asList(new String[]{"go"});
+  }
 }
diff --git a/src/prettify/lang/LangHs.java b/src/prettify/lang/LangHs.java
index 16295e4..dbd51b0 100644
--- a/src/prettify/lang/LangHs.java
+++ b/src/prettify/lang/LangHs.java
@@ -17,8 +17,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.regex.Pattern;
-import prettify.Lang;
-import prettify.Prettify;
+import prettify.parser.Prettify;
 
 /**
  * This is similar to the lang-hs.js in JavaScript Prettify.
@@ -50,65 +49,65 @@
  */
 public class LangHs extends Lang {
 
-    public LangHs() {
-        List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
-        List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
+  public LangHs() {
+    List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
+    List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
 
-        // Whitespace
-        // whitechar    ->    newline | vertab | space | tab | uniWhite
-        // newline      ->    return linefeed | return | linefeed | formfeed
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[\\t\\n\\x0B\\x0C\\r ]+"), null, "\t\n" + Character.toString((char) 0x0B) + Character.toString((char) 0x0C) + "\r "}));
-        // Single line double and single-quoted strings.
-        // char         ->    ' (graphic<' | \> | space | escape<\&>) '
-        // string       ->    " {graphic<" | \> | space | escape | gap}"
-        // escape       ->    \ ( charesc | ascii | decimal | o octal
-        //                        | x hexadecimal )
-        // charesc      ->    a | b | f | n | r | t | v | \ | " | ' | &
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^\\\"(?:[^\\\"\\\\\\n\\x0C\\r]|\\\\[\\s\\S])*(?:\\\"|$)"), null, "\""}));
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^\\'(?:[^\\'\\\\\\n\\x0C\\r]|\\\\[^&])\\'?"), null, "'"}));
-        // decimal      ->    digit{digit}
-        // octal        ->    octit{octit}
-        // hexadecimal  ->    hexit{hexit}
-        // integer      ->    decimal
-        //               |    0o octal | 0O octal
-        //               |    0x hexadecimal | 0X hexadecimal
-        // float        ->    decimal . decimal [exponent]
-        //               |    decimal exponent
-        // exponent     ->    (e | E) [+ | -] decimal
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^(?:0o[0-7]+|0x[\\da-f]+|\\d+(?:\\.\\d+)?(?:e[+\\-]?\\d+)?)", Pattern.CASE_INSENSITIVE), null, "0123456789"}));
-        // Haskell does not have a regular lexical grammar due to the nested
-        // ncomment.
-        // comment      ->    dashes [ any<symbol> {any}] newline
-        // ncomment     ->    opencom ANYseq {ncomment ANYseq}closecom
-        // dashes       ->    '--' {'-'}
-        // opencom      ->    '{-'
-        // closecom     ->    '-}'
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^(?:(?:--+(?:[^\\r\\n\\x0C]*)?)|(?:\\{-(?:[^-]|-+[^-\\}])*-\\}))")}));
-        // reservedid   ->    case | class | data | default | deriving | do
-        //               |    else | if | import | in | infix | infixl | infixr
-        //               |    instance | let | module | newtype | of | then
-        //               |    type | where | _
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_KEYWORD, Pattern.compile("^(?:case|class|data|default|deriving|do|else|if|import|in|infix|infixl|infixr|instance|let|module|newtype|of|then|type|where|_)(?=[^a-zA-Z0-9\\']|$)"), null}));
-        // qvarid       ->    [ modid . ] varid
-        // qconid       ->    [ modid . ] conid
-        // varid        ->    (small {small | large | digit | ' })<reservedid>
-        // conid        ->    large {small | large | digit | ' }
-        // modid        ->    conid
-        // small        ->    ascSmall | uniSmall | _
-        // ascSmall     ->    a | b | ... | z
-        // uniSmall     ->    any Unicode lowercase letter
-        // large        ->    ascLarge | uniLarge
-        // ascLarge     ->    A | B | ... | Z
-        // uniLarge     ->    any uppercase or titlecase Unicode letter
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^(?:[A-Z][\\w\\']*\\.)*[a-zA-Z][\\w\\']*")}));
-        // matches the symbol production
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PUNCTUATION, Pattern.compile("^[^\\t\\n\\x0B\\x0C\\r a-zA-Z0-9\\'\\\"]+")}));
+    // Whitespace
+    // whitechar    ->    newline | vertab | space | tab | uniWhite
+    // newline      ->    return linefeed | return | linefeed | formfeed
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[\\t\\n\\x0B\\x0C\\r ]+"), null, "\t\n" + Character.toString((char) 0x0B) + Character.toString((char) 0x0C) + "\r "}));
+    // Single line double and single-quoted strings.
+    // char         ->    ' (graphic<' | \> | space | escape<\&>) '
+    // string       ->    " {graphic<" | \> | space | escape | gap}"
+    // escape       ->    \ ( charesc | ascii | decimal | o octal
+    //                        | x hexadecimal )
+    // charesc      ->    a | b | f | n | r | t | v | \ | " | ' | &
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^\\\"(?:[^\\\"\\\\\\n\\x0C\\r]|\\\\[\\s\\S])*(?:\\\"|$)"), null, "\""}));
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^\\'(?:[^\\'\\\\\\n\\x0C\\r]|\\\\[^&])\\'?"), null, "'"}));
+    // decimal      ->    digit{digit}
+    // octal        ->    octit{octit}
+    // hexadecimal  ->    hexit{hexit}
+    // integer      ->    decimal
+    //               |    0o octal | 0O octal
+    //               |    0x hexadecimal | 0X hexadecimal
+    // float        ->    decimal . decimal [exponent]
+    //               |    decimal exponent
+    // exponent     ->    (e | E) [+ | -] decimal
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^(?:0o[0-7]+|0x[\\da-f]+|\\d+(?:\\.\\d+)?(?:e[+\\-]?\\d+)?)", Pattern.CASE_INSENSITIVE), null, "0123456789"}));
+    // Haskell does not have a regular lexical grammar due to the nested
+    // ncomment.
+    // comment      ->    dashes [ any<symbol> {any}] newline
+    // ncomment     ->    opencom ANYseq {ncomment ANYseq}closecom
+    // dashes       ->    '--' {'-'}
+    // opencom      ->    '{-'
+    // closecom     ->    '-}'
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^(?:(?:--+(?:[^\\r\\n\\x0C]*)?)|(?:\\{-(?:[^-]|-+[^-\\}])*-\\}))")}));
+    // reservedid   ->    case | class | data | default | deriving | do
+    //               |    else | if | import | in | infix | infixl | infixr
+    //               |    instance | let | module | newtype | of | then
+    //               |    type | where | _
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_KEYWORD, Pattern.compile("^(?:case|class|data|default|deriving|do|else|if|import|in|infix|infixl|infixr|instance|let|module|newtype|of|then|type|where|_)(?=[^a-zA-Z0-9\\']|$)"), null}));
+    // qvarid       ->    [ modid . ] varid
+    // qconid       ->    [ modid . ] conid
+    // varid        ->    (small {small | large | digit | ' })<reservedid>
+    // conid        ->    large {small | large | digit | ' }
+    // modid        ->    conid
+    // small        ->    ascSmall | uniSmall | _
+    // ascSmall     ->    a | b | ... | z
+    // uniSmall     ->    any Unicode lowercase letter
+    // large        ->    ascLarge | uniLarge
+    // ascLarge     ->    A | B | ... | Z
+    // uniLarge     ->    any uppercase or titlecase Unicode letter
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^(?:[A-Z][\\w\\']*\\.)*[a-zA-Z][\\w\\']*")}));
+    // matches the symbol production
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PUNCTUATION, Pattern.compile("^[^\\t\\n\\x0B\\x0C\\r a-zA-Z0-9\\'\\\"]+")}));
 
-        setShortcutStylePatterns(_shortcutStylePatterns);
-        setFallthroughStylePatterns(_fallthroughStylePatterns);
-    }
+    setShortcutStylePatterns(_shortcutStylePatterns);
+    setFallthroughStylePatterns(_fallthroughStylePatterns);
+  }
 
-    public static List<String> getFileExtensions() {
-        return Arrays.asList(new String[]{"hs"});
-    }
+  public static List<String> getFileExtensions() {
+    return Arrays.asList(new String[]{"hs"});
+  }
 }
diff --git a/src/prettify/lang/LangLisp.java b/src/prettify/lang/LangLisp.java
index 2bc6072..9e7721e 100644
--- a/src/prettify/lang/LangLisp.java
+++ b/src/prettify/lang/LangLisp.java
@@ -17,8 +17,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.regex.Pattern;
-import prettify.Lang;
-import prettify.Prettify;
+import prettify.parser.Prettify;
 
 /**
  * This is similar to the lang-lisp.js in JavaScript Prettify.
@@ -76,33 +75,33 @@
  */
 public class LangLisp extends Lang {
 
-    public LangLisp() {
-        List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
-        List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
+  public LangLisp() {
+    List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
+    List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
 
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{"opn", Pattern.compile("^\\(+"), null, "("}));
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{"clo", Pattern.compile("^\\)+"), null, ")"}));
-        // A line comment that starts with ;
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^;[^\r\n]*"), null, ";"}));
-        // Whitespace
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[\t\n\r \\xA0]+"), null, "\t\n\r " + Character.toString((char) 0xA0)}));
-        // A double quoted, possibly multi-line, string.
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^\\\"(?:[^\\\"\\\\]|\\\\[\\s\\S])*(?:\\\"|$)"), null, "\""}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_KEYWORD, Pattern.compile("^(?:block|c[ad]+r|catch|con[ds]|def(?:ine|un)|do|eq|eql|equal|equalp|eval-when|flet|format|go|if|labels|lambda|let|load-time-value|locally|macrolet|multiple-value-call|nil|progn|progv|quote|require|return-from|setq|symbol-macrolet|t|tagbody|the|throw|unwind)\\b", Pattern.CASE_INSENSITIVE), null}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^[+\\-]?(?:[0#]x[0-9a-f]+|\\d+\\/\\d+|(?:\\.\\d+|\\d+(?:\\.\\d*)?)(?:[ed][+\\-]?\\d+)?)", Pattern.CASE_INSENSITIVE)}));
-        // A single quote possibly followed by a word that optionally ends with
-        // = ! or ?.
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^\\'(?:-*(?:\\w|\\\\[\\x21-\\x7e])(?:[\\w-]*|\\\\[\\x21-\\x7e])[=!?]?)?")}));
-        // A word that optionally ends with = ! or ?.
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^-*(?:[a-z_]|\\\\[\\x21-\\x7e])(?:[\\w-]*|\\\\[\\x21-\\x7e])[=!?]?", Pattern.CASE_INSENSITIVE)}));
-        // A printable non-space non-special character
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PUNCTUATION, Pattern.compile("^[^\\w\\t\\n\\r \\xA0()\\\"\\\\\\';]+")}));
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{"opn", Pattern.compile("^\\(+"), null, "("}));
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{"clo", Pattern.compile("^\\)+"), null, ")"}));
+    // A line comment that starts with ;
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^;[^\r\n]*"), null, ";"}));
+    // Whitespace
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[\t\n\r \\xA0]+"), null, "\t\n\r " + Character.toString((char) 0xA0)}));
+    // A double quoted, possibly multi-line, string.
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^\\\"(?:[^\\\"\\\\]|\\\\[\\s\\S])*(?:\\\"|$)"), null, "\""}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_KEYWORD, Pattern.compile("^(?:block|c[ad]+r|catch|con[ds]|def(?:ine|un)|do|eq|eql|equal|equalp|eval-when|flet|format|go|if|labels|lambda|let|load-time-value|locally|macrolet|multiple-value-call|nil|progn|progv|quote|require|return-from|setq|symbol-macrolet|t|tagbody|the|throw|unwind)\\b", Pattern.CASE_INSENSITIVE), null}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^[+\\-]?(?:[0#]x[0-9a-f]+|\\d+\\/\\d+|(?:\\.\\d+|\\d+(?:\\.\\d*)?)(?:[ed][+\\-]?\\d+)?)", Pattern.CASE_INSENSITIVE)}));
+    // A single quote possibly followed by a word that optionally ends with
+    // = ! or ?.
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^\\'(?:-*(?:\\w|\\\\[\\x21-\\x7e])(?:[\\w-]*|\\\\[\\x21-\\x7e])[=!?]?)?")}));
+    // A word that optionally ends with = ! or ?.
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^-*(?:[a-z_]|\\\\[\\x21-\\x7e])(?:[\\w-]*|\\\\[\\x21-\\x7e])[=!?]?", Pattern.CASE_INSENSITIVE)}));
+    // A printable non-space non-special character
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PUNCTUATION, Pattern.compile("^[^\\w\\t\\n\\r \\xA0()\\\"\\\\\\';]+")}));
 
-        setShortcutStylePatterns(_shortcutStylePatterns);
-        setFallthroughStylePatterns(_fallthroughStylePatterns);
-    }
+    setShortcutStylePatterns(_shortcutStylePatterns);
+    setFallthroughStylePatterns(_fallthroughStylePatterns);
+  }
 
-    public static List<String> getFileExtensions() {
-        return Arrays.asList(new String[]{"cl", "el", "lisp", "lsp", "scm"});
-    }
+  public static List<String> getFileExtensions() {
+    return Arrays.asList(new String[]{"cl", "el", "lisp", "lsp", "scm"});
+  }
 }
diff --git a/src/prettify/lang/LangLua.java b/src/prettify/lang/LangLua.java
index e32cc26..c0576da 100644
--- a/src/prettify/lang/LangLua.java
+++ b/src/prettify/lang/LangLua.java
@@ -17,8 +17,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.regex.Pattern;
-import prettify.Lang;
-import prettify.Prettify;
+import prettify.parser.Prettify;
 
 /**
  * This is similar to the lang-lua.js in JavaScript Prettify.
@@ -43,33 +42,33 @@
  */
 public class LangLua extends Lang {
 
-    public LangLua() {
-        List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
-        List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
+  public LangLua() {
+    List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
+    List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
 
-        // Whitespace
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[\t\n\r \\xA0]+"), null, "\t\n\r " + Character.toString((char) 0xA0)}));
-        // A double or single quoted, possibly multi-line, string.
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^(?:\\\"(?:[^\\\"\\\\]|\\\\[\\s\\S])*(?:\\\"|$)|\\'(?:[^\\'\\\\]|\\\\[\\s\\S])*(?:\\'|$))"), null, "\"'"}));
-        // A comment is either a line comment that starts with two dashes, or
-        // two dashes preceding a long bracketed block.
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^--(?:\\[(=*)\\[[\\s\\S]*?(?:\\]\\1\\]|$)|[^\\r\\n]*)")}));
-        // A long bracketed block not preceded by -- is a string.
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^\\[(=*)\\[[\\s\\S]*?(?:\\]\\1\\]|$)")}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_KEYWORD, Pattern.compile("^(?:and|break|do|else|elseif|end|false|for|function|if|in|local|nil|not|or|repeat|return|then|true|until|while)\\b"), null}));
-        // A number is a hex integer literal, a decimal real literal, or in
-        // scientific notation.
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^[+-]?(?:0x[\\da-f]+|(?:(?:\\.\\d+|\\d+(?:\\.\\d*)?)(?:e[+\\-]?\\d+)?))", Pattern.CASE_INSENSITIVE)}));
-        // An identifier
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[a-z_]\\w*", Pattern.CASE_INSENSITIVE)}));
-        // A run of punctuation
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PUNCTUATION, Pattern.compile("^[^\\w\\t\\n\\r \\xA0][^\\w\\n\\r \\xA0\\\"\\'\\-\\+=]*")}));
+    // Whitespace
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[\t\n\r \\xA0]+"), null, "\t\n\r " + Character.toString((char) 0xA0)}));
+    // A double or single quoted, possibly multi-line, string.
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^(?:\\\"(?:[^\\\"\\\\]|\\\\[\\s\\S])*(?:\\\"|$)|\\'(?:[^\\'\\\\]|\\\\[\\s\\S])*(?:\\'|$))"), null, "\"'"}));
+    // A comment is either a line comment that starts with two dashes, or
+    // two dashes preceding a long bracketed block.
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^--(?:\\[(=*)\\[[\\s\\S]*?(?:\\]\\1\\]|$)|[^\\r\\n]*)")}));
+    // A long bracketed block not preceded by -- is a string.
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^\\[(=*)\\[[\\s\\S]*?(?:\\]\\1\\]|$)")}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_KEYWORD, Pattern.compile("^(?:and|break|do|else|elseif|end|false|for|function|if|in|local|nil|not|or|repeat|return|then|true|until|while)\\b"), null}));
+    // A number is a hex integer literal, a decimal real literal, or in
+    // scientific notation.
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^[+-]?(?:0x[\\da-f]+|(?:(?:\\.\\d+|\\d+(?:\\.\\d*)?)(?:e[+\\-]?\\d+)?))", Pattern.CASE_INSENSITIVE)}));
+    // An identifier
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[a-z_]\\w*", Pattern.CASE_INSENSITIVE)}));
+    // A run of punctuation
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PUNCTUATION, Pattern.compile("^[^\\w\\t\\n\\r \\xA0][^\\w\\n\\r \\xA0\\\"\\'\\-\\+=]*")}));
 
-        setShortcutStylePatterns(_shortcutStylePatterns);
-        setFallthroughStylePatterns(_fallthroughStylePatterns);
-    }
+    setShortcutStylePatterns(_shortcutStylePatterns);
+    setFallthroughStylePatterns(_fallthroughStylePatterns);
+  }
 
-    public static List<String> getFileExtensions() {
-        return Arrays.asList(new String[]{"lua"});
-    }
+  public static List<String> getFileExtensions() {
+    return Arrays.asList(new String[]{"lua"});
+  }
 }
diff --git a/src/prettify/lang/LangMl.java b/src/prettify/lang/LangMl.java
index 82eaf39..7b302c1 100644
--- a/src/prettify/lang/LangMl.java
+++ b/src/prettify/lang/LangMl.java
@@ -17,8 +17,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.regex.Pattern;
-import prettify.Lang;
-import prettify.Prettify;
+import prettify.parser.Prettify;
 
 /**
  * This is similar to the lang-ml.js in JavaScript Prettify.
@@ -35,36 +34,36 @@
  */
 public class LangMl extends Lang {
 
-    public LangMl() {
-        List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
-        List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
+  public LangMl() {
+    List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
+    List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
 
-        // Whitespace is made up of spaces, tabs and newline characters.
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[\\t\\n\\r \\xA0]+"), null, "\t\n\r " + Character.toString((char) 0xA0)}));
-        // #if ident/#else/#endif directives delimit conditional compilation
-        // sections
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^#(?:if[\\t\\n\\r \\xA0]+(?:[a-z_$][\\w\\']*|``[^\\r\\n\\t`]*(?:``|$))|else|endif|light)", Pattern.CASE_INSENSITIVE), null, "#"}));
-        // A double or single quoted, possibly multi-line, string.
-        // F# allows escaped newlines in strings.
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^(?:\\\"(?:[^\\\"\\\\]|\\\\[\\s\\S])*(?:\\\"|$)|\\'(?:[^\\'\\\\]|\\\\[\\s\\S])(?:\\'|$))"), null, "\"'"}));
-        // Block comments are delimited by (* and *) and may be
-        // nested. Single-line comments begin with // and extend to
-        // the end of a line.
-        // TODO: (*...*) comments can be nested.  This does not handle that.
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^(?:\\/\\/[^\\r\\n]*|\\(\\*[\\s\\S]*?\\*\\))")}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_KEYWORD, Pattern.compile("^(?:abstract|and|as|assert|begin|class|default|delegate|do|done|downcast|downto|elif|else|end|exception|extern|false|finally|for|fun|function|if|in|inherit|inline|interface|internal|lazy|let|match|member|module|mutable|namespace|new|null|of|open|or|override|private|public|rec|return|static|struct|then|to|true|try|type|upcast|use|val|void|when|while|with|yield|asr|land|lor|lsl|lsr|lxor|mod|sig|atomic|break|checked|component|const|constraint|constructor|continue|eager|event|external|fixed|functor|global|include|method|mixin|object|parallel|process|protected|pure|sealed|trait|virtual|volatile)\\b")}));
-        // A number is a hex integer literal, a decimal real literal, or in
-        // scientific notation.
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^[+\\-]?(?:0x[\\da-f]+|(?:(?:\\.\\d+|\\d+(?:\\.\\d*)?)(?:e[+\\-]?\\d+)?))", Pattern.CASE_INSENSITIVE)}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^(?:[a-z_][\\w']*[!?#]?|``[^\\r\\n\\t`]*(?:``|$))", Pattern.CASE_INSENSITIVE)}));
-        // A printable non-space non-special character
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PUNCTUATION, Pattern.compile("^[^\\t\\n\\r \\xA0\\\"\\'\\w]+")}));
+    // Whitespace is made up of spaces, tabs and newline characters.
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[\\t\\n\\r \\xA0]+"), null, "\t\n\r " + Character.toString((char) 0xA0)}));
+    // #if ident/#else/#endif directives delimit conditional compilation
+    // sections
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^#(?:if[\\t\\n\\r \\xA0]+(?:[a-z_$][\\w\\']*|``[^\\r\\n\\t`]*(?:``|$))|else|endif|light)", Pattern.CASE_INSENSITIVE), null, "#"}));
+    // A double or single quoted, possibly multi-line, string.
+    // F# allows escaped newlines in strings.
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^(?:\\\"(?:[^\\\"\\\\]|\\\\[\\s\\S])*(?:\\\"|$)|\\'(?:[^\\'\\\\]|\\\\[\\s\\S])(?:\\'|$))"), null, "\"'"}));
+    // Block comments are delimited by (* and *) and may be
+    // nested. Single-line comments begin with // and extend to
+    // the end of a line.
+    // TODO: (*...*) comments can be nested.  This does not handle that.
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^(?:\\/\\/[^\\r\\n]*|\\(\\*[\\s\\S]*?\\*\\))")}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_KEYWORD, Pattern.compile("^(?:abstract|and|as|assert|begin|class|default|delegate|do|done|downcast|downto|elif|else|end|exception|extern|false|finally|for|fun|function|if|in|inherit|inline|interface|internal|lazy|let|match|member|module|mutable|namespace|new|null|of|open|or|override|private|public|rec|return|static|struct|then|to|true|try|type|upcast|use|val|void|when|while|with|yield|asr|land|lor|lsl|lsr|lxor|mod|sig|atomic|break|checked|component|const|constraint|constructor|continue|eager|event|external|fixed|functor|global|include|method|mixin|object|parallel|process|protected|pure|sealed|trait|virtual|volatile)\\b")}));
+    // A number is a hex integer literal, a decimal real literal, or in
+    // scientific notation.
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^[+\\-]?(?:0x[\\da-f]+|(?:(?:\\.\\d+|\\d+(?:\\.\\d*)?)(?:e[+\\-]?\\d+)?))", Pattern.CASE_INSENSITIVE)}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^(?:[a-z_][\\w']*[!?#]?|``[^\\r\\n\\t`]*(?:``|$))", Pattern.CASE_INSENSITIVE)}));
+    // A printable non-space non-special character
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PUNCTUATION, Pattern.compile("^[^\\t\\n\\r \\xA0\\\"\\'\\w]+")}));
 
-        setShortcutStylePatterns(_shortcutStylePatterns);
-        setFallthroughStylePatterns(_fallthroughStylePatterns);
-    }
+    setShortcutStylePatterns(_shortcutStylePatterns);
+    setFallthroughStylePatterns(_fallthroughStylePatterns);
+  }
 
-    public static List<String> getFileExtensions() {
-        return Arrays.asList(new String[]{"fs", "ml"});
-    }
+  public static List<String> getFileExtensions() {
+    return Arrays.asList(new String[]{"fs", "ml"});
+  }
 }
diff --git a/src/prettify/lang/LangN.java b/src/prettify/lang/LangN.java
index 97640fc..0e902fa 100644
--- a/src/prettify/lang/LangN.java
+++ b/src/prettify/lang/LangN.java
@@ -17,8 +17,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.regex.Pattern;
-import prettify.Lang;
-import prettify.Prettify;
+import prettify.parser.Prettify;
 
 /**
  * This is similar to the lang-n.js in JavaScript Prettify.
@@ -32,47 +31,47 @@
  */
 public class LangN extends Lang {
 
-    protected static String keywords = "abstract|and|as|base|catch|class|def|delegate|enum|event|extern|false|finally|"
-            + "fun|implements|interface|internal|is|macro|match|matches|module|mutable|namespace|new|"
-            + "null|out|override|params|partial|private|protected|public|ref|sealed|static|struct|"
-            + "syntax|this|throw|true|try|type|typeof|using|variant|virtual|volatile|when|where|with|"
-            + "assert|assert2|async|break|checked|continue|do|else|ensures|for|foreach|if|late|lock|new|nolate|"
-            + "otherwise|regexp|repeat|requires|return|surroundwith|unchecked|unless|using|while|yield";
+  protected static String keywords = "abstract|and|as|base|catch|class|def|delegate|enum|event|extern|false|finally|"
+          + "fun|implements|interface|internal|is|macro|match|matches|module|mutable|namespace|new|"
+          + "null|out|override|params|partial|private|protected|public|ref|sealed|static|struct|"
+          + "syntax|this|throw|true|try|type|typeof|using|variant|virtual|volatile|when|where|with|"
+          + "assert|assert2|async|break|checked|continue|do|else|ensures|for|foreach|if|late|lock|new|nolate|"
+          + "otherwise|regexp|repeat|requires|return|surroundwith|unchecked|unless|using|while|yield";
 
-    public LangN() {
-        List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
-        List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
+  public LangN() {
+    List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
+    List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
 
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^(?:\\'(?:[^\\\\\\'\\r\\n]|\\\\.)*\\'|\\\"(?:[^\\\\\\\"\\r\\n]|\\\\.)*(?:\\\"|$))"), null, "\""}));
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\\b|[^\\r\\n]*)"), null, "#"}));
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^\\s+"), null, " \r\n\t" + Character.toString((char) 0xA0)}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^@\\\"(?:[^\\\"]|\\\"\\\")*(?:\\\"|$)"), null}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^<#(?:[^#>])*(?:#>|$)"), null}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^<(?:(?:(?:\\.\\.\\/)*|\\/?)(?:[\\w-]+(?:\\/[\\w-]+)+)?[\\w-]+\\.h|[a-z]\\w*)>"), null,}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^\\/\\/[^\\r\\n]*"), null}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^\\/\\*[\\s\\S]*?(?:\\*\\/|$)"), null}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_KEYWORD, Pattern.compile("^(?:" + keywords + ")\\\\b"), null}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_TYPE, Pattern.compile("^(?:array|bool|byte|char|decimal|double|float|int|list|long|object|sbyte|short|string|ulong|uint|ufloat|ulong|ushort|void)\\b"), null}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^@[a-z_$][a-z_$@0-9]*", Pattern.CASE_INSENSITIVE), null}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_TYPE, Pattern.compile("^@[A-Z]+[a-z][A-Za-z_$@0-9]*"), null}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^'?[A-Za-z_$][a-z_$@0-9]*", Pattern.CASE_INSENSITIVE), null}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^(?:"
-                    // A hex number
-                    + "0x[a-f0-9]+"
-                    // or an octal or decimal number,
-                    + "|(?:\\\\d(?:_\\\\d+)*\\\\d*(?:\\\\.\\\\d*)?|\\\\.\\\\d\\\\+)"
-                    // possibly in scientific notation
-                    + "(?:e[+\\\\-]?\\\\d+)?"
-                    + ")"
-                    // with an optional modifier like UL for unsigned long
-                    + "[a-z]*", Pattern.CASE_INSENSITIVE), null, "0123456789"}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PUNCTUATION, Pattern.compile("^.[^\\s\\w\\.$@\\'\\\"\\`\\/\\#]*"), null}));
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^(?:\\'(?:[^\\\\\\'\\r\\n]|\\\\.)*\\'|\\\"(?:[^\\\\\\\"\\r\\n]|\\\\.)*(?:\\\"|$))"), null, "\""}));
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\\b|[^\\r\\n]*)"), null, "#"}));
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^\\s+"), null, " \r\n\t" + Character.toString((char) 0xA0)}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^@\\\"(?:[^\\\"]|\\\"\\\")*(?:\\\"|$)"), null}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^<#(?:[^#>])*(?:#>|$)"), null}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^<(?:(?:(?:\\.\\.\\/)*|\\/?)(?:[\\w-]+(?:\\/[\\w-]+)+)?[\\w-]+\\.h|[a-z]\\w*)>"), null,}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^\\/\\/[^\\r\\n]*"), null}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^\\/\\*[\\s\\S]*?(?:\\*\\/|$)"), null}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_KEYWORD, Pattern.compile("^(?:" + keywords + ")\\\\b"), null}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_TYPE, Pattern.compile("^(?:array|bool|byte|char|decimal|double|float|int|list|long|object|sbyte|short|string|ulong|uint|ufloat|ulong|ushort|void)\\b"), null}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^@[a-z_$][a-z_$@0-9]*", Pattern.CASE_INSENSITIVE), null}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_TYPE, Pattern.compile("^@[A-Z]+[a-z][A-Za-z_$@0-9]*"), null}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^'?[A-Za-z_$][a-z_$@0-9]*", Pattern.CASE_INSENSITIVE), null}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^(?:"
+              // A hex number
+              + "0x[a-f0-9]+"
+              // or an octal or decimal number,
+              + "|(?:\\\\d(?:_\\\\d+)*\\\\d*(?:\\\\.\\\\d*)?|\\\\.\\\\d\\\\+)"
+              // possibly in scientific notation
+              + "(?:e[+\\\\-]?\\\\d+)?"
+              + ")"
+              // with an optional modifier like UL for unsigned long
+              + "[a-z]*", Pattern.CASE_INSENSITIVE), null, "0123456789"}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PUNCTUATION, Pattern.compile("^.[^\\s\\w\\.$@\\'\\\"\\`\\/\\#]*"), null}));
 
-        setShortcutStylePatterns(_shortcutStylePatterns);
-        setFallthroughStylePatterns(_fallthroughStylePatterns);
-    }
+    setShortcutStylePatterns(_shortcutStylePatterns);
+    setFallthroughStylePatterns(_fallthroughStylePatterns);
+  }
 
-    public static List<String> getFileExtensions() {
-        return Arrays.asList(new String[]{"n", "nemerle"});
-    }
+  public static List<String> getFileExtensions() {
+    return Arrays.asList(new String[]{"n", "nemerle"});
+  }
 }
diff --git a/src/prettify/lang/LangScala.java b/src/prettify/lang/LangScala.java
index 92da40e..067f511 100644
--- a/src/prettify/lang/LangScala.java
+++ b/src/prettify/lang/LangScala.java
@@ -17,8 +17,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.regex.Pattern;
-import prettify.Lang;
-import prettify.Prettify;
+import prettify.parser.Prettify;
 
 /**
  * This is similar to the lang-scala.js in JavaScript Prettify.
@@ -34,36 +33,36 @@
  */
 public class LangScala extends Lang {
 
-    public LangScala() {
-        List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
-        List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
+  public LangScala() {
+    List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
+    List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
 
-        // Whitespace
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[\\t\\n\\r \\xA0]+"), null, "\t\n\r " + Character.toString((char) 0xA0)}));
-        // A double or single quoted string 
-        // or a triple double-quoted multi-line string.
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^(?:\"(?:(?:\"\"(?:\"\"?(?!\")|[^\\\\\"]|\\\\.)*\"{0,3})|(?:[^\"\\r\\n\\\\]|\\\\.)*\"?))"), null, "\""}));
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^`(?:[^\\r\\n\\\\`]|\\\\.)*`?"), null, "`"}));
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PUNCTUATION, Pattern.compile("^[!#%&()*+,\\-:;<=>?@\\[\\\\\\]^{|}~]+"), null, "!#%&()*+,-:;<=>?@[\\\\]^{|}~"}));
-        // A symbol literal is a single quote followed by an identifier with no
-        // single quote following
-        // A character literal has single quotes on either side
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^'(?:[^\\r\\n\\\\']|\\\\(?:'|[^\\r\\n']+))'")}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^'[a-zA-Z_$][\\w$]*(?!['$\\w])")}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_KEYWORD, Pattern.compile("^(?:abstract|case|catch|class|def|do|else|extends|final|finally|for|forSome|if|implicit|import|lazy|match|new|object|override|package|private|protected|requires|return|sealed|super|throw|trait|try|type|val|var|while|with|yield)\\b")}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^(?:true|false|null|this)\\b")}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^(?:(?:0(?:[0-7]+|X[0-9A-F]+))L?|(?:(?:0|[1-9][0-9]*)(?:(?:\\.[0-9]+)?(?:E[+\\-]?[0-9]+)?F?|L?))|\\\\.[0-9]+(?:E[+\\-]?[0-9]+)?F?)", Pattern.CASE_INSENSITIVE)}));
-        // Treat upper camel case identifiers as types.
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_TYPE, Pattern.compile("^[$_]*[A-Z][_$A-Z0-9]*[a-z][\\w$]*")}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[$a-zA-Z_][\\w$]*")}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^\\/(?:\\/.*|\\*(?:\\/|\\**[^*/])*(?:\\*+\\/?)?)")}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PUNCTUATION, Pattern.compile("^(?:\\.+|\\/)")}));
+    // Whitespace
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[\\t\\n\\r \\xA0]+"), null, "\t\n\r " + Character.toString((char) 0xA0)}));
+    // A double or single quoted string 
+    // or a triple double-quoted multi-line string.
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^(?:\"(?:(?:\"\"(?:\"\"?(?!\")|[^\\\\\"]|\\\\.)*\"{0,3})|(?:[^\"\\r\\n\\\\]|\\\\.)*\"?))"), null, "\""}));
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^`(?:[^\\r\\n\\\\`]|\\\\.)*`?"), null, "`"}));
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PUNCTUATION, Pattern.compile("^[!#%&()*+,\\-:;<=>?@\\[\\\\\\]^{|}~]+"), null, "!#%&()*+,-:;<=>?@[\\\\]^{|}~"}));
+    // A symbol literal is a single quote followed by an identifier with no
+    // single quote following
+    // A character literal has single quotes on either side
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^'(?:[^\\r\\n\\\\']|\\\\(?:'|[^\\r\\n']+))'")}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^'[a-zA-Z_$][\\w$]*(?!['$\\w])")}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_KEYWORD, Pattern.compile("^(?:abstract|case|catch|class|def|do|else|extends|final|finally|for|forSome|if|implicit|import|lazy|match|new|object|override|package|private|protected|requires|return|sealed|super|throw|trait|try|type|val|var|while|with|yield)\\b")}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^(?:true|false|null|this)\\b")}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^(?:(?:0(?:[0-7]+|X[0-9A-F]+))L?|(?:(?:0|[1-9][0-9]*)(?:(?:\\.[0-9]+)?(?:E[+\\-]?[0-9]+)?F?|L?))|\\\\.[0-9]+(?:E[+\\-]?[0-9]+)?F?)", Pattern.CASE_INSENSITIVE)}));
+    // Treat upper camel case identifiers as types.
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_TYPE, Pattern.compile("^[$_]*[A-Z][_$A-Z0-9]*[a-z][\\w$]*")}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[$a-zA-Z_][\\w$]*")}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^\\/(?:\\/.*|\\*(?:\\/|\\**[^*/])*(?:\\*+\\/?)?)")}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PUNCTUATION, Pattern.compile("^(?:\\.+|\\/)")}));
 
-        setShortcutStylePatterns(_shortcutStylePatterns);
-        setFallthroughStylePatterns(_fallthroughStylePatterns);
-    }
+    setShortcutStylePatterns(_shortcutStylePatterns);
+    setFallthroughStylePatterns(_fallthroughStylePatterns);
+  }
 
-    public static List<String> getFileExtensions() {
-        return Arrays.asList(new String[]{"scala"});
-    }
+  public static List<String> getFileExtensions() {
+    return Arrays.asList(new String[]{"scala"});
+  }
 }
diff --git a/src/prettify/lang/LangSql.java b/src/prettify/lang/LangSql.java
index c97aa47..2610fc8 100644
--- a/src/prettify/lang/LangSql.java
+++ b/src/prettify/lang/LangSql.java
@@ -17,8 +17,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.regex.Pattern;
-import prettify.Lang;
-import prettify.Prettify;
+import prettify.parser.Prettify;
 
 /**
  * This is similar to the lang-sql.js in JavaScript Prettify.
@@ -42,31 +41,31 @@
  */
 public class LangSql extends Lang {
 
-    public LangSql() {
-        List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
-        List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
+  public LangSql() {
+    List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
+    List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
 
-        // Whitespace
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[\\t\\n\\r \\xA0]+"), null, "\t\n\r " + Character.toString((char) 0xA0)}));
-        // A double or single quoted, possibly multi-line, string.
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^(?:\"(?:[^\\\"\\\\]|\\\\.)*\"|'(?:[^\\'\\\\]|\\\\.)*')"), null, "\"'"}));
-        // A comment is either a line comment that starts with two dashes, or
-        // two dashes preceding a long bracketed block.
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^(?:--[^\\r\\n]*|\\/\\*[\\s\\S]*?(?:\\*\\/|$))")}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_KEYWORD, Pattern.compile("^(?:ADD|ALL|ALTER|AND|ANY|AS|ASC|AUTHORIZATION|BACKUP|BEGIN|BETWEEN|BREAK|BROWSE|BULK|BY|CASCADE|CASE|CHECK|CHECKPOINT|CLOSE|CLUSTERED|COALESCE|COLLATE|COLUMN|COMMIT|COMPUTE|CONSTRAINT|CONTAINS|CONTAINSTABLE|CONTINUE|CONVERT|CREATE|CROSS|CURRENT|CURRENT_DATE|CURRENT_TIME|CURRENT_TIMESTAMP|CURRENT_USER|CURSOR|DATABASE|DBCC|DEALLOCATE|DECLARE|DEFAULT|DELETE|DENY|DESC|DISK|DISTINCT|DISTRIBUTED|DOUBLE|DROP|DUMMY|DUMP|ELSE|END|ERRLVL|ESCAPE|EXCEPT|EXEC|EXECUTE|EXISTS|EXIT|FETCH|FILE|FILLFACTOR|FOR|FOREIGN|FREETEXT|FREETEXTTABLE|FROM|FULL|FUNCTION|GOTO|GRANT|GROUP|HAVING|HOLDLOCK|IDENTITY|IDENTITYCOL|IDENTITY_INSERT|IF|IN|INDEX|INNER|INSERT|INTERSECT|INTO|IS|JOIN|KEY|KILL|LEFT|LIKE|LINENO|LOAD|MATCH|MERGE|NATIONAL|NOCHECK|NONCLUSTERED|NOT|NULL|NULLIF|OF|OFF|OFFSETS|ON|OPEN|OPENDATASOURCE|OPENQUERY|OPENROWSET|OPENXML|OPTION|OR|ORDER|OUTER|OVER|PERCENT|PLAN|PRECISION|PRIMARY|PRINT|PROC|PROCEDURE|PUBLIC|RAISERROR|READ|READTEXT|RECONFIGURE|REFERENCES|REPLICATION|RESTORE|RESTRICT|RETURN|REVOKE|RIGHT|ROLLBACK|ROWCOUNT|ROWGUIDCOL|RULE|SAVE|SCHEMA|SELECT|SESSION_USER|SET|SETUSER|SHUTDOWN|SOME|STATISTICS|SYSTEM_USER|TABLE|TEXTSIZE|THEN|TO|TOP|TRAN|TRANSACTION|TRIGGER|TRUNCATE|TSEQUAL|UNION|UNIQUE|UPDATE|UPDATETEXT|USE|USER|USING|VALUES|VARYING|VIEW|WAITFOR|WHEN|WHERE|WHILE|WITH|WRITETEXT)(?=[^\\w-]|$)", Pattern.CASE_INSENSITIVE), null}));
-        // A number is a hex integer literal, a decimal real literal, or in
-        // scientific notation.
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^[+-]?(?:0x[\\da-f]+|(?:(?:\\.\\d+|\\d+(?:\\.\\d*)?)(?:e[+\\-]?\\d+)?))", Pattern.CASE_INSENSITIVE)}));
-        // An identifier
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[a-z_][\\w-]*", Pattern.CASE_INSENSITIVE)}));
-        // A run of punctuation
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PUNCTUATION, Pattern.compile("^[^\\w\\t\\n\\r \\xA0\\\"\\'][^\\w\\t\\n\\r \\xA0+\\-\\\"\\']*")}));
+    // Whitespace
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[\\t\\n\\r \\xA0]+"), null, "\t\n\r " + Character.toString((char) 0xA0)}));
+    // A double or single quoted, possibly multi-line, string.
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^(?:\"(?:[^\\\"\\\\]|\\\\.)*\"|'(?:[^\\'\\\\]|\\\\.)*')"), null, "\"'"}));
+    // A comment is either a line comment that starts with two dashes, or
+    // two dashes preceding a long bracketed block.
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^(?:--[^\\r\\n]*|\\/\\*[\\s\\S]*?(?:\\*\\/|$))")}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_KEYWORD, Pattern.compile("^(?:ADD|ALL|ALTER|AND|ANY|AS|ASC|AUTHORIZATION|BACKUP|BEGIN|BETWEEN|BREAK|BROWSE|BULK|BY|CASCADE|CASE|CHECK|CHECKPOINT|CLOSE|CLUSTERED|COALESCE|COLLATE|COLUMN|COMMIT|COMPUTE|CONSTRAINT|CONTAINS|CONTAINSTABLE|CONTINUE|CONVERT|CREATE|CROSS|CURRENT|CURRENT_DATE|CURRENT_TIME|CURRENT_TIMESTAMP|CURRENT_USER|CURSOR|DATABASE|DBCC|DEALLOCATE|DECLARE|DEFAULT|DELETE|DENY|DESC|DISK|DISTINCT|DISTRIBUTED|DOUBLE|DROP|DUMMY|DUMP|ELSE|END|ERRLVL|ESCAPE|EXCEPT|EXEC|EXECUTE|EXISTS|EXIT|FETCH|FILE|FILLFACTOR|FOR|FOREIGN|FREETEXT|FREETEXTTABLE|FROM|FULL|FUNCTION|GOTO|GRANT|GROUP|HAVING|HOLDLOCK|IDENTITY|IDENTITYCOL|IDENTITY_INSERT|IF|IN|INDEX|INNER|INSERT|INTERSECT|INTO|IS|JOIN|KEY|KILL|LEFT|LIKE|LINENO|LOAD|MATCH|MERGE|NATIONAL|NOCHECK|NONCLUSTERED|NOT|NULL|NULLIF|OF|OFF|OFFSETS|ON|OPEN|OPENDATASOURCE|OPENQUERY|OPENROWSET|OPENXML|OPTION|OR|ORDER|OUTER|OVER|PERCENT|PLAN|PRECISION|PRIMARY|PRINT|PROC|PROCEDURE|PUBLIC|RAISERROR|READ|READTEXT|RECONFIGURE|REFERENCES|REPLICATION|RESTORE|RESTRICT|RETURN|REVOKE|RIGHT|ROLLBACK|ROWCOUNT|ROWGUIDCOL|RULE|SAVE|SCHEMA|SELECT|SESSION_USER|SET|SETUSER|SHUTDOWN|SOME|STATISTICS|SYSTEM_USER|TABLE|TEXTSIZE|THEN|TO|TOP|TRAN|TRANSACTION|TRIGGER|TRUNCATE|TSEQUAL|UNION|UNIQUE|UPDATE|UPDATETEXT|USE|USER|USING|VALUES|VARYING|VIEW|WAITFOR|WHEN|WHERE|WHILE|WITH|WRITETEXT)(?=[^\\w-]|$)", Pattern.CASE_INSENSITIVE), null}));
+    // A number is a hex integer literal, a decimal real literal, or in
+    // scientific notation.
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^[+-]?(?:0x[\\da-f]+|(?:(?:\\.\\d+|\\d+(?:\\.\\d*)?)(?:e[+\\-]?\\d+)?))", Pattern.CASE_INSENSITIVE)}));
+    // An identifier
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[a-z_][\\w-]*", Pattern.CASE_INSENSITIVE)}));
+    // A run of punctuation
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PUNCTUATION, Pattern.compile("^[^\\w\\t\\n\\r \\xA0\\\"\\'][^\\w\\t\\n\\r \\xA0+\\-\\\"\\']*")}));
 
-        setShortcutStylePatterns(_shortcutStylePatterns);
-        setFallthroughStylePatterns(_fallthroughStylePatterns);
-    }
+    setShortcutStylePatterns(_shortcutStylePatterns);
+    setFallthroughStylePatterns(_fallthroughStylePatterns);
+  }
 
-    public static List<String> getFileExtensions() {
-        return Arrays.asList(new String[]{"sql"});
-    }
+  public static List<String> getFileExtensions() {
+    return Arrays.asList(new String[]{"sql"});
+  }
 }
diff --git a/src/prettify/lang/LangTex.java b/src/prettify/lang/LangTex.java
index c9a16c3..cfecb3f 100644
--- a/src/prettify/lang/LangTex.java
+++ b/src/prettify/lang/LangTex.java
@@ -17,8 +17,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.regex.Pattern;
-import prettify.Lang;
-import prettify.Prettify;
+import prettify.parser.Prettify;
 
 /**
  * This is similar to the lang-tex.js in JavaScript Prettify.
@@ -33,32 +32,32 @@
  */
 public class LangTex extends Lang {
 
-    public LangTex() {
-        List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
-        List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
+  public LangTex() {
+    List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
+    List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
 
-        // whitespace
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[\\t\\n\\r \\xA0]+"), null, "\t\n\r " + Character.toString((char) 0xA0)}));
-        // all comments begin with '%'
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^%[^\\r\\n]*"), null, "%"}));
-        //[PR['PR_DECLARATION'], /^\\([egx]?def|(new|renew|provide)(command|environment))\b/],
-        // any command starting with a \ and contains
-        // either only letters (a-z,A-Z), '@' (internal macros)
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_KEYWORD, Pattern.compile("^\\\\[a-zA-Z@]+")}));
-        // or contains only one character
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_KEYWORD, Pattern.compile("^\\\\.")}));
-        // Highlight dollar for math mode and ampersam for tabular
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_TYPE, Pattern.compile("^[$&]")}));
-        // numeric measurement values with attached units
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("[+-]?(?:\\.\\d+|\\d+(?:\\.\\d*)?)(cm|em|ex|in|pc|pt|bp|mm)", Pattern.CASE_INSENSITIVE)}));
-        // punctuation usually occurring within commands
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PUNCTUATION, Pattern.compile("^[{}()\\[\\]=]+")}));
+    // whitespace
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[\\t\\n\\r \\xA0]+"), null, "\t\n\r " + Character.toString((char) 0xA0)}));
+    // all comments begin with '%'
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^%[^\\r\\n]*"), null, "%"}));
+    //[PR['PR_DECLARATION'], /^\\([egx]?def|(new|renew|provide)(command|environment))\b/],
+    // any command starting with a \ and contains
+    // either only letters (a-z,A-Z), '@' (internal macros)
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_KEYWORD, Pattern.compile("^\\\\[a-zA-Z@]+")}));
+    // or contains only one character
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_KEYWORD, Pattern.compile("^\\\\.")}));
+    // Highlight dollar for math mode and ampersam for tabular
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_TYPE, Pattern.compile("^[$&]")}));
+    // numeric measurement values with attached units
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("[+-]?(?:\\.\\d+|\\d+(?:\\.\\d*)?)(cm|em|ex|in|pc|pt|bp|mm)", Pattern.CASE_INSENSITIVE)}));
+    // punctuation usually occurring within commands
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PUNCTUATION, Pattern.compile("^[{}()\\[\\]=]+")}));
 
-        setShortcutStylePatterns(_shortcutStylePatterns);
-        setFallthroughStylePatterns(_fallthroughStylePatterns);
-    }
+    setShortcutStylePatterns(_shortcutStylePatterns);
+    setFallthroughStylePatterns(_fallthroughStylePatterns);
+  }
 
-    public static List<String> getFileExtensions() {
-        return Arrays.asList(new String[]{"latex", "tex"});
-    }
+  public static List<String> getFileExtensions() {
+    return Arrays.asList(new String[]{"latex", "tex"});
+  }
 }
diff --git a/src/prettify/lang/LangVb.java b/src/prettify/lang/LangVb.java
index e969b7b..ee89311 100644
--- a/src/prettify/lang/LangVb.java
+++ b/src/prettify/lang/LangVb.java
@@ -17,8 +17,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.regex.Pattern;
-import prettify.Lang;
-import prettify.Prettify;
+import prettify.parser.Prettify;
 
 /**
  * This is similar to the lang-vb.js in JavaScript Prettify.
@@ -41,34 +40,34 @@
  */
 public class LangVb extends Lang {
 
-    public LangVb() {
-        List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
-        List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
+  public LangVb() {
+    List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
+    List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
 
-        // Whitespace
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[\\t\\n\\r \\xA0\\u2028\\u2029]+"), null, "\t\n\r " + Character.toString((char) 0xA0) + "\u2028\u2029"}));
-        // A double quoted string with quotes escaped by doubling them.
-        // A single character can be suffixed with C.
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^(?:[\\\"\\u201C\\u201D](?:[^\\\"\\u201C\\u201D]|[\\\"\\u201C\\u201D]{2})(?:[\\\"\\u201C\\u201D]c|$)|[\\\"\\u201C\\u201D](?:[^\\\"\\u201C\\u201D]|[\\\"\\u201C\\u201D]{2})*(?:[\\\"\\u201C\\u201D]|$))", Pattern.CASE_INSENSITIVE), null, "\"\u201C\u201D"}));
-        // A comment starts with a single quote and runs until the end of the line.
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^[\\'\\u2018\\u2019][^\\r\\n\\u2028\\u2029]*"), null, "'\u2018\u2019"}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_KEYWORD, Pattern.compile("^(?:AddHandler|AddressOf|Alias|And|AndAlso|Ansi|As|Assembly|Auto|Boolean|ByRef|Byte|ByVal|Call|Case|Catch|CBool|CByte|CChar|CDate|CDbl|CDec|Char|CInt|Class|CLng|CObj|Const|CShort|CSng|CStr|CType|Date|Decimal|Declare|Default|Delegate|Dim|DirectCast|Do|Double|Each|Else|ElseIf|End|EndIf|Enum|Erase|Error|Event|Exit|Finally|For|Friend|Function|Get|GetType|GoSub|GoTo|Handles|If|Implements|Imports|In|Inherits|Integer|Interface|Is|Let|Lib|Like|Long|Loop|Me|Mod|Module|MustInherit|MustOverride|MyBase|MyClass|Namespace|New|Next|Not|NotInheritable|NotOverridable|Object|On|Option|Optional|Or|OrElse|Overloads|Overridable|Overrides|ParamArray|Preserve|Private|Property|Protected|Public|RaiseEvent|ReadOnly|ReDim|RemoveHandler|Resume|Return|Select|Set|Shadows|Shared|Short|Single|Static|Step|Stop|String|Structure|Sub|SyncLock|Then|Throw|To|Try|TypeOf|Unicode|Until|Variant|Wend|When|While|With|WithEvents|WriteOnly|Xor|EndIf|GoSub|Let|Variant|Wend)\\b", Pattern.CASE_INSENSITIVE), null}));
-        // A second comment form
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^REM[^\\r\\n\\u2028\\u2029]*", Pattern.CASE_INSENSITIVE)}));
-        // A boolean, numeric, or date literal.
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^(?:True\\b|False\\b|Nothing\\b|\\d+(?:E[+\\-]?\\d+[FRD]?|[FRDSIL])?|(?:&H[0-9A-F]+|&O[0-7]+)[SIL]?|\\d*\\.\\d+(?:E[+\\-]?\\d+)?[FRD]?|#\\s+(?:\\d+[\\-\\/]\\d+[\\-\\/]\\d+(?:\\s+\\d+:\\d+(?::\\d+)?(\\s*(?:AM|PM))?)?|\\d+:\\d+(?::\\d+)?(\\s*(?:AM|PM))?)\\s+#)", Pattern.CASE_INSENSITIVE)}));
-        // An identifier?
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^(?:(?:[a-z]|_\\w)\\w*|\\[(?:[a-z]|_\\w)\\w*\\])", Pattern.CASE_INSENSITIVE)}));
-        // A run of punctuation
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PUNCTUATION, Pattern.compile("^[^\\w\\t\\n\\r \\\"\\'\\[\\]\\xA0\\u2018\\u2019\\u201C\\u201D\\u2028\\u2029]+")}));
-        // Square brackets
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PUNCTUATION, Pattern.compile("^(?:\\[|\\])")}));
+    // Whitespace
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[\\t\\n\\r \\xA0\\u2028\\u2029]+"), null, "\t\n\r " + Character.toString((char) 0xA0) + "\u2028\u2029"}));
+    // A double quoted string with quotes escaped by doubling them.
+    // A single character can be suffixed with C.
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^(?:[\\\"\\u201C\\u201D](?:[^\\\"\\u201C\\u201D]|[\\\"\\u201C\\u201D]{2})(?:[\\\"\\u201C\\u201D]c|$)|[\\\"\\u201C\\u201D](?:[^\\\"\\u201C\\u201D]|[\\\"\\u201C\\u201D]{2})*(?:[\\\"\\u201C\\u201D]|$))", Pattern.CASE_INSENSITIVE), null, "\"\u201C\u201D"}));
+    // A comment starts with a single quote and runs until the end of the line.
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^[\\'\\u2018\\u2019][^\\r\\n\\u2028\\u2029]*"), null, "'\u2018\u2019"}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_KEYWORD, Pattern.compile("^(?:AddHandler|AddressOf|Alias|And|AndAlso|Ansi|As|Assembly|Auto|Boolean|ByRef|Byte|ByVal|Call|Case|Catch|CBool|CByte|CChar|CDate|CDbl|CDec|Char|CInt|Class|CLng|CObj|Const|CShort|CSng|CStr|CType|Date|Decimal|Declare|Default|Delegate|Dim|DirectCast|Do|Double|Each|Else|ElseIf|End|EndIf|Enum|Erase|Error|Event|Exit|Finally|For|Friend|Function|Get|GetType|GoSub|GoTo|Handles|If|Implements|Imports|In|Inherits|Integer|Interface|Is|Let|Lib|Like|Long|Loop|Me|Mod|Module|MustInherit|MustOverride|MyBase|MyClass|Namespace|New|Next|Not|NotInheritable|NotOverridable|Object|On|Option|Optional|Or|OrElse|Overloads|Overridable|Overrides|ParamArray|Preserve|Private|Property|Protected|Public|RaiseEvent|ReadOnly|ReDim|RemoveHandler|Resume|Return|Select|Set|Shadows|Shared|Short|Single|Static|Step|Stop|String|Structure|Sub|SyncLock|Then|Throw|To|Try|TypeOf|Unicode|Until|Variant|Wend|When|While|With|WithEvents|WriteOnly|Xor|EndIf|GoSub|Let|Variant|Wend)\\b", Pattern.CASE_INSENSITIVE), null}));
+    // A second comment form
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^REM[^\\r\\n\\u2028\\u2029]*", Pattern.CASE_INSENSITIVE)}));
+    // A boolean, numeric, or date literal.
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^(?:True\\b|False\\b|Nothing\\b|\\d+(?:E[+\\-]?\\d+[FRD]?|[FRDSIL])?|(?:&H[0-9A-F]+|&O[0-7]+)[SIL]?|\\d*\\.\\d+(?:E[+\\-]?\\d+)?[FRD]?|#\\s+(?:\\d+[\\-\\/]\\d+[\\-\\/]\\d+(?:\\s+\\d+:\\d+(?::\\d+)?(\\s*(?:AM|PM))?)?|\\d+:\\d+(?::\\d+)?(\\s*(?:AM|PM))?)\\s+#)", Pattern.CASE_INSENSITIVE)}));
+    // An identifier?
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^(?:(?:[a-z]|_\\w)\\w*|\\[(?:[a-z]|_\\w)\\w*\\])", Pattern.CASE_INSENSITIVE)}));
+    // A run of punctuation
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PUNCTUATION, Pattern.compile("^[^\\w\\t\\n\\r \\\"\\'\\[\\]\\xA0\\u2018\\u2019\\u201C\\u201D\\u2028\\u2029]+")}));
+    // Square brackets
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PUNCTUATION, Pattern.compile("^(?:\\[|\\])")}));
 
-        setShortcutStylePatterns(_shortcutStylePatterns);
-        setFallthroughStylePatterns(_fallthroughStylePatterns);
-    }
+    setShortcutStylePatterns(_shortcutStylePatterns);
+    setFallthroughStylePatterns(_fallthroughStylePatterns);
+  }
 
-    public static List<String> getFileExtensions() {
-        return Arrays.asList(new String[]{"vb", "vbs"});
-    }
+  public static List<String> getFileExtensions() {
+    return Arrays.asList(new String[]{"vb", "vbs"});
+  }
 }
diff --git a/src/prettify/lang/LangVhdl.java b/src/prettify/lang/LangVhdl.java
index 48b9a62..fc13501 100644
--- a/src/prettify/lang/LangVhdl.java
+++ b/src/prettify/lang/LangVhdl.java
@@ -17,8 +17,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.regex.Pattern;
-import prettify.Lang;
-import prettify.Prettify;
+import prettify.parser.Prettify;
 
 /**
  * This is similar to the lang-vhdl.js in JavaScript Prettify.
@@ -35,33 +34,33 @@
  */
 public class LangVhdl extends Lang {
 
-    public LangVhdl() {
-        List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
-        List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
+  public LangVhdl() {
+    List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
+    List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
 
-        // Whitespace
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[\\t\\n\\r \\xA0]+"), null, "\t\n\r " + Character.toString((char) 0xA0)}));
-        // String, character or bit string
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^(?:[BOX]?\"(?:[^\\\"]|\"\")*\"|'.')", Pattern.CASE_INSENSITIVE)}));
-        // Comment, from two dashes until end of line.
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^--[^\\r\\n]*")}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_KEYWORD, Pattern.compile("^(?:abs|access|after|alias|all|and|architecture|array|assert|attribute|begin|block|body|buffer|bus|case|component|configuration|constant|disconnect|downto|else|elsif|end|entity|exit|file|for|function|generate|generic|group|guarded|if|impure|in|inertial|inout|is|label|library|linkage|literal|loop|map|mod|nand|new|next|nor|not|null|of|on|open|or|others|out|package|port|postponed|procedure|process|pure|range|record|register|reject|rem|report|return|rol|ror|select|severity|shared|signal|sla|sll|sra|srl|subtype|then|to|transport|type|unaffected|units|until|use|variable|wait|when|while|with|xnor|xor)(?=[^\\w-]|$)", Pattern.CASE_INSENSITIVE), null}));
-        // Type, predefined or standard
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_TYPE, Pattern.compile("^(?:bit|bit_vector|character|boolean|integer|real|time|string|severity_level|positive|natural|signed|unsigned|line|text|std_u?logic(?:_vector)?)(?=[^\\w-]|$)", Pattern.CASE_INSENSITIVE), null}));
-        // Predefined attributes
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_TYPE, Pattern.compile("^\\'(?:ACTIVE|ASCENDING|BASE|DELAYED|DRIVING|DRIVING_VALUE|EVENT|HIGH|IMAGE|INSTANCE_NAME|LAST_ACTIVE|LAST_EVENT|LAST_VALUE|LEFT|LEFTOF|LENGTH|LOW|PATH_NAME|POS|PRED|QUIET|RANGE|REVERSE_RANGE|RIGHT|RIGHTOF|SIMPLE_NAME|STABLE|SUCC|TRANSACTION|VAL|VALUE)(?=[^\\w-]|$)", Pattern.CASE_INSENSITIVE), null}));
-        // Number, decimal or based literal
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^\\d+(?:_\\d+)*(?:#[\\w\\\\.]+#(?:[+\\-]?\\d+(?:_\\d+)*)?|(?:\\.\\d+(?:_\\d+)*)?(?:E[+\\-]?\\d+(?:_\\d+)*)?)", Pattern.CASE_INSENSITIVE)}));
-        // Identifier, basic or extended
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^(?:[a-z]\\w*|\\\\[^\\\\]*\\\\)", Pattern.CASE_INSENSITIVE)}));
-        // Punctuation
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PUNCTUATION, Pattern.compile("^[^\\w\\t\\n\\r \\xA0\\\"\\'][^\\w\\t\\n\\r \\xA0\\-\\\"\\']*")}));
+    // Whitespace
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[\\t\\n\\r \\xA0]+"), null, "\t\n\r " + Character.toString((char) 0xA0)}));
+    // String, character or bit string
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^(?:[BOX]?\"(?:[^\\\"]|\"\")*\"|'.')", Pattern.CASE_INSENSITIVE)}));
+    // Comment, from two dashes until end of line.
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^--[^\\r\\n]*")}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_KEYWORD, Pattern.compile("^(?:abs|access|after|alias|all|and|architecture|array|assert|attribute|begin|block|body|buffer|bus|case|component|configuration|constant|disconnect|downto|else|elsif|end|entity|exit|file|for|function|generate|generic|group|guarded|if|impure|in|inertial|inout|is|label|library|linkage|literal|loop|map|mod|nand|new|next|nor|not|null|of|on|open|or|others|out|package|port|postponed|procedure|process|pure|range|record|register|reject|rem|report|return|rol|ror|select|severity|shared|signal|sla|sll|sra|srl|subtype|then|to|transport|type|unaffected|units|until|use|variable|wait|when|while|with|xnor|xor)(?=[^\\w-]|$)", Pattern.CASE_INSENSITIVE), null}));
+    // Type, predefined or standard
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_TYPE, Pattern.compile("^(?:bit|bit_vector|character|boolean|integer|real|time|string|severity_level|positive|natural|signed|unsigned|line|text|std_u?logic(?:_vector)?)(?=[^\\w-]|$)", Pattern.CASE_INSENSITIVE), null}));
+    // Predefined attributes
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_TYPE, Pattern.compile("^\\'(?:ACTIVE|ASCENDING|BASE|DELAYED|DRIVING|DRIVING_VALUE|EVENT|HIGH|IMAGE|INSTANCE_NAME|LAST_ACTIVE|LAST_EVENT|LAST_VALUE|LEFT|LEFTOF|LENGTH|LOW|PATH_NAME|POS|PRED|QUIET|RANGE|REVERSE_RANGE|RIGHT|RIGHTOF|SIMPLE_NAME|STABLE|SUCC|TRANSACTION|VAL|VALUE)(?=[^\\w-]|$)", Pattern.CASE_INSENSITIVE), null}));
+    // Number, decimal or based literal
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^\\d+(?:_\\d+)*(?:#[\\w\\\\.]+#(?:[+\\-]?\\d+(?:_\\d+)*)?|(?:\\.\\d+(?:_\\d+)*)?(?:E[+\\-]?\\d+(?:_\\d+)*)?)", Pattern.CASE_INSENSITIVE)}));
+    // Identifier, basic or extended
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^(?:[a-z]\\w*|\\\\[^\\\\]*\\\\)", Pattern.CASE_INSENSITIVE)}));
+    // Punctuation
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PUNCTUATION, Pattern.compile("^[^\\w\\t\\n\\r \\xA0\\\"\\'][^\\w\\t\\n\\r \\xA0\\-\\\"\\']*")}));
 
-        setShortcutStylePatterns(_shortcutStylePatterns);
-        setFallthroughStylePatterns(_fallthroughStylePatterns);
-    }
+    setShortcutStylePatterns(_shortcutStylePatterns);
+    setFallthroughStylePatterns(_fallthroughStylePatterns);
+  }
 
-    public static List<String> getFileExtensions() {
-        return Arrays.asList(new String[]{"vhdl", "vhd"});
-    }
+  public static List<String> getFileExtensions() {
+    return Arrays.asList(new String[]{"vhdl", "vhd"});
+  }
 }
diff --git a/src/prettify/lang/LangWiki.java b/src/prettify/lang/LangWiki.java
index f14b5e9..4b98e62 100644
--- a/src/prettify/lang/LangWiki.java
+++ b/src/prettify/lang/LangWiki.java
@@ -17,8 +17,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.regex.Pattern;
-import prettify.Lang;
-import prettify.Prettify;
+import prettify.parser.Prettify;
 
 /**
  * This is similar to the lang-wiki.js in JavaScript Prettify.
@@ -34,50 +33,50 @@
  */
 public class LangWiki extends Lang {
 
-    public LangWiki() {
-        List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
-        List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
+  public LangWiki() {
+    List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
+    List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
 
-        // Whitespace
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[\\t \\xA0a-gi-z0-9]+"), null, "\t " + Character.toString((char) 0xA0) + "abcdefgijklmnopqrstuvwxyz0123456789"}));
-        // Wiki formatting
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PUNCTUATION, Pattern.compile("^[=*~\\^\\[\\]]+"), null, "=*~^[]"}));
-        // Meta-info like #summary, #labels, etc.
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-wiki.meta", Pattern.compile("(?:^^|\r\n?|\n)(#[a-z]+)\\b")}));
-        // A WikiWord
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^(?:[A-Z][a-z][a-z0-9]+[A-Z][a-z][a-zA-Z0-9]+)\\b")}));
-        // A preformatted block in an unknown language
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-", Pattern.compile("^\\{\\{\\{([\\s\\S]+?)\\}\\}\\}")}));
-        // A block of source code in an unknown language
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-", Pattern.compile("^`([^\r\n`]+)`")}));
-        // An inline URL.
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^https?:\\/\\/[^\\/?#\\s]*(?:\\/[^?#\\s]*)?(?:\\?[^#\\s]*)?(?:#\\S*)?", Pattern.CASE_INSENSITIVE)}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^(?:\r\n|[\\s\\S])[^#=*~^A-Zh\\{`\\[\r\n]*")}));
+    // Whitespace
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[\\t \\xA0a-gi-z0-9]+"), null, "\t " + Character.toString((char) 0xA0) + "abcdefgijklmnopqrstuvwxyz0123456789"}));
+    // Wiki formatting
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PUNCTUATION, Pattern.compile("^[=*~\\^\\[\\]]+"), null, "=*~^[]"}));
+    // Meta-info like #summary, #labels, etc.
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-wiki.meta", Pattern.compile("(?:^^|\r\n?|\n)(#[a-z]+)\\b")}));
+    // A WikiWord
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^(?:[A-Z][a-z][a-z0-9]+[A-Z][a-z][a-zA-Z0-9]+)\\b")}));
+    // A preformatted block in an unknown language
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-", Pattern.compile("^\\{\\{\\{([\\s\\S]+?)\\}\\}\\}")}));
+    // A block of source code in an unknown language
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-", Pattern.compile("^`([^\r\n`]+)`")}));
+    // An inline URL.
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^https?:\\/\\/[^\\/?#\\s]*(?:\\/[^?#\\s]*)?(?:\\?[^#\\s]*)?(?:#\\S*)?", Pattern.CASE_INSENSITIVE)}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^(?:\r\n|[\\s\\S])[^#=*~^A-Zh\\{`\\[\r\n]*")}));
 
-        setShortcutStylePatterns(_shortcutStylePatterns);
-        setFallthroughStylePatterns(_fallthroughStylePatterns);
+    setShortcutStylePatterns(_shortcutStylePatterns);
+    setFallthroughStylePatterns(_fallthroughStylePatterns);
 
-        setExtendedLangs(Arrays.asList(new Lang[]{new LangWikiMeta()}));
+    setExtendedLangs(Arrays.asList(new Lang[]{new LangWikiMeta()}));
+  }
+
+  public static List<String> getFileExtensions() {
+    return Arrays.asList(new String[]{"wiki"});
+  }
+
+  protected static class LangWikiMeta extends Lang {
+
+    public LangWikiMeta() {
+      List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
+      List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
+
+      _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_KEYWORD, Pattern.compile("^#[a-z]+", Pattern.CASE_INSENSITIVE), null, "#"}));
+
+      setShortcutStylePatterns(_shortcutStylePatterns);
+      setFallthroughStylePatterns(_fallthroughStylePatterns);
     }
 
     public static List<String> getFileExtensions() {
-        return Arrays.asList(new String[]{"wiki"});
+      return Arrays.asList(new String[]{"wiki.meta"});
     }
-
-    protected static class LangWikiMeta extends Lang {
-
-        public LangWikiMeta() {
-            List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
-            List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
-
-            _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_KEYWORD, Pattern.compile("^#[a-z]+", Pattern.CASE_INSENSITIVE), null, "#"}));
-
-            setShortcutStylePatterns(_shortcutStylePatterns);
-            setFallthroughStylePatterns(_fallthroughStylePatterns);
-        }
-
-        public static List<String> getFileExtensions() {
-            return Arrays.asList(new String[]{"wiki.meta"});
-        }
-    }
+  }
 }
diff --git a/src/prettify/lang/LangXq.java b/src/prettify/lang/LangXq.java
index c6581cd..087be7c 100644
--- a/src/prettify/lang/LangXq.java
+++ b/src/prettify/lang/LangXq.java
@@ -17,8 +17,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.regex.Pattern;
-import prettify.Lang;
-import prettify.Prettify;
+import prettify.parser.Prettify;
 
 /**
  * This is similar to the lang-xq.js in JavaScript Prettify.
@@ -38,45 +37,45 @@
  */
 public class LangXq extends Lang {
 
-    public static final String PR_FUNCTION = "fun";
-    public static final String PR_VARIABLE = "var";
+  public static final String PR_FUNCTION = "fun";
+  public static final String PR_VARIABLE = "var";
 
-    public LangXq() {
-        List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
-        List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
+  public LangXq() {
+    List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
+    List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
 
-        // Matching $var-ia_bles
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{PR_VARIABLE, Pattern.compile("^\\$[A-Za-z0-9_\\-]+"), null, "$"}));
-        // Matching lt and gt operators
-        // Not the best matching solution but you have to differentiate between the gt operator and the tag closing char
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[\\s=][<>][\\s=]")}));
-        // Matching @Attributes
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^\\@[\\w-]+")}));
-        // Matching xml tags
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_TAG, Pattern.compile("^<\\/?[a-z](?:[\\w.:-]*\\w)?|\\/?>$", Pattern.CASE_INSENSITIVE)}));
-        // Matching single or multiline xquery comments -> (: <text> :)
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^\\(:[\\s\\S]*?:\\)")}));
-        // Tokenizing /{}:=;*,[]() as plain
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[\\/\\{\\};,\\[\\]\\(\\)]$")}));
-        // Matching a double or single quoted, possibly multi-line, string.
-        // with the special condition that a { in a string changes to xquery context 
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^(?:\\\"(?:[^\\\"\\\\\\{]|\\\\[\\s\\S])*(?:\\\"|$)|\\'(?:[^\\'\\\\\\{]|\\\\[\\s\\S])*(?:\\'|$))", Pattern.CASE_INSENSITIVE), null, "\"'"}));
-        // Matching standard xquery keywords
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_KEYWORD, Pattern.compile("^(?:xquery|where|version|variable|union|typeswitch|treat|to|then|text|stable|sortby|some|self|schema|satisfies|returns|return|ref|processing-instruction|preceding-sibling|preceding|precedes|parent|only|of|node|namespace|module|let|item|intersect|instance|in|import|if|function|for|follows|following-sibling|following|external|except|every|else|element|descending|descendant-or-self|descendant|define|default|declare|comment|child|cast|case|before|attribute|assert|ascending|as|ancestor-or-self|ancestor|after|eq|order|by|or|and|schema-element|document-node|node|at)\\b")}));
-        // Matching standard xquery types
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_TYPE, Pattern.compile("^(?:xs:yearMonthDuration|xs:unsignedLong|xs:time|xs:string|xs:short|xs:QName|xs:Name|xs:long|xs:integer|xs:int|xs:gYearMonth|xs:gYear|xs:gMonthDay|xs:gDay|xs:float|xs:duration|xs:double|xs:decimal|xs:dayTimeDuration|xs:dateTime|xs:date|xs:byte|xs:boolean|xs:anyURI|xf:yearMonthDuration)\\b"), null}));
-        // Matching standard xquery functions
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_FUNCTION, Pattern.compile("^(?:xp:dereference|xinc:node-expand|xinc:link-references|xinc:link-expand|xhtml:restructure|xhtml:clean|xhtml:add-lists|xdmp:zip-manifest|xdmp:zip-get|xdmp:zip-create|xdmp:xquery-version|xdmp:word-convert|xdmp:with-namespaces|xdmp:version|xdmp:value|xdmp:user-roles|xdmp:user-last-login|xdmp:user|xdmp:url-encode|xdmp:url-decode|xdmp:uri-is-file|xdmp:uri-format|xdmp:uri-content-type|xdmp:unquote|xdmp:unpath|xdmp:triggers-database|xdmp:trace|xdmp:to-json|xdmp:tidy|xdmp:subbinary|xdmp:strftime|xdmp:spawn-in|xdmp:spawn|xdmp:sleep|xdmp:shutdown|xdmp:set-session-field|xdmp:set-response-encoding|xdmp:set-response-content-type|xdmp:set-response-code|xdmp:set-request-time-limit|xdmp:set|xdmp:servers|xdmp:server-status|xdmp:server-name|xdmp:server|xdmp:security-database|xdmp:security-assert|xdmp:schema-database|xdmp:save|xdmp:role-roles|xdmp:role|xdmp:rethrow|xdmp:restart|xdmp:request-timestamp|xdmp:request-status|xdmp:request-cancel|xdmp:request|xdmp:redirect-response|xdmp:random|xdmp:quote|xdmp:query-trace|xdmp:query-meters|xdmp:product-edition|xdmp:privilege-roles|xdmp:privilege|xdmp:pretty-print|xdmp:powerpoint-convert|xdmp:platform|xdmp:permission|xdmp:pdf-convert|xdmp:path|xdmp:octal-to-integer|xdmp:node-uri|xdmp:node-replace|xdmp:node-kind|xdmp:node-insert-child|xdmp:node-insert-before|xdmp:node-insert-after|xdmp:node-delete|xdmp:node-database|xdmp:mul64|xdmp:modules-root|xdmp:modules-database|xdmp:merging|xdmp:merge-cancel|xdmp:merge|xdmp:md5|xdmp:logout|xdmp:login|xdmp:log-level|xdmp:log|xdmp:lock-release|xdmp:lock-acquire|xdmp:load|xdmp:invoke-in|xdmp:invoke|xdmp:integer-to-octal|xdmp:integer-to-hex|xdmp:http-put|xdmp:http-post|xdmp:http-options|xdmp:http-head|xdmp:http-get|xdmp:http-delete|xdmp:hosts|xdmp:host-status|xdmp:host-name|xdmp:host|xdmp:hex-to-integer|xdmp:hash64|xdmp:hash32|xdmp:has-privilege|xdmp:groups|xdmp:group-serves|xdmp:group-servers|xdmp:group-name|xdmp:group-hosts|xdmp:group|xdmp:get-session-field-names|xdmp:get-session-field|xdmp:get-response-encoding|xdmp:get-response-code|xdmp:get-request-username|xdmp:get-request-user|xdmp:get-request-url|xdmp:get-request-protocol|xdmp:get-request-path|xdmp:get-request-method|xdmp:get-request-header-names|xdmp:get-request-header|xdmp:get-request-field-names|xdmp:get-request-field-filename|xdmp:get-request-field-content-type|xdmp:get-request-field|xdmp:get-request-client-certificate|xdmp:get-request-client-address|xdmp:get-request-body|xdmp:get-current-user|xdmp:get-current-roles|xdmp:get|xdmp:function-name|xdmp:function-module|xdmp:function|xdmp:from-json|xdmp:forests|xdmp:forest-status|xdmp:forest-restore|xdmp:forest-restart|xdmp:forest-name|xdmp:forest-delete|xdmp:forest-databases|xdmp:forest-counts|xdmp:forest-clear|xdmp:forest-backup|xdmp:forest|xdmp:filesystem-file|xdmp:filesystem-directory|xdmp:exists|xdmp:excel-convert|xdmp:eval-in|xdmp:eval|xdmp:estimate|xdmp:email|xdmp:element-content-type|xdmp:elapsed-time|xdmp:document-set-quality|xdmp:document-set-property|xdmp:document-set-properties|xdmp:document-set-permissions|xdmp:document-set-collections|xdmp:document-remove-properties|xdmp:document-remove-permissions|xdmp:document-remove-collections|xdmp:document-properties|xdmp:document-locks|xdmp:document-load|xdmp:document-insert|xdmp:document-get-quality|xdmp:document-get-properties|xdmp:document-get-permissions|xdmp:document-get-collections|xdmp:document-get|xdmp:document-forest|xdmp:document-delete|xdmp:document-add-properties|xdmp:document-add-permissions|xdmp:document-add-collections|xdmp:directory-properties|xdmp:directory-locks|xdmp:directory-delete|xdmp:directory-create|xdmp:directory|xdmp:diacritic-less|xdmp:describe|xdmp:default-permissions|xdmp:default-collections|xdmp:databases|xdmp:database-restore-validate|xdmp:database-restore-status|xdmp:database-restore-cancel|xdmp:database-restore|xdmp:database-name|xdmp:database-forests|xdmp:database-backup-validate|xdmp:database-backup-status|xdmp:database-backup-purge|xdmp:database-backup-cancel|xdmp:database-backup|xdmp:database|xdmp:collection-properties|xdmp:collection-locks|xdmp:collection-delete|xdmp:collation-canonical-uri|xdmp:castable-as|xdmp:can-grant-roles|xdmp:base64-encode|xdmp:base64-decode|xdmp:architecture|xdmp:apply|xdmp:amp-roles|xdmp:amp|xdmp:add64|xdmp:add-response-header|xdmp:access|trgr:trigger-set-recursive|trgr:trigger-set-permissions|trgr:trigger-set-name|trgr:trigger-set-module|trgr:trigger-set-event|trgr:trigger-set-description|trgr:trigger-remove-permissions|trgr:trigger-module|trgr:trigger-get-permissions|trgr:trigger-enable|trgr:trigger-disable|trgr:trigger-database-online-event|trgr:trigger-data-event|trgr:trigger-add-permissions|trgr:remove-trigger|trgr:property-content|trgr:pre-commit|trgr:post-commit|trgr:get-trigger-by-id|trgr:get-trigger|trgr:document-scope|trgr:document-content|trgr:directory-scope|trgr:create-trigger|trgr:collection-scope|trgr:any-property-content|thsr:set-entry|thsr:remove-term|thsr:remove-synonym|thsr:remove-entry|thsr:query-lookup|thsr:lookup|thsr:load|thsr:insert|thsr:expand|thsr:add-synonym|spell:suggest-detailed|spell:suggest|spell:remove-word|spell:make-dictionary|spell:load|spell:levenshtein-distance|spell:is-correct|spell:insert|spell:double-metaphone|spell:add-word|sec:users-collection|sec:user-set-roles|sec:user-set-password|sec:user-set-name|sec:user-set-description|sec:user-set-default-permissions|sec:user-set-default-collections|sec:user-remove-roles|sec:user-privileges|sec:user-get-roles|sec:user-get-description|sec:user-get-default-permissions|sec:user-get-default-collections|sec:user-doc-permissions|sec:user-doc-collections|sec:user-add-roles|sec:unprotect-collection|sec:uid-for-name|sec:set-realm|sec:security-version|sec:security-namespace|sec:security-installed|sec:security-collection|sec:roles-collection|sec:role-set-roles|sec:role-set-name|sec:role-set-description|sec:role-set-default-permissions|sec:role-set-default-collections|sec:role-remove-roles|sec:role-privileges|sec:role-get-roles|sec:role-get-description|sec:role-get-default-permissions|sec:role-get-default-collections|sec:role-doc-permissions|sec:role-doc-collections|sec:role-add-roles|sec:remove-user|sec:remove-role-from-users|sec:remove-role-from-role|sec:remove-role-from-privileges|sec:remove-role-from-amps|sec:remove-role|sec:remove-privilege|sec:remove-amp|sec:protect-collection|sec:privileges-collection|sec:privilege-set-roles|sec:privilege-set-name|sec:privilege-remove-roles|sec:privilege-get-roles|sec:privilege-add-roles|sec:priv-doc-permissions|sec:priv-doc-collections|sec:get-user-names|sec:get-unique-elem-id|sec:get-role-names|sec:get-role-ids|sec:get-privilege|sec:get-distinct-permissions|sec:get-collection|sec:get-amp|sec:create-user-with-role|sec:create-user|sec:create-role|sec:create-privilege|sec:create-amp|sec:collections-collection|sec:collection-set-permissions|sec:collection-remove-permissions|sec:collection-get-permissions|sec:collection-add-permissions|sec:check-admin|sec:amps-collection|sec:amp-set-roles|sec:amp-remove-roles|sec:amp-get-roles|sec:amp-doc-permissions|sec:amp-doc-collections|sec:amp-add-roles|search:unparse|search:suggest|search:snippet|search:search|search:resolve-nodes|search:resolve|search:remove-constraint|search:parse|search:get-default-options|search:estimate|search:check-options|prof:value|prof:reset|prof:report|prof:invoke|prof:eval|prof:enable|prof:disable|prof:allowed|ppt:clean|pki:template-set-request|pki:template-set-name|pki:template-set-key-type|pki:template-set-key-options|pki:template-set-description|pki:template-in-use|pki:template-get-version|pki:template-get-request|pki:template-get-name|pki:template-get-key-type|pki:template-get-key-options|pki:template-get-id|pki:template-get-description|pki:need-certificate|pki:is-temporary|pki:insert-trusted-certificates|pki:insert-template|pki:insert-signed-certificates|pki:insert-certificate-revocation-list|pki:get-trusted-certificate-ids|pki:get-template-ids|pki:get-template-certificate-authority|pki:get-template-by-name|pki:get-template|pki:get-pending-certificate-requests-xml|pki:get-pending-certificate-requests-pem|pki:get-pending-certificate-request|pki:get-certificates-for-template-xml|pki:get-certificates-for-template|pki:get-certificates|pki:get-certificate-xml|pki:get-certificate-pem|pki:get-certificate|pki:generate-temporary-certificate-if-necessary|pki:generate-temporary-certificate|pki:generate-template-certificate-authority|pki:generate-certificate-request|pki:delete-template|pki:delete-certificate|pki:create-template|pdf:make-toc|pdf:insert-toc-headers|pdf:get-toc|pdf:clean|p:status-transition|p:state-transition|p:remove|p:pipelines|p:insert|p:get-by-id|p:get|p:execute|p:create|p:condition|p:collection|p:action|ooxml:runs-merge|ooxml:package-uris|ooxml:package-parts-insert|ooxml:package-parts|msword:clean|mcgm:polygon|mcgm:point|mcgm:geospatial-query-from-elements|mcgm:geospatial-query|mcgm:circle|math:tanh|math:tan|math:sqrt|math:sinh|math:sin|math:pow|math:modf|math:log10|math:log|math:ldexp|math:frexp|math:fmod|math:floor|math:fabs|math:exp|math:cosh|math:cos|math:ceil|math:atan2|math:atan|math:asin|math:acos|map:put|map:map|map:keys|map:get|map:delete|map:count|map:clear|lnk:to|lnk:remove|lnk:insert|lnk:get|lnk:from|lnk:create|kml:polygon|kml:point|kml:interior-polygon|kml:geospatial-query-from-elements|kml:geospatial-query|kml:circle|kml:box|gml:polygon|gml:point|gml:interior-polygon|gml:geospatial-query-from-elements|gml:geospatial-query|gml:circle|gml:box|georss:point|georss:geospatial-query|georss:circle|geo:polygon|geo:point|geo:interior-polygon|geo:geospatial-query-from-elements|geo:geospatial-query|geo:circle|geo:box|fn:zero-or-one|fn:years-from-duration|fn:year-from-dateTime|fn:year-from-date|fn:upper-case|fn:unordered|fn:true|fn:translate|fn:trace|fn:tokenize|fn:timezone-from-time|fn:timezone-from-dateTime|fn:timezone-from-date|fn:sum|fn:subtract-dateTimes-yielding-yearMonthDuration|fn:subtract-dateTimes-yielding-dayTimeDuration|fn:substring-before|fn:substring-after|fn:substring|fn:subsequence|fn:string-to-codepoints|fn:string-pad|fn:string-length|fn:string-join|fn:string|fn:static-base-uri|fn:starts-with|fn:seconds-from-time|fn:seconds-from-duration|fn:seconds-from-dateTime|fn:round-half-to-even|fn:round|fn:root|fn:reverse|fn:resolve-uri|fn:resolve-QName|fn:replace|fn:remove|fn:QName|fn:prefix-from-QName|fn:position|fn:one-or-more|fn:number|fn:not|fn:normalize-unicode|fn:normalize-space|fn:node-name|fn:node-kind|fn:nilled|fn:namespace-uri-from-QName|fn:namespace-uri-for-prefix|fn:namespace-uri|fn:name|fn:months-from-duration|fn:month-from-dateTime|fn:month-from-date|fn:minutes-from-time|fn:minutes-from-duration|fn:minutes-from-dateTime|fn:min|fn:max|fn:matches|fn:lower-case|fn:local-name-from-QName|fn:local-name|fn:last|fn:lang|fn:iri-to-uri|fn:insert-before|fn:index-of|fn:in-scope-prefixes|fn:implicit-timezone|fn:idref|fn:id|fn:hours-from-time|fn:hours-from-duration|fn:hours-from-dateTime|fn:floor|fn:false|fn:expanded-QName|fn:exists|fn:exactly-one|fn:escape-uri|fn:escape-html-uri|fn:error|fn:ends-with|fn:encode-for-uri|fn:empty|fn:document-uri|fn:doc-available|fn:doc|fn:distinct-values|fn:distinct-nodes|fn:default-collation|fn:deep-equal|fn:days-from-duration|fn:day-from-dateTime|fn:day-from-date|fn:data|fn:current-time|fn:current-dateTime|fn:current-date|fn:count|fn:contains|fn:concat|fn:compare|fn:collection|fn:codepoints-to-string|fn:codepoint-equal|fn:ceiling|fn:boolean|fn:base-uri|fn:avg|fn:adjust-time-to-timezone|fn:adjust-dateTime-to-timezone|fn:adjust-date-to-timezone|fn:abs|feed:unsubscribe|feed:subscription|feed:subscribe|feed:request|feed:item|feed:description|excel:clean|entity:enrich|dom:set-pipelines|dom:set-permissions|dom:set-name|dom:set-evaluation-context|dom:set-domain-scope|dom:set-description|dom:remove-pipeline|dom:remove-permissions|dom:remove|dom:get|dom:evaluation-context|dom:domains|dom:domain-scope|dom:create|dom:configuration-set-restart-user|dom:configuration-set-permissions|dom:configuration-set-evaluation-context|dom:configuration-set-default-domain|dom:configuration-get|dom:configuration-create|dom:collection|dom:add-pipeline|dom:add-permissions|dls:retention-rules|dls:retention-rule-remove|dls:retention-rule-insert|dls:retention-rule|dls:purge|dls:node-expand|dls:link-references|dls:link-expand|dls:documents-query|dls:document-versions-query|dls:document-version-uri|dls:document-version-query|dls:document-version-delete|dls:document-version-as-of|dls:document-version|dls:document-update|dls:document-unmanage|dls:document-set-quality|dls:document-set-property|dls:document-set-properties|dls:document-set-permissions|dls:document-set-collections|dls:document-retention-rules|dls:document-remove-properties|dls:document-remove-permissions|dls:document-remove-collections|dls:document-purge|dls:document-manage|dls:document-is-managed|dls:document-insert-and-manage|dls:document-include-query|dls:document-history|dls:document-get-permissions|dls:document-extract-part|dls:document-delete|dls:document-checkout-status|dls:document-checkout|dls:document-checkin|dls:document-add-properties|dls:document-add-permissions|dls:document-add-collections|dls:break-checkout|dls:author-query|dls:as-of-query|dbk:convert|dbg:wait|dbg:value|dbg:stopped|dbg:stop|dbg:step|dbg:status|dbg:stack|dbg:out|dbg:next|dbg:line|dbg:invoke|dbg:function|dbg:finish|dbg:expr|dbg:eval|dbg:disconnect|dbg:detach|dbg:continue|dbg:connect|dbg:clear|dbg:breakpoints|dbg:break|dbg:attached|dbg:attach|cvt:save-converted-documents|cvt:part-uri|cvt:destination-uri|cvt:basepath|cvt:basename|cts:words|cts:word-query-weight|cts:word-query-text|cts:word-query-options|cts:word-query|cts:word-match|cts:walk|cts:uris|cts:uri-match|cts:train|cts:tokenize|cts:thresholds|cts:stem|cts:similar-query-weight|cts:similar-query-nodes|cts:similar-query|cts:shortest-distance|cts:search|cts:score|cts:reverse-query-weight|cts:reverse-query-nodes|cts:reverse-query|cts:remainder|cts:registered-query-weight|cts:registered-query-options|cts:registered-query-ids|cts:registered-query|cts:register|cts:query|cts:quality|cts:properties-query-query|cts:properties-query|cts:polygon-vertices|cts:polygon|cts:point-longitude|cts:point-latitude|cts:point|cts:or-query-queries|cts:or-query|cts:not-query-weight|cts:not-query-query|cts:not-query|cts:near-query-weight|cts:near-query-queries|cts:near-query-options|cts:near-query-distance|cts:near-query|cts:highlight|cts:geospatial-co-occurrences|cts:frequency|cts:fitness|cts:field-words|cts:field-word-query-weight|cts:field-word-query-text|cts:field-word-query-options|cts:field-word-query-field-name|cts:field-word-query|cts:field-word-match|cts:entity-highlight|cts:element-words|cts:element-word-query-weight|cts:element-word-query-text|cts:element-word-query-options|cts:element-word-query-element-name|cts:element-word-query|cts:element-word-match|cts:element-values|cts:element-value-ranges|cts:element-value-query-weight|cts:element-value-query-text|cts:element-value-query-options|cts:element-value-query-element-name|cts:element-value-query|cts:element-value-match|cts:element-value-geospatial-co-occurrences|cts:element-value-co-occurrences|cts:element-range-query-weight|cts:element-range-query-value|cts:element-range-query-options|cts:element-range-query-operator|cts:element-range-query-element-name|cts:element-range-query|cts:element-query-query|cts:element-query-element-name|cts:element-query|cts:element-pair-geospatial-values|cts:element-pair-geospatial-value-match|cts:element-pair-geospatial-query-weight|cts:element-pair-geospatial-query-region|cts:element-pair-geospatial-query-options|cts:element-pair-geospatial-query-longitude-name|cts:element-pair-geospatial-query-latitude-name|cts:element-pair-geospatial-query-element-name|cts:element-pair-geospatial-query|cts:element-pair-geospatial-boxes|cts:element-geospatial-values|cts:element-geospatial-value-match|cts:element-geospatial-query-weight|cts:element-geospatial-query-region|cts:element-geospatial-query-options|cts:element-geospatial-query-element-name|cts:element-geospatial-query|cts:element-geospatial-boxes|cts:element-child-geospatial-values|cts:element-child-geospatial-value-match|cts:element-child-geospatial-query-weight|cts:element-child-geospatial-query-region|cts:element-child-geospatial-query-options|cts:element-child-geospatial-query-element-name|cts:element-child-geospatial-query-child-name|cts:element-child-geospatial-query|cts:element-child-geospatial-boxes|cts:element-attribute-words|cts:element-attribute-word-query-weight|cts:element-attribute-word-query-text|cts:element-attribute-word-query-options|cts:element-attribute-word-query-element-name|cts:element-attribute-word-query-attribute-name|cts:element-attribute-word-query|cts:element-attribute-word-match|cts:element-attribute-values|cts:element-attribute-value-ranges|cts:element-attribute-value-query-weight|cts:element-attribute-value-query-text|cts:element-attribute-value-query-options|cts:element-attribute-value-query-element-name|cts:element-attribute-value-query-attribute-name|cts:element-attribute-value-query|cts:element-attribute-value-match|cts:element-attribute-value-geospatial-co-occurrences|cts:element-attribute-value-co-occurrences|cts:element-attribute-range-query-weight|cts:element-attribute-range-query-value|cts:element-attribute-range-query-options|cts:element-attribute-range-query-operator|cts:element-attribute-range-query-element-name|cts:element-attribute-range-query-attribute-name|cts:element-attribute-range-query|cts:element-attribute-pair-geospatial-values|cts:element-attribute-pair-geospatial-value-match|cts:element-attribute-pair-geospatial-query-weight|cts:element-attribute-pair-geospatial-query-region|cts:element-attribute-pair-geospatial-query-options|cts:element-attribute-pair-geospatial-query-longitude-name|cts:element-attribute-pair-geospatial-query-latitude-name|cts:element-attribute-pair-geospatial-query-element-name|cts:element-attribute-pair-geospatial-query|cts:element-attribute-pair-geospatial-boxes|cts:document-query-uris|cts:document-query|cts:distance|cts:directory-query-uris|cts:directory-query-depth|cts:directory-query|cts:destination|cts:deregister|cts:contains|cts:confidence|cts:collections|cts:collection-query-uris|cts:collection-query|cts:collection-match|cts:classify|cts:circle-radius|cts:circle-center|cts:circle|cts:box-west|cts:box-south|cts:box-north|cts:box-east|cts:box|cts:bearing|cts:arc-intersection|cts:and-query-queries|cts:and-query-options|cts:and-query|cts:and-not-query-positive-query|cts:and-not-query-negative-query|cts:and-not-query|css:get|css:convert|cpf:success|cpf:failure|cpf:document-set-state|cpf:document-set-processing-status|cpf:document-set-last-updated|cpf:document-set-error|cpf:document-get-state|cpf:document-get-processing-status|cpf:document-get-last-updated|cpf:document-get-error|cpf:check-transition|alert:spawn-matching-actions|alert:rule-user-id-query|alert:rule-set-user-id|alert:rule-set-query|alert:rule-set-options|alert:rule-set-name|alert:rule-set-description|alert:rule-set-action|alert:rule-remove|alert:rule-name-query|alert:rule-insert|alert:rule-id-query|alert:rule-get-user-id|alert:rule-get-query|alert:rule-get-options|alert:rule-get-name|alert:rule-get-id|alert:rule-get-description|alert:rule-get-action|alert:rule-action-query|alert:remove-triggers|alert:make-rule|alert:make-log-action|alert:make-config|alert:make-action|alert:invoke-matching-actions|alert:get-my-rules|alert:get-all-rules|alert:get-actions|alert:find-matching-rules|alert:create-triggers|alert:config-set-uri|alert:config-set-trigger-ids|alert:config-set-options|alert:config-set-name|alert:config-set-description|alert:config-set-cpf-domain-names|alert:config-set-cpf-domain-ids|alert:config-insert|alert:config-get-uri|alert:config-get-trigger-ids|alert:config-get-options|alert:config-get-name|alert:config-get-id|alert:config-get-description|alert:config-get-cpf-domain-names|alert:config-get-cpf-domain-ids|alert:config-get|alert:config-delete|alert:action-set-options|alert:action-set-name|alert:action-set-module-root|alert:action-set-module-db|alert:action-set-module|alert:action-set-description|alert:action-remove|alert:action-insert|alert:action-get-options|alert:action-get-name|alert:action-get-module-root|alert:action-get-module-db|alert:action-get-module|alert:action-get-description|zero-or-one|years-from-duration|year-from-dateTime|year-from-date|upper-case|unordered|true|translate|trace|tokenize|timezone-from-time|timezone-from-dateTime|timezone-from-date|sum|subtract-dateTimes-yielding-yearMonthDuration|subtract-dateTimes-yielding-dayTimeDuration|substring-before|substring-after|substring|subsequence|string-to-codepoints|string-pad|string-length|string-join|string|static-base-uri|starts-with|seconds-from-time|seconds-from-duration|seconds-from-dateTime|round-half-to-even|round|root|reverse|resolve-uri|resolve-QName|replace|remove|QName|prefix-from-QName|position|one-or-more|number|not|normalize-unicode|normalize-space|node-name|node-kind|nilled|namespace-uri-from-QName|namespace-uri-for-prefix|namespace-uri|name|months-from-duration|month-from-dateTime|month-from-date|minutes-from-time|minutes-from-duration|minutes-from-dateTime|min|max|matches|lower-case|local-name-from-QName|local-name|last|lang|iri-to-uri|insert-before|index-of|in-scope-prefixes|implicit-timezone|idref|id|hours-from-time|hours-from-duration|hours-from-dateTime|floor|false|expanded-QName|exists|exactly-one|escape-uri|escape-html-uri|error|ends-with|encode-for-uri|empty|document-uri|doc-available|doc|distinct-values|distinct-nodes|default-collation|deep-equal|days-from-duration|day-from-dateTime|day-from-date|data|current-time|current-dateTime|current-date|count|contains|concat|compare|collection|codepoints-to-string|codepoint-equal|ceiling|boolean|base-uri|avg|adjust-time-to-timezone|adjust-dateTime-to-timezone|adjust-date-to-timezone|abs)\\b")}));
-        // Matching normal words if none of the previous regular expressions matched
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[A-Za-z0-9_\\-\\:]+")}));
-        // Matching whitespaces
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[\\t\\n\\r \\xA0]+")}));
+    // Matching $var-ia_bles
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{PR_VARIABLE, Pattern.compile("^\\$[A-Za-z0-9_\\-]+"), null, "$"}));
+    // Matching lt and gt operators
+    // Not the best matching solution but you have to differentiate between the gt operator and the tag closing char
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[\\s=][<>][\\s=]")}));
+    // Matching @Attributes
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_LITERAL, Pattern.compile("^\\@[\\w-]+")}));
+    // Matching xml tags
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_TAG, Pattern.compile("^<\\/?[a-z](?:[\\w.:-]*\\w)?|\\/?>$", Pattern.CASE_INSENSITIVE)}));
+    // Matching single or multiline xquery comments -> (: <text> :)
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^\\(:[\\s\\S]*?:\\)")}));
+    // Tokenizing /{}:=;*,[]() as plain
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[\\/\\{\\};,\\[\\]\\(\\)]$")}));
+    // Matching a double or single quoted, possibly multi-line, string.
+    // with the special condition that a { in a string changes to xquery context 
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^(?:\\\"(?:[^\\\"\\\\\\{]|\\\\[\\s\\S])*(?:\\\"|$)|\\'(?:[^\\'\\\\\\{]|\\\\[\\s\\S])*(?:\\'|$))", Pattern.CASE_INSENSITIVE), null, "\"'"}));
+    // Matching standard xquery keywords
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_KEYWORD, Pattern.compile("^(?:xquery|where|version|variable|union|typeswitch|treat|to|then|text|stable|sortby|some|self|schema|satisfies|returns|return|ref|processing-instruction|preceding-sibling|preceding|precedes|parent|only|of|node|namespace|module|let|item|intersect|instance|in|import|if|function|for|follows|following-sibling|following|external|except|every|else|element|descending|descendant-or-self|descendant|define|default|declare|comment|child|cast|case|before|attribute|assert|ascending|as|ancestor-or-self|ancestor|after|eq|order|by|or|and|schema-element|document-node|node|at)\\b")}));
+    // Matching standard xquery types
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_TYPE, Pattern.compile("^(?:xs:yearMonthDuration|xs:unsignedLong|xs:time|xs:string|xs:short|xs:QName|xs:Name|xs:long|xs:integer|xs:int|xs:gYearMonth|xs:gYear|xs:gMonthDay|xs:gDay|xs:float|xs:duration|xs:double|xs:decimal|xs:dayTimeDuration|xs:dateTime|xs:date|xs:byte|xs:boolean|xs:anyURI|xf:yearMonthDuration)\\b"), null}));
+    // Matching standard xquery functions
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_FUNCTION, Pattern.compile("^(?:xp:dereference|xinc:node-expand|xinc:link-references|xinc:link-expand|xhtml:restructure|xhtml:clean|xhtml:add-lists|xdmp:zip-manifest|xdmp:zip-get|xdmp:zip-create|xdmp:xquery-version|xdmp:word-convert|xdmp:with-namespaces|xdmp:version|xdmp:value|xdmp:user-roles|xdmp:user-last-login|xdmp:user|xdmp:url-encode|xdmp:url-decode|xdmp:uri-is-file|xdmp:uri-format|xdmp:uri-content-type|xdmp:unquote|xdmp:unpath|xdmp:triggers-database|xdmp:trace|xdmp:to-json|xdmp:tidy|xdmp:subbinary|xdmp:strftime|xdmp:spawn-in|xdmp:spawn|xdmp:sleep|xdmp:shutdown|xdmp:set-session-field|xdmp:set-response-encoding|xdmp:set-response-content-type|xdmp:set-response-code|xdmp:set-request-time-limit|xdmp:set|xdmp:servers|xdmp:server-status|xdmp:server-name|xdmp:server|xdmp:security-database|xdmp:security-assert|xdmp:schema-database|xdmp:save|xdmp:role-roles|xdmp:role|xdmp:rethrow|xdmp:restart|xdmp:request-timestamp|xdmp:request-status|xdmp:request-cancel|xdmp:request|xdmp:redirect-response|xdmp:random|xdmp:quote|xdmp:query-trace|xdmp:query-meters|xdmp:product-edition|xdmp:privilege-roles|xdmp:privilege|xdmp:pretty-print|xdmp:powerpoint-convert|xdmp:platform|xdmp:permission|xdmp:pdf-convert|xdmp:path|xdmp:octal-to-integer|xdmp:node-uri|xdmp:node-replace|xdmp:node-kind|xdmp:node-insert-child|xdmp:node-insert-before|xdmp:node-insert-after|xdmp:node-delete|xdmp:node-database|xdmp:mul64|xdmp:modules-root|xdmp:modules-database|xdmp:merging|xdmp:merge-cancel|xdmp:merge|xdmp:md5|xdmp:logout|xdmp:login|xdmp:log-level|xdmp:log|xdmp:lock-release|xdmp:lock-acquire|xdmp:load|xdmp:invoke-in|xdmp:invoke|xdmp:integer-to-octal|xdmp:integer-to-hex|xdmp:http-put|xdmp:http-post|xdmp:http-options|xdmp:http-head|xdmp:http-get|xdmp:http-delete|xdmp:hosts|xdmp:host-status|xdmp:host-name|xdmp:host|xdmp:hex-to-integer|xdmp:hash64|xdmp:hash32|xdmp:has-privilege|xdmp:groups|xdmp:group-serves|xdmp:group-servers|xdmp:group-name|xdmp:group-hosts|xdmp:group|xdmp:get-session-field-names|xdmp:get-session-field|xdmp:get-response-encoding|xdmp:get-response-code|xdmp:get-request-username|xdmp:get-request-user|xdmp:get-request-url|xdmp:get-request-protocol|xdmp:get-request-path|xdmp:get-request-method|xdmp:get-request-header-names|xdmp:get-request-header|xdmp:get-request-field-names|xdmp:get-request-field-filename|xdmp:get-request-field-content-type|xdmp:get-request-field|xdmp:get-request-client-certificate|xdmp:get-request-client-address|xdmp:get-request-body|xdmp:get-current-user|xdmp:get-current-roles|xdmp:get|xdmp:function-name|xdmp:function-module|xdmp:function|xdmp:from-json|xdmp:forests|xdmp:forest-status|xdmp:forest-restore|xdmp:forest-restart|xdmp:forest-name|xdmp:forest-delete|xdmp:forest-databases|xdmp:forest-counts|xdmp:forest-clear|xdmp:forest-backup|xdmp:forest|xdmp:filesystem-file|xdmp:filesystem-directory|xdmp:exists|xdmp:excel-convert|xdmp:eval-in|xdmp:eval|xdmp:estimate|xdmp:email|xdmp:element-content-type|xdmp:elapsed-time|xdmp:document-set-quality|xdmp:document-set-property|xdmp:document-set-properties|xdmp:document-set-permissions|xdmp:document-set-collections|xdmp:document-remove-properties|xdmp:document-remove-permissions|xdmp:document-remove-collections|xdmp:document-properties|xdmp:document-locks|xdmp:document-load|xdmp:document-insert|xdmp:document-get-quality|xdmp:document-get-properties|xdmp:document-get-permissions|xdmp:document-get-collections|xdmp:document-get|xdmp:document-forest|xdmp:document-delete|xdmp:document-add-properties|xdmp:document-add-permissions|xdmp:document-add-collections|xdmp:directory-properties|xdmp:directory-locks|xdmp:directory-delete|xdmp:directory-create|xdmp:directory|xdmp:diacritic-less|xdmp:describe|xdmp:default-permissions|xdmp:default-collections|xdmp:databases|xdmp:database-restore-validate|xdmp:database-restore-status|xdmp:database-restore-cancel|xdmp:database-restore|xdmp:database-name|xdmp:database-forests|xdmp:database-backup-validate|xdmp:database-backup-status|xdmp:database-backup-purge|xdmp:database-backup-cancel|xdmp:database-backup|xdmp:database|xdmp:collection-properties|xdmp:collection-locks|xdmp:collection-delete|xdmp:collation-canonical-uri|xdmp:castable-as|xdmp:can-grant-roles|xdmp:base64-encode|xdmp:base64-decode|xdmp:architecture|xdmp:apply|xdmp:amp-roles|xdmp:amp|xdmp:add64|xdmp:add-response-header|xdmp:access|trgr:trigger-set-recursive|trgr:trigger-set-permissions|trgr:trigger-set-name|trgr:trigger-set-module|trgr:trigger-set-event|trgr:trigger-set-description|trgr:trigger-remove-permissions|trgr:trigger-module|trgr:trigger-get-permissions|trgr:trigger-enable|trgr:trigger-disable|trgr:trigger-database-online-event|trgr:trigger-data-event|trgr:trigger-add-permissions|trgr:remove-trigger|trgr:property-content|trgr:pre-commit|trgr:post-commit|trgr:get-trigger-by-id|trgr:get-trigger|trgr:document-scope|trgr:document-content|trgr:directory-scope|trgr:create-trigger|trgr:collection-scope|trgr:any-property-content|thsr:set-entry|thsr:remove-term|thsr:remove-synonym|thsr:remove-entry|thsr:query-lookup|thsr:lookup|thsr:load|thsr:insert|thsr:expand|thsr:add-synonym|spell:suggest-detailed|spell:suggest|spell:remove-word|spell:make-dictionary|spell:load|spell:levenshtein-distance|spell:is-correct|spell:insert|spell:double-metaphone|spell:add-word|sec:users-collection|sec:user-set-roles|sec:user-set-password|sec:user-set-name|sec:user-set-description|sec:user-set-default-permissions|sec:user-set-default-collections|sec:user-remove-roles|sec:user-privileges|sec:user-get-roles|sec:user-get-description|sec:user-get-default-permissions|sec:user-get-default-collections|sec:user-doc-permissions|sec:user-doc-collections|sec:user-add-roles|sec:unprotect-collection|sec:uid-for-name|sec:set-realm|sec:security-version|sec:security-namespace|sec:security-installed|sec:security-collection|sec:roles-collection|sec:role-set-roles|sec:role-set-name|sec:role-set-description|sec:role-set-default-permissions|sec:role-set-default-collections|sec:role-remove-roles|sec:role-privileges|sec:role-get-roles|sec:role-get-description|sec:role-get-default-permissions|sec:role-get-default-collections|sec:role-doc-permissions|sec:role-doc-collections|sec:role-add-roles|sec:remove-user|sec:remove-role-from-users|sec:remove-role-from-role|sec:remove-role-from-privileges|sec:remove-role-from-amps|sec:remove-role|sec:remove-privilege|sec:remove-amp|sec:protect-collection|sec:privileges-collection|sec:privilege-set-roles|sec:privilege-set-name|sec:privilege-remove-roles|sec:privilege-get-roles|sec:privilege-add-roles|sec:priv-doc-permissions|sec:priv-doc-collections|sec:get-user-names|sec:get-unique-elem-id|sec:get-role-names|sec:get-role-ids|sec:get-privilege|sec:get-distinct-permissions|sec:get-collection|sec:get-amp|sec:create-user-with-role|sec:create-user|sec:create-role|sec:create-privilege|sec:create-amp|sec:collections-collection|sec:collection-set-permissions|sec:collection-remove-permissions|sec:collection-get-permissions|sec:collection-add-permissions|sec:check-admin|sec:amps-collection|sec:amp-set-roles|sec:amp-remove-roles|sec:amp-get-roles|sec:amp-doc-permissions|sec:amp-doc-collections|sec:amp-add-roles|search:unparse|search:suggest|search:snippet|search:search|search:resolve-nodes|search:resolve|search:remove-constraint|search:parse|search:get-default-options|search:estimate|search:check-options|prof:value|prof:reset|prof:report|prof:invoke|prof:eval|prof:enable|prof:disable|prof:allowed|ppt:clean|pki:template-set-request|pki:template-set-name|pki:template-set-key-type|pki:template-set-key-options|pki:template-set-description|pki:template-in-use|pki:template-get-version|pki:template-get-request|pki:template-get-name|pki:template-get-key-type|pki:template-get-key-options|pki:template-get-id|pki:template-get-description|pki:need-certificate|pki:is-temporary|pki:insert-trusted-certificates|pki:insert-template|pki:insert-signed-certificates|pki:insert-certificate-revocation-list|pki:get-trusted-certificate-ids|pki:get-template-ids|pki:get-template-certificate-authority|pki:get-template-by-name|pki:get-template|pki:get-pending-certificate-requests-xml|pki:get-pending-certificate-requests-pem|pki:get-pending-certificate-request|pki:get-certificates-for-template-xml|pki:get-certificates-for-template|pki:get-certificates|pki:get-certificate-xml|pki:get-certificate-pem|pki:get-certificate|pki:generate-temporary-certificate-if-necessary|pki:generate-temporary-certificate|pki:generate-template-certificate-authority|pki:generate-certificate-request|pki:delete-template|pki:delete-certificate|pki:create-template|pdf:make-toc|pdf:insert-toc-headers|pdf:get-toc|pdf:clean|p:status-transition|p:state-transition|p:remove|p:pipelines|p:insert|p:get-by-id|p:get|p:execute|p:create|p:condition|p:collection|p:action|ooxml:runs-merge|ooxml:package-uris|ooxml:package-parts-insert|ooxml:package-parts|msword:clean|mcgm:polygon|mcgm:point|mcgm:geospatial-query-from-elements|mcgm:geospatial-query|mcgm:circle|math:tanh|math:tan|math:sqrt|math:sinh|math:sin|math:pow|math:modf|math:log10|math:log|math:ldexp|math:frexp|math:fmod|math:floor|math:fabs|math:exp|math:cosh|math:cos|math:ceil|math:atan2|math:atan|math:asin|math:acos|map:put|map:map|map:keys|map:get|map:delete|map:count|map:clear|lnk:to|lnk:remove|lnk:insert|lnk:get|lnk:from|lnk:create|kml:polygon|kml:point|kml:interior-polygon|kml:geospatial-query-from-elements|kml:geospatial-query|kml:circle|kml:box|gml:polygon|gml:point|gml:interior-polygon|gml:geospatial-query-from-elements|gml:geospatial-query|gml:circle|gml:box|georss:point|georss:geospatial-query|georss:circle|geo:polygon|geo:point|geo:interior-polygon|geo:geospatial-query-from-elements|geo:geospatial-query|geo:circle|geo:box|fn:zero-or-one|fn:years-from-duration|fn:year-from-dateTime|fn:year-from-date|fn:upper-case|fn:unordered|fn:true|fn:translate|fn:trace|fn:tokenize|fn:timezone-from-time|fn:timezone-from-dateTime|fn:timezone-from-date|fn:sum|fn:subtract-dateTimes-yielding-yearMonthDuration|fn:subtract-dateTimes-yielding-dayTimeDuration|fn:substring-before|fn:substring-after|fn:substring|fn:subsequence|fn:string-to-codepoints|fn:string-pad|fn:string-length|fn:string-join|fn:string|fn:static-base-uri|fn:starts-with|fn:seconds-from-time|fn:seconds-from-duration|fn:seconds-from-dateTime|fn:round-half-to-even|fn:round|fn:root|fn:reverse|fn:resolve-uri|fn:resolve-QName|fn:replace|fn:remove|fn:QName|fn:prefix-from-QName|fn:position|fn:one-or-more|fn:number|fn:not|fn:normalize-unicode|fn:normalize-space|fn:node-name|fn:node-kind|fn:nilled|fn:namespace-uri-from-QName|fn:namespace-uri-for-prefix|fn:namespace-uri|fn:name|fn:months-from-duration|fn:month-from-dateTime|fn:month-from-date|fn:minutes-from-time|fn:minutes-from-duration|fn:minutes-from-dateTime|fn:min|fn:max|fn:matches|fn:lower-case|fn:local-name-from-QName|fn:local-name|fn:last|fn:lang|fn:iri-to-uri|fn:insert-before|fn:index-of|fn:in-scope-prefixes|fn:implicit-timezone|fn:idref|fn:id|fn:hours-from-time|fn:hours-from-duration|fn:hours-from-dateTime|fn:floor|fn:false|fn:expanded-QName|fn:exists|fn:exactly-one|fn:escape-uri|fn:escape-html-uri|fn:error|fn:ends-with|fn:encode-for-uri|fn:empty|fn:document-uri|fn:doc-available|fn:doc|fn:distinct-values|fn:distinct-nodes|fn:default-collation|fn:deep-equal|fn:days-from-duration|fn:day-from-dateTime|fn:day-from-date|fn:data|fn:current-time|fn:current-dateTime|fn:current-date|fn:count|fn:contains|fn:concat|fn:compare|fn:collection|fn:codepoints-to-string|fn:codepoint-equal|fn:ceiling|fn:boolean|fn:base-uri|fn:avg|fn:adjust-time-to-timezone|fn:adjust-dateTime-to-timezone|fn:adjust-date-to-timezone|fn:abs|feed:unsubscribe|feed:subscription|feed:subscribe|feed:request|feed:item|feed:description|excel:clean|entity:enrich|dom:set-pipelines|dom:set-permissions|dom:set-name|dom:set-evaluation-context|dom:set-domain-scope|dom:set-description|dom:remove-pipeline|dom:remove-permissions|dom:remove|dom:get|dom:evaluation-context|dom:domains|dom:domain-scope|dom:create|dom:configuration-set-restart-user|dom:configuration-set-permissions|dom:configuration-set-evaluation-context|dom:configuration-set-default-domain|dom:configuration-get|dom:configuration-create|dom:collection|dom:add-pipeline|dom:add-permissions|dls:retention-rules|dls:retention-rule-remove|dls:retention-rule-insert|dls:retention-rule|dls:purge|dls:node-expand|dls:link-references|dls:link-expand|dls:documents-query|dls:document-versions-query|dls:document-version-uri|dls:document-version-query|dls:document-version-delete|dls:document-version-as-of|dls:document-version|dls:document-update|dls:document-unmanage|dls:document-set-quality|dls:document-set-property|dls:document-set-properties|dls:document-set-permissions|dls:document-set-collections|dls:document-retention-rules|dls:document-remove-properties|dls:document-remove-permissions|dls:document-remove-collections|dls:document-purge|dls:document-manage|dls:document-is-managed|dls:document-insert-and-manage|dls:document-include-query|dls:document-history|dls:document-get-permissions|dls:document-extract-part|dls:document-delete|dls:document-checkout-status|dls:document-checkout|dls:document-checkin|dls:document-add-properties|dls:document-add-permissions|dls:document-add-collections|dls:break-checkout|dls:author-query|dls:as-of-query|dbk:convert|dbg:wait|dbg:value|dbg:stopped|dbg:stop|dbg:step|dbg:status|dbg:stack|dbg:out|dbg:next|dbg:line|dbg:invoke|dbg:function|dbg:finish|dbg:expr|dbg:eval|dbg:disconnect|dbg:detach|dbg:continue|dbg:connect|dbg:clear|dbg:breakpoints|dbg:break|dbg:attached|dbg:attach|cvt:save-converted-documents|cvt:part-uri|cvt:destination-uri|cvt:basepath|cvt:basename|cts:words|cts:word-query-weight|cts:word-query-text|cts:word-query-options|cts:word-query|cts:word-match|cts:walk|cts:uris|cts:uri-match|cts:train|cts:tokenize|cts:thresholds|cts:stem|cts:similar-query-weight|cts:similar-query-nodes|cts:similar-query|cts:shortest-distance|cts:search|cts:score|cts:reverse-query-weight|cts:reverse-query-nodes|cts:reverse-query|cts:remainder|cts:registered-query-weight|cts:registered-query-options|cts:registered-query-ids|cts:registered-query|cts:register|cts:query|cts:quality|cts:properties-query-query|cts:properties-query|cts:polygon-vertices|cts:polygon|cts:point-longitude|cts:point-latitude|cts:point|cts:or-query-queries|cts:or-query|cts:not-query-weight|cts:not-query-query|cts:not-query|cts:near-query-weight|cts:near-query-queries|cts:near-query-options|cts:near-query-distance|cts:near-query|cts:highlight|cts:geospatial-co-occurrences|cts:frequency|cts:fitness|cts:field-words|cts:field-word-query-weight|cts:field-word-query-text|cts:field-word-query-options|cts:field-word-query-field-name|cts:field-word-query|cts:field-word-match|cts:entity-highlight|cts:element-words|cts:element-word-query-weight|cts:element-word-query-text|cts:element-word-query-options|cts:element-word-query-element-name|cts:element-word-query|cts:element-word-match|cts:element-values|cts:element-value-ranges|cts:element-value-query-weight|cts:element-value-query-text|cts:element-value-query-options|cts:element-value-query-element-name|cts:element-value-query|cts:element-value-match|cts:element-value-geospatial-co-occurrences|cts:element-value-co-occurrences|cts:element-range-query-weight|cts:element-range-query-value|cts:element-range-query-options|cts:element-range-query-operator|cts:element-range-query-element-name|cts:element-range-query|cts:element-query-query|cts:element-query-element-name|cts:element-query|cts:element-pair-geospatial-values|cts:element-pair-geospatial-value-match|cts:element-pair-geospatial-query-weight|cts:element-pair-geospatial-query-region|cts:element-pair-geospatial-query-options|cts:element-pair-geospatial-query-longitude-name|cts:element-pair-geospatial-query-latitude-name|cts:element-pair-geospatial-query-element-name|cts:element-pair-geospatial-query|cts:element-pair-geospatial-boxes|cts:element-geospatial-values|cts:element-geospatial-value-match|cts:element-geospatial-query-weight|cts:element-geospatial-query-region|cts:element-geospatial-query-options|cts:element-geospatial-query-element-name|cts:element-geospatial-query|cts:element-geospatial-boxes|cts:element-child-geospatial-values|cts:element-child-geospatial-value-match|cts:element-child-geospatial-query-weight|cts:element-child-geospatial-query-region|cts:element-child-geospatial-query-options|cts:element-child-geospatial-query-element-name|cts:element-child-geospatial-query-child-name|cts:element-child-geospatial-query|cts:element-child-geospatial-boxes|cts:element-attribute-words|cts:element-attribute-word-query-weight|cts:element-attribute-word-query-text|cts:element-attribute-word-query-options|cts:element-attribute-word-query-element-name|cts:element-attribute-word-query-attribute-name|cts:element-attribute-word-query|cts:element-attribute-word-match|cts:element-attribute-values|cts:element-attribute-value-ranges|cts:element-attribute-value-query-weight|cts:element-attribute-value-query-text|cts:element-attribute-value-query-options|cts:element-attribute-value-query-element-name|cts:element-attribute-value-query-attribute-name|cts:element-attribute-value-query|cts:element-attribute-value-match|cts:element-attribute-value-geospatial-co-occurrences|cts:element-attribute-value-co-occurrences|cts:element-attribute-range-query-weight|cts:element-attribute-range-query-value|cts:element-attribute-range-query-options|cts:element-attribute-range-query-operator|cts:element-attribute-range-query-element-name|cts:element-attribute-range-query-attribute-name|cts:element-attribute-range-query|cts:element-attribute-pair-geospatial-values|cts:element-attribute-pair-geospatial-value-match|cts:element-attribute-pair-geospatial-query-weight|cts:element-attribute-pair-geospatial-query-region|cts:element-attribute-pair-geospatial-query-options|cts:element-attribute-pair-geospatial-query-longitude-name|cts:element-attribute-pair-geospatial-query-latitude-name|cts:element-attribute-pair-geospatial-query-element-name|cts:element-attribute-pair-geospatial-query|cts:element-attribute-pair-geospatial-boxes|cts:document-query-uris|cts:document-query|cts:distance|cts:directory-query-uris|cts:directory-query-depth|cts:directory-query|cts:destination|cts:deregister|cts:contains|cts:confidence|cts:collections|cts:collection-query-uris|cts:collection-query|cts:collection-match|cts:classify|cts:circle-radius|cts:circle-center|cts:circle|cts:box-west|cts:box-south|cts:box-north|cts:box-east|cts:box|cts:bearing|cts:arc-intersection|cts:and-query-queries|cts:and-query-options|cts:and-query|cts:and-not-query-positive-query|cts:and-not-query-negative-query|cts:and-not-query|css:get|css:convert|cpf:success|cpf:failure|cpf:document-set-state|cpf:document-set-processing-status|cpf:document-set-last-updated|cpf:document-set-error|cpf:document-get-state|cpf:document-get-processing-status|cpf:document-get-last-updated|cpf:document-get-error|cpf:check-transition|alert:spawn-matching-actions|alert:rule-user-id-query|alert:rule-set-user-id|alert:rule-set-query|alert:rule-set-options|alert:rule-set-name|alert:rule-set-description|alert:rule-set-action|alert:rule-remove|alert:rule-name-query|alert:rule-insert|alert:rule-id-query|alert:rule-get-user-id|alert:rule-get-query|alert:rule-get-options|alert:rule-get-name|alert:rule-get-id|alert:rule-get-description|alert:rule-get-action|alert:rule-action-query|alert:remove-triggers|alert:make-rule|alert:make-log-action|alert:make-config|alert:make-action|alert:invoke-matching-actions|alert:get-my-rules|alert:get-all-rules|alert:get-actions|alert:find-matching-rules|alert:create-triggers|alert:config-set-uri|alert:config-set-trigger-ids|alert:config-set-options|alert:config-set-name|alert:config-set-description|alert:config-set-cpf-domain-names|alert:config-set-cpf-domain-ids|alert:config-insert|alert:config-get-uri|alert:config-get-trigger-ids|alert:config-get-options|alert:config-get-name|alert:config-get-id|alert:config-get-description|alert:config-get-cpf-domain-names|alert:config-get-cpf-domain-ids|alert:config-get|alert:config-delete|alert:action-set-options|alert:action-set-name|alert:action-set-module-root|alert:action-set-module-db|alert:action-set-module|alert:action-set-description|alert:action-remove|alert:action-insert|alert:action-get-options|alert:action-get-name|alert:action-get-module-root|alert:action-get-module-db|alert:action-get-module|alert:action-get-description|zero-or-one|years-from-duration|year-from-dateTime|year-from-date|upper-case|unordered|true|translate|trace|tokenize|timezone-from-time|timezone-from-dateTime|timezone-from-date|sum|subtract-dateTimes-yielding-yearMonthDuration|subtract-dateTimes-yielding-dayTimeDuration|substring-before|substring-after|substring|subsequence|string-to-codepoints|string-pad|string-length|string-join|string|static-base-uri|starts-with|seconds-from-time|seconds-from-duration|seconds-from-dateTime|round-half-to-even|round|root|reverse|resolve-uri|resolve-QName|replace|remove|QName|prefix-from-QName|position|one-or-more|number|not|normalize-unicode|normalize-space|node-name|node-kind|nilled|namespace-uri-from-QName|namespace-uri-for-prefix|namespace-uri|name|months-from-duration|month-from-dateTime|month-from-date|minutes-from-time|minutes-from-duration|minutes-from-dateTime|min|max|matches|lower-case|local-name-from-QName|local-name|last|lang|iri-to-uri|insert-before|index-of|in-scope-prefixes|implicit-timezone|idref|id|hours-from-time|hours-from-duration|hours-from-dateTime|floor|false|expanded-QName|exists|exactly-one|escape-uri|escape-html-uri|error|ends-with|encode-for-uri|empty|document-uri|doc-available|doc|distinct-values|distinct-nodes|default-collation|deep-equal|days-from-duration|day-from-dateTime|day-from-date|data|current-time|current-dateTime|current-date|count|contains|concat|compare|collection|codepoints-to-string|codepoint-equal|ceiling|boolean|base-uri|avg|adjust-time-to-timezone|adjust-dateTime-to-timezone|adjust-date-to-timezone|abs)\\b")}));
+    // Matching normal words if none of the previous regular expressions matched
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[A-Za-z0-9_\\-\\:]+")}));
+    // Matching whitespaces
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^[\\t\\n\\r \\xA0]+")}));
 
-        setShortcutStylePatterns(_shortcutStylePatterns);
-        setFallthroughStylePatterns(_fallthroughStylePatterns);
-    }
+    setShortcutStylePatterns(_shortcutStylePatterns);
+    setFallthroughStylePatterns(_fallthroughStylePatterns);
+  }
 
-    public static List<String> getFileExtensions() {
-        return Arrays.asList(new String[]{"xq", "xquery"});
-    }
+  public static List<String> getFileExtensions() {
+    return Arrays.asList(new String[]{"xq", "xquery"});
+  }
 }
diff --git a/src/prettify/lang/LangYaml.java b/src/prettify/lang/LangYaml.java
index d889d36..6b8673e 100644
--- a/src/prettify/lang/LangYaml.java
+++ b/src/prettify/lang/LangYaml.java
@@ -17,8 +17,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.regex.Pattern;
-import prettify.Lang;
-import prettify.Prettify;
+import prettify.parser.Prettify;
 
 /**
  * This is similar to the lang-yaml.js in JavaScript Prettify.
@@ -32,28 +31,28 @@
  */
 public class LangYaml extends Lang {
 
-    public LangYaml() {
-        List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
-        List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
+  public LangYaml() {
+    List<List<Object>> _shortcutStylePatterns = new ArrayList<List<Object>>();
+    List<List<Object>> _fallthroughStylePatterns = new ArrayList<List<Object>>();
 
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PUNCTUATION, Pattern.compile("^[:|>?]+"), null, ":|>?"}));
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_DECLARATION, Pattern.compile("^%(?:YAML|TAG)[^#\\r\\n]+"), null, "%"}));
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_TYPE, Pattern.compile("^[&]\\S+"), null, "&"}));
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_TYPE, Pattern.compile("^!\\S*"), null, "!"}));
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^\"(?:[^\\\\\"]|\\\\.)*(?:\"|$)"), null, "\""}));
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^'(?:[^']|'')*(?:'|$)"), null, "'"}));
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^#[^\\r\\n]*"), null, "#"}));
-        _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^\\s+"), null, " \t\r\n"}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_DECLARATION, Pattern.compile("^(?:---|\\.\\.\\.)(?:[\\r\\n]|$)")}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PUNCTUATION, Pattern.compile("^-")}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_KEYWORD, Pattern.compile("^\\w+:[ \\r\\n]")}));
-        _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^\\w+")}));
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PUNCTUATION, Pattern.compile("^[:|>?]+"), null, ":|>?"}));
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_DECLARATION, Pattern.compile("^%(?:YAML|TAG)[^#\\r\\n]+"), null, "%"}));
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_TYPE, Pattern.compile("^[&]\\S+"), null, "&"}));
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_TYPE, Pattern.compile("^!\\S*"), null, "!"}));
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^\"(?:[^\\\\\"]|\\\\.)*(?:\"|$)"), null, "\""}));
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_STRING, Pattern.compile("^'(?:[^']|'')*(?:'|$)"), null, "'"}));
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_COMMENT, Pattern.compile("^#[^\\r\\n]*"), null, "#"}));
+    _shortcutStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^\\s+"), null, " \t\r\n"}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_DECLARATION, Pattern.compile("^(?:---|\\.\\.\\.)(?:[\\r\\n]|$)")}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PUNCTUATION, Pattern.compile("^-")}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_KEYWORD, Pattern.compile("^\\w+:[ \\r\\n]")}));
+    _fallthroughStylePatterns.add(Arrays.asList(new Object[]{Prettify.PR_PLAIN, Pattern.compile("^\\w+")}));
 
-        setShortcutStylePatterns(_shortcutStylePatterns);
-        setFallthroughStylePatterns(_fallthroughStylePatterns);
-    }
+    setShortcutStylePatterns(_shortcutStylePatterns);
+    setFallthroughStylePatterns(_fallthroughStylePatterns);
+  }
 
-    public static List<String> getFileExtensions() {
-        return Arrays.asList(new String[]{"yaml", "yml"});
-    }
+  public static List<String> getFileExtensions() {
+    return Arrays.asList(new String[]{"yaml", "yml"});
+  }
 }
diff --git a/src/prettify/parser/CombinePrefixPattern.java b/src/prettify/parser/CombinePrefixPattern.java
new file mode 100644
index 0000000..d170208
--- /dev/null
+++ b/src/prettify/parser/CombinePrefixPattern.java
@@ -0,0 +1,298 @@
+// Copyright (C) 2006 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package prettify.parser;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import prettify.Util;
+
+/**
+ * This is similar to the combinePrefixPattern.js in JavaScript Prettify.
+ * 
+ * All comments are adapted from the JavaScript Prettify.
+ * 
+ * @author Chan Wai Shing <cws1989@gmail.com>
+ */
+public class CombinePrefixPattern {
+
+  protected int capturedGroupIndex = 0;
+  protected boolean needToFoldCase = false;
+
+  public CombinePrefixPattern() {
+  }
+
+  /**
+   * Given a group of {@link java.util.regex.Pattern}s, returns a {@code RegExp} that globally
+   * matches the union of the sets of strings matched by the input RegExp.
+   * Since it matches globally, if the input strings have a start-of-input
+   * anchor (/^.../), it is ignored for the purposes of unioning.
+   * @param regexs non multiline, non-global regexs.
+   * @return Pattern a global regex.
+   */
+  public Pattern combinePrefixPattern(List<Pattern> regexs) throws Exception {
+    boolean ignoreCase = false;
+
+    for (int i = 0, n = regexs.size(); i < n; ++i) {
+      Pattern regex = regexs.get(i);
+      if ((regex.flags() & Pattern.CASE_INSENSITIVE) != 0) {
+        ignoreCase = true;
+      } else if (Util.test(Pattern.compile("[a-z]", Pattern.CASE_INSENSITIVE), regex.pattern().replaceAll("\\\\[Uu][0-9A-Fa-f]{4}|\\\\[Xx][0-9A-Fa-f]{2}|\\\\[^UuXx]", ""))) {
+        needToFoldCase = true;
+        ignoreCase = false;
+        break;
+      }
+    }
+
+    List<String> rewritten = new ArrayList<String>();
+    for (int i = 0, n = regexs.size(); i < n; ++i) {
+      Pattern regex = regexs.get(i);
+      if ((regex.flags() & Pattern.MULTILINE) != 0) {
+        throw new Exception(regex.pattern());
+      }
+      rewritten.add("(?:" + allowAnywhereFoldCaseAndRenumberGroups(regex) + ")");
+    }
+
+    return ignoreCase ? Pattern.compile(Util.join(rewritten, "|"), Pattern.CASE_INSENSITIVE) : Pattern.compile(Util.join(rewritten, "|"));
+  }
+  protected static final Map<Character, Integer> escapeCharToCodeUnit = new HashMap<Character, Integer>();
+
+  static {
+    escapeCharToCodeUnit.put('b', 8);
+    escapeCharToCodeUnit.put('t', 9);
+    escapeCharToCodeUnit.put('n', 0xa);
+    escapeCharToCodeUnit.put('v', 0xb);
+    escapeCharToCodeUnit.put('f', 0xc);
+    escapeCharToCodeUnit.put('r', 0xf);
+  }
+
+  protected static int decodeEscape(String charsetPart) {
+    Integer cc0 = charsetPart.codePointAt(0);
+    if (cc0 != 92 /* \\ */) {
+      return cc0;
+    }
+    char c1 = charsetPart.charAt(1);
+    cc0 = escapeCharToCodeUnit.get(c1);
+    if (cc0 != null) {
+      return cc0;
+    } else if ('0' <= c1 && c1 <= '7') {
+      return Integer.parseInt(charsetPart.substring(1), 8);
+    } else if (c1 == 'u' || c1 == 'x') {
+      return Integer.parseInt(charsetPart.substring(2), 16);
+    } else {
+      return charsetPart.codePointAt(1);
+    }
+  }
+
+  protected static String encodeEscape(int charCode) {
+    if (charCode < 0x20) {
+      return (charCode < 0x10 ? "\\x0" : "\\x") + Integer.toString(charCode, 16);
+    }
+
+    String ch = new String(Character.toChars(charCode));
+    return (charCode == '\\' || charCode == '-' || charCode == ']' || charCode == '^')
+            ? "\\" + ch : ch;
+  }
+
+  protected static String caseFoldCharset(String charSet) {
+    String[] charsetParts = Util.match(Pattern.compile("\\\\u[0-9A-Fa-f]{4}"
+            + "|\\\\x[0-9A-Fa-f]{2}"
+            + "|\\\\[0-3][0-7]{0,2}"
+            + "|\\\\[0-7]{1,2}"
+            + "|\\\\[\\s\\S]"
+            + "|-"
+            + "|[^-\\\\]"), charSet.substring(1, charSet.length() - 1), true);
+    List<List<Integer>> ranges = new ArrayList<List<Integer>>();
+    boolean inverse = charsetParts[0] != null && charsetParts[0].equals("^");
+
+    List<String> out = new ArrayList<String>(Arrays.asList(new String[]{"["}));
+    if (inverse) {
+      out.add("^");
+    }
+
+    for (int i = inverse ? 1 : 0, n = charsetParts.length; i < n; ++i) {
+      String p = charsetParts[i];
+      if (Util.test(Pattern.compile("\\\\[bdsw]", Pattern.CASE_INSENSITIVE), p)) {  // Don't muck with named groups.
+        out.add(p);
+      } else {
+        int start = decodeEscape(p);
+        int end;
+        if (i + 2 < n && "-".equals(charsetParts[i + 1])) {
+          end = decodeEscape(charsetParts[i + 2]);
+          i += 2;
+        } else {
+          end = start;
+        }
+        ranges.add(Arrays.asList(new Integer[]{start, end}));
+        // If the range might intersect letters, then expand it.
+        // This case handling is too simplistic.
+        // It does not deal with non-latin case folding.
+        // It works for latin source code identifiers though.
+        if (!(end < 65 || start > 122)) {
+          if (!(end < 65 || start > 90)) {
+            ranges.add(Arrays.asList(new Integer[]{Math.max(65, start) | 32, Math.min(end, 90) | 32}));
+          }
+          if (!(end < 97 || start > 122)) {
+            ranges.add(Arrays.asList(new Integer[]{Math.max(97, start) & ~32, Math.min(end, 122) & ~32}));
+          }
+        }
+      }
+    }
+
+    // [[1, 10], [3, 4], [8, 12], [14, 14], [16, 16], [17, 17]]
+    // -> [[1, 12], [14, 14], [16, 17]]
+    Collections.sort(ranges, new Comparator<List<Integer>>() {
+
+      @Override
+      public int compare(List<Integer> a, List<Integer> b) {
+        return a.get(0) != b.get(0) ? (a.get(0) - b.get(0)) : (b.get(1) - a.get(1));
+      }
+    });
+    List<List<Integer>> consolidatedRanges = new ArrayList<List<Integer>>();
+//        List<Integer> lastRange = Arrays.asList(new Integer[]{0, 0});
+    List<Integer> lastRange = new ArrayList<Integer>(Arrays.asList(new Integer[]{0, 0}));
+    for (int i = 0; i < ranges.size(); ++i) {
+      List<Integer> range = ranges.get(i);
+      if (lastRange.get(1) != null && range.get(0) <= lastRange.get(1) + 1) {
+        lastRange.set(1, Math.max(lastRange.get(1), range.get(1)));
+      } else {
+        // reference of lastRange is added
+        consolidatedRanges.add(lastRange = range);
+      }
+    }
+
+    for (int i = 0; i < consolidatedRanges.size(); ++i) {
+      List<Integer> range = consolidatedRanges.get(i);
+      out.add(encodeEscape(range.get(0)));
+      if (range.get(1) > range.get(0)) {
+        if (range.get(1) + 1 > range.get(0)) {
+          out.add("-");
+        }
+        out.add(encodeEscape(range.get(1)));
+      }
+    }
+    out.add("]");
+
+    return Util.join(out);
+  }
+
+  protected String allowAnywhereFoldCaseAndRenumberGroups(Pattern regex) {
+    // Split into character sets, escape sequences, punctuation strings
+    // like ('(', '(?:', ')', '^'), and runs of characters that do not
+    // include any of the above.
+    String[] parts = Util.match(Pattern.compile("(?:"
+            + "\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]" // a character set
+            + "|\\\\u[A-Fa-f0-9]{4}" // a unicode escape
+            + "|\\\\x[A-Fa-f0-9]{2}" // a hex escape
+            + "|\\\\[0-9]+" // a back-reference or octal escape
+            + "|\\\\[^ux0-9]" // other escape sequence
+            + "|\\(\\?[:!=]" // start of a non-capturing group
+            + "|[\\(\\)\\^]" // start/end of a group, or line start
+            + "|[^\\x5B\\x5C\\(\\)\\^]+" // run of other characters
+            + ")"), regex.pattern(), true);
+    int n = parts.length;
+
+    // Maps captured group numbers to the number they will occupy in
+    // the output or to -1 if that has not been determined, or to
+    // undefined if they need not be capturing in the output.
+    Map<Integer, Integer> capturedGroups = new HashMap<Integer, Integer>();
+
+    // Walk over and identify back references to build the capturedGroups
+    // mapping.
+    for (int i = 0, groupIndex = 0; i < n; ++i) {
+      String p = parts[i];
+      if (p.equals("(")) {
+        // groups are 1-indexed, so max group index is count of '('
+        ++groupIndex;
+      } else if ('\\' == p.charAt(0)) {
+        try {
+          int decimalValue = Math.abs(Integer.parseInt(p.substring(1)));
+          if (decimalValue <= groupIndex) {
+            capturedGroups.put(decimalValue, -1);
+          } else {
+            // Replace with an unambiguous escape sequence so that
+            // an octal escape sequence does not turn into a backreference
+            // to a capturing group from an earlier regex.
+            parts[i] = encodeEscape(decimalValue);
+          }
+        } catch (NumberFormatException ex) {
+        }
+      }
+    }
+
+    // Renumber groups and reduce capturing groups to non-capturing groups
+    // where possible.
+    for (int i : capturedGroups.keySet()) {
+      if (-1 == capturedGroups.get(i)) {
+        capturedGroups.put(i, ++capturedGroupIndex);
+      }
+    }
+    for (int i = 0, groupIndex = 0; i < n; ++i) {
+      String p = parts[i];
+      if (p.equals("(")) {
+        ++groupIndex;
+        if (capturedGroups.get(groupIndex) == null) {
+          parts[i] = "(?:";
+        }
+      } else if ('\\' == p.charAt(0)) {
+        try {
+          int decimalValue = Math.abs(Integer.parseInt(p.substring(1)));
+          if (decimalValue <= groupIndex) {
+            parts[i] = "\\" + capturedGroups.get(decimalValue);
+          }
+        } catch (NumberFormatException ex) {
+        }
+      }
+    }
+
+    // Remove any prefix anchors so that the output will match anywhere.
+    // ^^ really does mean an anchored match though.
+    for (int i = 0; i < n; ++i) {
+      if ("^".equals(parts[i]) && !"^".equals(parts[i + 1])) {
+        parts[i] = "";
+      }
+    }
+
+    // Expand letters to groups to handle mixing of case-sensitive and
+    // case-insensitive patterns if necessary.
+    if ((regex.flags() & Pattern.CASE_INSENSITIVE) != 0 && needToFoldCase) {
+      for (int i = 0; i < n; ++i) {
+        String p = parts[i];
+        char ch0 = p.length() > 0 ? p.charAt(0) : 0;
+        if (p.length() >= 2 && ch0 == '[') {
+          parts[i] = caseFoldCharset(p);
+        } else if (ch0 != '\\') {
+          // TODO: handle letters in numeric escapes.
+          StringBuffer sb = new StringBuffer();
+          Matcher _matcher = Pattern.compile("[a-zA-Z]").matcher(p);
+          while (_matcher.find()) {
+            int cc = _matcher.group(0).codePointAt(0);
+            _matcher.appendReplacement(sb, "");
+            sb.append("[").append(Character.toString((char) (cc & ~32))).append(Character.toString((char) (cc | 32))).append("]");
+          }
+          _matcher.appendTail(sb);
+          parts[i] = sb.toString();
+        }
+      }
+    }
+
+    return Util.join(parts);
+  }
+}
diff --git a/src/prettify/parser/Job.java b/src/prettify/parser/Job.java
new file mode 100644
index 0000000..e56edfd
--- /dev/null
+++ b/src/prettify/parser/Job.java
@@ -0,0 +1,123 @@
+// Copyright (C) 2006 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package prettify.parser;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This is the job object that similar to those in JavaScript Prettify.
+ * 
+ * @author Chan Wai Shing <cws1989@gmail.com>
+ */
+public class Job {
+
+  /**
+   * The starting point of the source code.
+   */
+  protected int basePos;
+  /**
+   * The source code.
+   */
+  protected String sourceCode;
+  /**
+   * The parsed results. n<sup>th</sup> items are starting position position, 
+   * n+1<sup>th</sup> items are the three-letter style keyword, where n start 
+   * from 0.
+   */
+  protected List<Object> decorations;
+
+  /**
+   * Constructor.
+   */
+  public Job() {
+    this(0, "");
+  }
+
+  /**
+   * Constructor.
+   * 
+   * @param basePos the starting point of the source code
+   * @param sourceCode the source code
+   */
+  public Job(int basePos, String sourceCode) {
+    if (sourceCode == null) {
+      throw new NullPointerException("argument 'sourceCode' cannot be null");
+    }
+    this.basePos = basePos;
+    this.sourceCode = sourceCode;
+    decorations = new ArrayList<Object>();
+  }
+
+  /**
+   * Set the starting point of the source code.
+   * 
+   * @return the position
+   */
+  public int getBasePos() {
+    return basePos;
+  }
+
+  /**
+   * Set the starting point of the source code.
+   * 
+   * @param basePos the position
+   */
+  public void setBasePos(int basePos) {
+    this.basePos = basePos;
+  }
+
+  /**
+   * Get the source code.
+   * 
+   * @return the source code
+   */
+  public String getSourceCode() {
+    return sourceCode;
+  }
+
+  /**
+   * Set the source code.
+   * 
+   * @param sourceCode the source code 
+   */
+  public void setSourceCode(String sourceCode) {
+    if (sourceCode == null) {
+      throw new NullPointerException("argument 'sourceCode' cannot be null");
+    }
+    this.sourceCode = sourceCode;
+  }
+
+  /**
+   * Get the parsed results. see {@link #decorations}.
+   * 
+   * @return the parsed results
+   */
+  public List<Object> getDecorations() {
+    return new ArrayList<Object>(decorations);
+  }
+
+  /**
+   * Set the parsed results. see {@link #decorations}.
+   * 
+   * @param decorations the parsed results
+   */
+  public void setDecorations(List<Object> decorations) {
+    if (decorations == null) {
+      this.decorations = new ArrayList<Object>();
+      return;
+    }
+    this.decorations = new ArrayList<Object>(decorations);
+  }
+}
diff --git a/src/prettify/parser/Prettify.java b/src/prettify/parser/Prettify.java
new file mode 100644
index 0000000..dab2da9
--- /dev/null
+++ b/src/prettify/parser/Prettify.java
@@ -0,0 +1,815 @@
+// Copyright (C) 2006 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package prettify.parser;
+
+import prettify.lang.Lang;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Pattern;
+import prettify.Util;
+import prettify.lang.LangAppollo;
+import prettify.lang.LangClj;
+import prettify.lang.LangCss;
+import prettify.lang.LangGo;
+import prettify.lang.LangHs;
+import prettify.lang.LangLisp;
+import prettify.lang.LangLua;
+import prettify.lang.LangMl;
+import prettify.lang.LangN;
+import prettify.lang.LangScala;
+import prettify.lang.LangSql;
+import prettify.lang.LangTex;
+import prettify.lang.LangVb;
+import prettify.lang.LangVhdl;
+import prettify.lang.LangWiki;
+import prettify.lang.LangXq;
+import prettify.lang.LangYaml;
+
+/**
+ * This is similar to the prettify.js in JavaScript Prettify.
+ * 
+ * All comments are adapted from the JavaScript Prettify.
+ * 
+ * <p>
+ * Some functions for browser-side pretty printing of code contained in html.
+ * </p>
+ *
+ * <p>
+ * For a fairly comprehensive set of languages see the
+ * <a href="http://google-code-prettify.googlecode.com/svn/trunk/README.html#langs">README</a>
+ * file that came with this source.  At a minimum, the lexer should work on a
+ * number of languages including C and friends, Java, Python, Bash, SQL, HTML,
+ * XML, CSS, Javascript, and Makefiles.  It works passably on Ruby, PHP and Awk
+ * and a subset of Perl, but, because of commenting conventions, doesn't work on
+ * Smalltalk, Lisp-like, or CAML-like languages without an explicit lang class.
+ * <p>
+ * Usage: <ol>
+ * <li> include this source file in an html page via
+ *   {@code <script type="text/javascript" src="/path/to/prettify.js"></script>}
+ * <li> define style rules.  See the example page for examples.
+ * <li> mark the {@code <pre>} and {@code <code>} tags in your source with
+ *    {@code class=prettyprint.}
+ *    You can also use the (html deprecated) {@code <xmp>} tag, but the pretty
+ *    printer needs to do more substantial DOM manipulations to support that, so
+ *    some css styles may not be preserved.
+ * </ol>
+ * That's it.  I wanted to keep the API as simple as possible, so there's no
+ * need to specify which language the code is in, but if you wish, you can add
+ * another class to the {@code <pre>} or {@code <code>} element to specify the
+ * language, as in {@code <pre class="prettyprint lang-java">}.  Any class that
+ * starts with "lang-" followed by a file extension, specifies the file type.
+ * See the "lang-*.js" files in this directory for code that implements
+ * per-language file handlers.
+ * <p>
+ * Change log:<br>
+ * cbeust, 2006/08/22
+ * <blockquote>
+ *   Java annotations (start with "@") are now captured as literals ("lit")
+ * </blockquote>
+ */
+public class Prettify {
+
+  private static final Logger LOG = Logger.getLogger(Prettify.class.getName());
+  // Keyword lists for various languages.
+  public static final String FLOW_CONTROL_KEYWORDS = "break,continue,do,else,for,if,return,while";
+  public static final String C_KEYWORDS = FLOW_CONTROL_KEYWORDS + "," + "auto,case,char,const,default,"
+          + "double,enum,extern,float,goto,int,long,register,short,signed,sizeof,"
+          + "static,struct,switch,typedef,union,unsigned,void,volatile";
+  public static final String COMMON_KEYWORDS = C_KEYWORDS + "," + "catch,class,delete,false,import,"
+          + "new,operator,private,protected,public,this,throw,true,try,typeof";
+  public static final String CPP_KEYWORDS = COMMON_KEYWORDS + "," + "alignof,align_union,asm,axiom,bool,"
+          + "concept,concept_map,const_cast,constexpr,decltype,"
+          + "dynamic_cast,explicit,export,friend,inline,late_check,"
+          + "mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,"
+          + "template,typeid,typename,using,virtual,where";
+  public static final String JAVA_KEYWORDS = COMMON_KEYWORDS + ","
+          + "abstract,boolean,byte,extends,final,finally,implements,import,"
+          + "instanceof,null,native,package,strictfp,super,synchronized,throws,"
+          + "transient";
+  public static final String CSHARP_KEYWORDS = JAVA_KEYWORDS + ","
+          + "as,base,by,checked,decimal,delegate,descending,dynamic,event,"
+          + "fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,"
+          + "object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,"
+          + "stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var";
+  public static final String COFFEE_KEYWORDS = "all,and,by,catch,class,else,extends,false,finally,"
+          + "for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,"
+          + "true,try,unless,until,when,while,yes";
+  public static final String JSCRIPT_KEYWORDS = COMMON_KEYWORDS + ","
+          + "debugger,eval,export,function,get,null,set,undefined,var,with,"
+          + "Infinity,NaN";
+  public static final String PERL_KEYWORDS = "caller,delete,die,do,dump,elsif,eval,exit,foreach,for,"
+          + "goto,if,import,last,local,my,next,no,our,print,package,redo,require,"
+          + "sub,undef,unless,until,use,wantarray,while,BEGIN,END";
+  public static final String PYTHON_KEYWORDS = FLOW_CONTROL_KEYWORDS + "," + "and,as,assert,class,def,del,"
+          + "elif,except,exec,finally,from,global,import,in,is,lambda,"
+          + "nonlocal,not,or,pass,print,raise,try,with,yield,"
+          + "False,True,None";
+  public static final String RUBY_KEYWORDS = FLOW_CONTROL_KEYWORDS + "," + "alias,and,begin,case,class,"
+          + "def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,"
+          + "rescue,retry,self,super,then,true,undef,unless,until,when,yield,"
+          + "BEGIN,END";
+  public static final String SH_KEYWORDS = FLOW_CONTROL_KEYWORDS + "," + "case,done,elif,esac,eval,fi,"
+          + "function,in,local,set,then,until";
+  public static final String ALL_KEYWORDS = CPP_KEYWORDS + "," + CSHARP_KEYWORDS + "," + JSCRIPT_KEYWORDS + "," + PERL_KEYWORDS + ","
+          + PYTHON_KEYWORDS + "," + RUBY_KEYWORDS + "," + SH_KEYWORDS;
+  public static final Pattern C_TYPES = Pattern.compile("^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\\d*)");
+  // token style names.  correspond to css classes
+  /**
+   * token style for a string literal
+   */
+  public static final String PR_STRING = "str";
+  /**
+   * token style for a keyword
+   */
+  public static final String PR_KEYWORD = "kwd";
+  /**
+   * token style for a comment
+   */
+  public static final String PR_COMMENT = "com";
+  /**
+   * token style for a type
+   */
+  public static final String PR_TYPE = "typ";
+  /**
+   * token style for a literal value.  e.g. 1, null, true.
+   */
+  public static final String PR_LITERAL = "lit";
+  /**
+   * token style for a punctuation string.
+   */
+  public static final String PR_PUNCTUATION = "pun";
+  /**
+   * token style for a plain text.
+   */
+  public static final String PR_PLAIN = "pln";
+  /**
+   * token style for an sgml tag.
+   */
+  public static final String PR_TAG = "tag";
+  /**
+   * token style for a markup declaration such as a DOCTYPE.
+   */
+  public static final String PR_DECLARATION = "dec";
+  /**
+   * token style for embedded source.
+   */
+  public static final String PR_SOURCE = "src";
+  /**
+   * token style for an sgml attribute name.
+   */
+  public static final String PR_ATTRIB_NAME = "atn";
+  /**
+   * token style for an sgml attribute value.
+   */
+  public static final String PR_ATTRIB_VALUE = "atv";
+  /**
+   * A class that indicates a section of markup that is not code, e.g. to allow
+   * embedding of line numbers within code listings.
+   */
+  public static final String PR_NOCODE = "nocode";
+  /**
+   * A set of tokens that can precede a regular expression literal in
+   * javascript
+   * http://web.archive.org/web/20070717142515/http://www.mozilla.org/js/language/js20/rationale/syntax.html
+   * has the full list, but I've removed ones that might be problematic when
+   * seen in languages that don't support regular expression literals.
+   *
+   * <p>Specifically, I've removed any keywords that can't precede a regexp
+   * literal in a syntactically legal javascript program, and I've removed the
+   * "in" keyword since it's not a keyword in many languages, and might be used
+   * as a count of inches.
+   *
+   * <p>The link above does not accurately describe EcmaScript rules since
+   * it fails to distinguish between (a=++/b/i) and (a++/b/i) but it works
+   * very well in practice.
+   */
+  private static final String REGEXP_PRECEDER_PATTERN = "(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[+\\-]=|->|\\/=?|::?|<<?=?|>>?>?=?|,|;|\\?|@|\\[|~|\\{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";
+  // CAVEAT: this does not properly handle the case where a regular
+  // expression immediately follows another since a regular expression may
+  // have flags for case-sensitivity and the like.  Having regexp tokens
+  // adjacent is not valid in any language I'm aware of, so I'm punting.
+  // TODO: maybe style special characters inside a regexp as punctuation.
+
+  public Prettify() {
+    try {
+      Map<String, Object> decorateSourceMap = new HashMap<String, Object>();
+      decorateSourceMap.put("keywords", ALL_KEYWORDS);
+      decorateSourceMap.put("hashComments", true);
+      decorateSourceMap.put("cStyleComments", true);
+      decorateSourceMap.put("multiLineStrings", true);
+      decorateSourceMap.put("regexLiterals", true);
+      registerLangHandler(sourceDecorator(decorateSourceMap), Arrays.asList(new String[]{"default-code"}));
+
+      List<List<Object>> shortcutStylePatterns, fallthroughStylePatterns;
+
+      shortcutStylePatterns = new ArrayList<List<Object>>();
+      fallthroughStylePatterns = new ArrayList<List<Object>>();
+      fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_PLAIN, Pattern.compile("^[^<?]+")}));
+      fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_DECLARATION, Pattern.compile("^<!\\w[^>]*(?:>|$)")}));
+      fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_COMMENT, Pattern.compile("^<\\!--[\\s\\S]*?(?:-\\->|$)")}));
+      // Unescaped content in an unknown language
+      fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-", Pattern.compile("^<\\?([\\s\\S]+?)(?:\\?>|$)")}));
+      fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-", Pattern.compile("^<%([\\s\\S]+?)(?:%>|$)")}));
+      fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_PUNCTUATION, Pattern.compile("^(?:<[%?]|[%?]>)")}));
+      fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-", Pattern.compile("^<xmp\\b[^>]*>([\\s\\S]+?)<\\/xmp\\b[^>]*>", Pattern.CASE_INSENSITIVE)}));
+      // Unescaped content in javascript.  (Or possibly vbscript).
+      fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-js", Pattern.compile("^<script\\b[^>]*>([\\s\\S]*?)(<\\/script\\b[^>]*>)", Pattern.CASE_INSENSITIVE)}));
+      // Contains unescaped stylesheet content
+      fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-css", Pattern.compile("^<style\\b[^>]*>([\\s\\S]*?)(<\\/style\\b[^>]*>)", Pattern.CASE_INSENSITIVE)}));
+      fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-in.tag", Pattern.compile("^(<\\/?[a-z][^<>]*>)", Pattern.CASE_INSENSITIVE)}));
+      registerLangHandler(new CreateSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns), Arrays.asList(new String[]{"default-markup", "htm", "html", "mxml", "xhtml", "xml", "xsl"}));
+
+      shortcutStylePatterns = new ArrayList<List<Object>>();
+      fallthroughStylePatterns = new ArrayList<List<Object>>();
+      shortcutStylePatterns.add(Arrays.asList(new Object[]{PR_PLAIN, Pattern.compile("^[\\s]+"), null, " \t\r\n"}));
+      shortcutStylePatterns.add(Arrays.asList(new Object[]{PR_ATTRIB_VALUE, Pattern.compile("^(?:\\\"[^\\\"]*\\\"?|\\'[^\\']*\\'?)"), null, "\"'"}));
+      fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_TAG, Pattern.compile("^^<\\/?[a-z](?:[\\w.:-]*\\w)?|\\/?>$", Pattern.CASE_INSENSITIVE)}));
+      fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_ATTRIB_NAME, Pattern.compile("^(?!style[\\s=]|on)[a-z](?:[\\w:-]*\\w)?", Pattern.CASE_INSENSITIVE)}));
+      fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-uq.val", Pattern.compile("^=\\s*([^>\\'\\\"\\s]*(?:[^>\\'\\\"\\s\\/]|\\/(?=\\s)))", Pattern.CASE_INSENSITIVE)}));
+      fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_PUNCTUATION, Pattern.compile("^[=<>\\/]+")}));
+      fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-js", Pattern.compile("^on\\w+\\s*=\\s*\\\"([^\\\"]+)\\\"", Pattern.CASE_INSENSITIVE)}));
+      fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-js", Pattern.compile("^on\\w+\\s*=\\s*\\'([^\\']+)\\'", Pattern.CASE_INSENSITIVE)}));
+      fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-js", Pattern.compile("^on\\w+\\s*=\\s*([^\\\"\\'>\\s]+)", Pattern.CASE_INSENSITIVE)}));
+      fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-css", Pattern.compile("^style\\s*=\\s*\\\"([^\\\"]+)\\\"", Pattern.CASE_INSENSITIVE)}));
+      fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-css", Pattern.compile("^style\\s*=\\s*\\'([^\\']+)\\'", Pattern.CASE_INSENSITIVE)}));
+      fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-css", Pattern.compile("^style\\s*=\\s\\*([^\\\"\\'>\\s]+)", Pattern.CASE_INSENSITIVE)}));
+      registerLangHandler(new CreateSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns), Arrays.asList(new String[]{"in.tag"}));
+
+      shortcutStylePatterns = new ArrayList<List<Object>>();
+      fallthroughStylePatterns = new ArrayList<List<Object>>();
+      fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_ATTRIB_VALUE, Pattern.compile("^[\\s\\S]+")}));
+      registerLangHandler(new CreateSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns), Arrays.asList(new String[]{"uq.val"}));
+
+      decorateSourceMap = new HashMap<String, Object>();
+      decorateSourceMap.put("keywords", CPP_KEYWORDS);
+      decorateSourceMap.put("hashComments", true);
+      decorateSourceMap.put("cStyleComments", true);
+      decorateSourceMap.put("types", C_TYPES);
+      registerLangHandler(sourceDecorator(decorateSourceMap), Arrays.asList(new String[]{"c", "cc", "cpp", "cxx", "cyc", "m"}));
+
+      decorateSourceMap = new HashMap<String, Object>();
+      decorateSourceMap.put("keywords", "null,true,false");
+      registerLangHandler(sourceDecorator(decorateSourceMap), Arrays.asList(new String[]{"json"}));
+
+      decorateSourceMap = new HashMap<String, Object>();
+      decorateSourceMap.put("keywords", CSHARP_KEYWORDS);
+      decorateSourceMap.put("hashComments", true);
+      decorateSourceMap.put("cStyleComments", true);
+      decorateSourceMap.put("verbatimStrings", true);
+      decorateSourceMap.put("types", C_TYPES);
+      registerLangHandler(sourceDecorator(decorateSourceMap), Arrays.asList(new String[]{"cs"}));
+
+      decorateSourceMap = new HashMap<String, Object>();
+      decorateSourceMap.put("keywords", JAVA_KEYWORDS);
+      decorateSourceMap.put("cStyleComments", true);
+      registerLangHandler(sourceDecorator(decorateSourceMap), Arrays.asList(new String[]{"java"}));
+
+      decorateSourceMap = new HashMap<String, Object>();
+      decorateSourceMap.put("keywords", SH_KEYWORDS);
+      decorateSourceMap.put("hashComments", true);
+      decorateSourceMap.put("multiLineStrings", true);
+      registerLangHandler(sourceDecorator(decorateSourceMap), Arrays.asList(new String[]{"bsh", "csh", "sh"}));
+
+      decorateSourceMap = new HashMap<String, Object>();
+      decorateSourceMap.put("keywords", PYTHON_KEYWORDS);
+      decorateSourceMap.put("hashComments", true);
+      decorateSourceMap.put("multiLineStrings", true);
+      decorateSourceMap.put("tripleQuotedStrings", true);
+      registerLangHandler(sourceDecorator(decorateSourceMap), Arrays.asList(new String[]{"cv", "py"}));
+
+      decorateSourceMap = new HashMap<String, Object>();
+      decorateSourceMap.put("keywords", PERL_KEYWORDS);
+      decorateSourceMap.put("hashComments", true);
+      decorateSourceMap.put("multiLineStrings", true);
+      decorateSourceMap.put("regexLiterals", true);
+      registerLangHandler(sourceDecorator(decorateSourceMap), Arrays.asList(new String[]{"perl", "pl", "pm"}));
+
+      decorateSourceMap = new HashMap<String, Object>();
+      decorateSourceMap.put("keywords", RUBY_KEYWORDS);
+      decorateSourceMap.put("hashComments", true);
+      decorateSourceMap.put("multiLineStrings", true);
+      decorateSourceMap.put("regexLiterals", true);
+      registerLangHandler(sourceDecorator(decorateSourceMap), Arrays.asList(new String[]{"rb"}));
+
+      decorateSourceMap = new HashMap<String, Object>();
+      decorateSourceMap.put("keywords", JSCRIPT_KEYWORDS);
+      decorateSourceMap.put("cStyleComments", true);
+      decorateSourceMap.put("regexLiterals", true);
+      registerLangHandler(sourceDecorator(decorateSourceMap), Arrays.asList(new String[]{"js"}));
+
+      decorateSourceMap = new HashMap<String, Object>();
+      decorateSourceMap.put("keywords", COFFEE_KEYWORDS);
+      decorateSourceMap.put("hashComments", 3); // ### style block comments
+      decorateSourceMap.put("cStyleComments", true);
+      decorateSourceMap.put("multilineStrings", true);
+      decorateSourceMap.put("tripleQuotedStrings", true);
+      decorateSourceMap.put("regexLiterals", true);
+      registerLangHandler(sourceDecorator(decorateSourceMap), Arrays.asList(new String[]{"coffee"}));
+
+      shortcutStylePatterns = new ArrayList<List<Object>>();
+      fallthroughStylePatterns = new ArrayList<List<Object>>();
+      fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_STRING, Pattern.compile("^[\\s\\S]+")}));
+      registerLangHandler(new CreateSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns), Arrays.asList(new String[]{"regex"}));
+
+      /**
+       * Registers a language handler for Protocol Buffers as described at
+       * http://code.google.com/p/protobuf/.
+       *
+       * Based on the lexical grammar at
+       * http://research.microsoft.com/fsharp/manual/spec2.aspx#_Toc202383715
+       *
+       * @author mikesamuel@gmail.com
+       */
+      decorateSourceMap = new HashMap<String, Object>();
+      decorateSourceMap.put("keywords", "bytes,default,double,enum,extend,extensions,false,"
+              + "group,import,max,message,option,"
+              + "optional,package,repeated,required,returns,rpc,service,"
+              + "syntax,to,true");
+      decorateSourceMap.put("types", Pattern.compile("^(bool|(double|s?fixed|[su]?int)(32|64)|float|string)\\b"));
+      decorateSourceMap.put("cStyleComments", true);
+      registerLangHandler(sourceDecorator(decorateSourceMap), Arrays.asList(new String[]{"proto"}));
+
+      register(LangAppollo.class);
+      register(LangClj.class);
+      register(LangCss.class);
+      register(LangGo.class);
+      register(LangHs.class);
+      register(LangLisp.class);
+      register(LangLua.class);
+      register(LangMl.class);
+      register(LangN.class);
+      register(LangScala.class);
+      register(LangSql.class);
+      register(LangTex.class);
+      register(LangVb.class);
+      register(LangVhdl.class);
+      register(LangWiki.class);
+      register(LangXq.class);
+      register(LangYaml.class);
+    } catch (Exception ex) {
+      LOG.log(Level.SEVERE, null, ex);
+    }
+  }
+
+  /**
+   * Apply the given language handler to sourceCode and add the resulting
+   * decorations to out.
+   * @param basePos the index of sourceCode within the chunk of source
+   *    whose decorations are already present on out.
+   */
+  protected static void appendDecorations(int basePos, String sourceCode, CreateSimpleLexer langHandler, List<Object> out) {
+    if (sourceCode == null) {
+      throw new NullPointerException("argument 'sourceCode' cannot be null");
+    }
+    Job job = new Job();
+    job.setSourceCode(sourceCode);
+    job.setBasePos(basePos);
+    langHandler.decorate(job);
+    out.addAll(job.getDecorations());
+  }
+
+  public class CreateSimpleLexer {
+
+    protected List<List<Object>> fallthroughStylePatterns;
+    protected Map<Character, List<Object>> shortcuts = new HashMap<Character, List<Object>>();
+    protected Pattern tokenizer;
+    protected int nPatterns;
+
+    /** Given triples of [style, pattern, context] returns a lexing function,
+     * The lexing function interprets the patterns to find token boundaries and
+     * returns a decoration list of the form
+     * [index_0, style_0, index_1, style_1, ..., index_n, style_n]
+     * where index_n is an index into the sourceCode, and style_n is a style
+     * constant like PR_PLAIN.  index_n-1 <= index_n, and style_n-1 applies to
+     * all characters in sourceCode[index_n-1:index_n].
+     *
+     * The stylePatterns is a list whose elements have the form
+     * [style : string, pattern : RegExp, DEPRECATED, shortcut : string].
+     *
+     * Style is a style constant like PR_PLAIN, or can be a string of the
+     * form 'lang-FOO', where FOO is a language extension describing the
+     * language of the portion of the token in $1 after pattern executes.
+     * E.g., if style is 'lang-lisp', and group 1 contains the text
+     * '(hello (world))', then that portion of the token will be passed to the
+     * registered lisp handler for formatting.
+     * The text before and after group 1 will be restyled using this decorator
+     * so decorators should take care that this doesn't result in infinite
+     * recursion.  For example, the HTML lexer rule for SCRIPT elements looks
+     * something like ['lang-js', /<[s]cript>(.+?)<\/script>/].  This may match
+     * '<script>foo()<\/script>', which would cause the current decorator to
+     * be called with '<script>' which would not match the same rule since
+     * group 1 must not be empty, so it would be instead styled as PR_TAG by
+     * the generic tag rule.  The handler registered for the 'js' extension would
+     * then be called with 'foo()', and finally, the current decorator would
+     * be called with '<\/script>' which would not match the original rule and
+     * so the generic tag rule would identify it as a tag.
+     *
+     * Pattern must only match prefixes, and if it matches a prefix, then that
+     * match is considered a token with the same style.
+     *
+     * Context is applied to the last non-whitespace, non-comment token
+     * recognized.
+     *
+     * Shortcut is an optional string of characters, any of which, if the first
+     * character, gurantee that this pattern and only this pattern matches.
+     *
+     * @param shortcutStylePatterns patterns that always start with
+     *   a known character.  Must have a shortcut string.
+     * @param fallthroughStylePatterns patterns that will be tried in
+     *   order if the shortcut ones fail.  May have shortcuts.
+     */
+    protected CreateSimpleLexer(List<List<Object>> shortcutStylePatterns, List<List<Object>> fallthroughStylePatterns) throws Exception {
+      this.fallthroughStylePatterns = fallthroughStylePatterns;
+
+      List<List<Object>> allPatterns = new ArrayList<List<Object>>(shortcutStylePatterns);
+      allPatterns.addAll(fallthroughStylePatterns);
+      List<Pattern> allRegexs = new ArrayList<Pattern>();
+      Map<String, Object> regexKeys = new HashMap<String, Object>();
+      for (int i = 0, n = allPatterns.size(); i < n; ++i) {
+        List<Object> patternParts = allPatterns.get(i);
+        String shortcutChars = patternParts.size() > 3 ? (String) patternParts.get(3) : null;
+        if (shortcutChars != null) {
+          for (int c = shortcutChars.length(); --c >= 0;) {
+            shortcuts.put(shortcutChars.charAt(c), patternParts);
+          }
+        }
+        Pattern regex = (Pattern) patternParts.get(1);
+        String k = regex.pattern();
+        if (regexKeys.get(k) == null) {
+          allRegexs.add(regex);
+          regexKeys.put(k, new Object());
+        }
+      }
+      allRegexs.add(Pattern.compile("[\0-\\uffff]"));
+      tokenizer = new CombinePrefixPattern().combinePrefixPattern(allRegexs);
+
+      nPatterns = fallthroughStylePatterns.size();
+    }
+
+    /**
+     * Lexes job.sourceCode and produces an output array job.decorations of
+     * style classes preceded by the position at which they start in
+     * job.sourceCode in order.
+     *
+     * @param job an object like <pre>{
+     *    sourceCode: {string} sourceText plain text,
+     *    basePos: {int} position of job.sourceCode in the larger chunk of
+     *        sourceCode.
+     * }</pre>
+     */
+    public void decorate(Job job) {
+      String sourceCode = job.getSourceCode();
+      int basePos = job.getBasePos();
+      /** Even entries are positions in source in ascending order.  Odd enties
+       * are style markers (e.g., PR_COMMENT) that run from that position until
+       * the end.
+       * @type {Array.<number|string>}
+       */
+      List<Object> decorations = new ArrayList<Object>(Arrays.asList(new Object[]{basePos, PR_PLAIN}));
+      int pos = 0;  // index into sourceCode
+      String[] tokens = Util.match(tokenizer, sourceCode, true);
+      Map<String, String> styleCache = new HashMap<String, String>();
+
+      for (int ti = 0, nTokens = tokens.length; ti < nTokens; ++ti) {
+        String token = tokens[ti];
+        String style = styleCache.get(token);
+        String[] match = null;
+
+        boolean isEmbedded;
+        if (style != null) {
+          isEmbedded = false;
+        } else {
+          List<Object> patternParts = shortcuts.get(token.charAt(0));
+          if (patternParts != null) {
+            match = Util.match((Pattern) patternParts.get(1), token, false);
+            style = (String) patternParts.get(0);
+          } else {
+            for (int i = 0; i < nPatterns; ++i) {
+              patternParts = fallthroughStylePatterns.get(i);
+              match = Util.match((Pattern) patternParts.get(1), token, false);
+              if (match.length != 0) {
+                style = (String) patternParts.get(0);
+                break;
+              }
+            }
+
+            if (match.length == 0) {  // make sure that we make progress
+              style = PR_PLAIN;
+            }
+          }
+
+          isEmbedded = style != null && style.length() >= 5 && style.startsWith("lang-");
+          if (isEmbedded && !(match.length > 1 && match[1] != null)) {
+            isEmbedded = false;
+            style = PR_SOURCE;
+          }
+
+          if (!isEmbedded) {
+            styleCache.put(token, style);
+          }
+        }
+
+        int tokenStart = pos;
+        pos += token.length();
+
+        if (!isEmbedded) {
+          decorations.add(basePos + tokenStart);
+          decorations.add(style);
+        } else {  // Treat group 1 as an embedded block of source code.
+          String embeddedSource = match[1];
+          int embeddedSourceStart = token.indexOf(embeddedSource);
+          int embeddedSourceEnd = embeddedSourceStart + embeddedSource.length();
+          if (match.length > 2 && match[2] != null) {
+            // If embeddedSource can be blank, then it would match at the
+            // beginning which would cause us to infinitely recurse on the
+            // entire token, so we catch the right context in match[2].
+            embeddedSourceEnd = token.length() - match[2].length();
+            embeddedSourceStart = embeddedSourceEnd - embeddedSource.length();
+          }
+          String lang = style.substring(5);
+          // Decorate the left of the embedded source
+          appendDecorations(basePos + tokenStart,
+                  token.substring(0, embeddedSourceStart),
+                  this, decorations);
+          // Decorate the embedded source
+          appendDecorations(basePos + tokenStart + embeddedSourceStart,
+                  embeddedSource,
+                  langHandlerForExtension(lang, embeddedSource),
+                  decorations);
+          // Decorate the right of the embedded section
+          appendDecorations(basePos + tokenStart + embeddedSourceEnd,
+                  token.substring(embeddedSourceEnd),
+                  this, decorations);
+        }
+      }
+
+      job.setDecorations(Util.removeDuplicates(decorations, job.getSourceCode()));
+    }
+  }
+
+  /** returns a function that produces a list of decorations from source text.
+   *
+   * This code treats ", ', and ` as string delimiters, and \ as a string
+   * escape.  It does not recognize perl's qq() style strings.
+   * It has no special handling for double delimiter escapes as in basic, or
+   * the tripled delimiters used in python, but should work on those regardless
+   * although in those cases a single string literal may be broken up into
+   * multiple adjacent string literals.
+   *
+   * It recognizes C, C++, and shell style comments.
+   *
+   * @param options a set of optional parameters.
+   * @return a function that examines the source code
+   *     in the input job and builds the decoration list.
+   */
+  protected CreateSimpleLexer sourceDecorator(Map<String, Object> options) throws Exception {
+    List<List<Object>> shortcutStylePatterns = new ArrayList<List<Object>>();
+    List<List<Object>> fallthroughStylePatterns = new ArrayList<List<Object>>();
+    if (options.get("tripleQuotedStrings") != null) {
+      // '''multi-line-string''', 'single-line-string', and double-quoted
+      shortcutStylePatterns.add(Arrays.asList(new Object[]{PR_STRING,
+                Pattern.compile("^(?:\\'\\'\\'(?:[^\\'\\\\]|\\\\[\\s\\S]|\\'{1,2}(?=[^\\']))*(?:\\'\\'\\'|$)|\\\"\\\"\\\"(?:[^\\\"\\\\]|\\\\[\\s\\S]|\\\"{1,2}(?=[^\\\"]))*(?:\\\"\\\"\\\"|$)|\\'(?:[^\\\\\\']|\\\\[\\s\\S])*(?:\\'|$)|\\\"(?:[^\\\\\\\"]|\\\\[\\s\\S])*(?:\\\"|$))"),
+                null,
+                "'\""}));
+    } else if (options.get("multiLineStrings") != null) {
+      // 'multi-line-string', "multi-line-string"
+      shortcutStylePatterns.add(Arrays.asList(new Object[]{PR_STRING,
+                Pattern.compile("^(?:\\'(?:[^\\\\\\']|\\\\[\\s\\S])*(?:\\'|$)|\\\"(?:[^\\\\\\\"]|\\\\[\\s\\S])*(?:\\\"|$)|\\`(?:[^\\\\\\`]|\\\\[\\s\\S])*(?:\\`|$))"),
+                null,
+                "'\"`"}));
+    } else {
+      // 'single-line-string', "single-line-string"
+      shortcutStylePatterns.add(Arrays.asList(new Object[]{PR_STRING,
+                Pattern.compile("^(?:\\'(?:[^\\\\\\'\r\n]|\\\\.)*(?:\\'|$)|\\\"(?:[^\\\\\\\"\r\n]|\\\\.)*(?:\\\"|$))"),
+                null,
+                "\"'"}));
+    }
+    if (options.get("verbatimStrings") != null) {
+      // verbatim-string-literal production from the C# grammar.  See issue 93.
+      fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_STRING,
+                Pattern.compile("^@\\\"(?:[^\\\"]|\\\"\\\")*(?:\\\"|$)"),
+                null}));
+    }
+    Object hc = options.get("hashComments");
+    if (hc != null) {
+      if (options.get("cStyleComments") != null) {
+        if ((hc instanceof Integer) && (Integer) hc > 1) {  // multiline hash comments
+          shortcutStylePatterns.add(Arrays.asList(new Object[]{PR_COMMENT,
+                    Pattern.compile("^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)"),
+                    null,
+                    "#"}));
+        } else {
+          // Stop C preprocessor declarations at an unclosed open comment
+          shortcutStylePatterns.add(Arrays.asList(new Object[]{PR_COMMENT,
+                    Pattern.compile("^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\\b|[^\r\n]*)"),
+                    null,
+                    "#"}));
+        }
+        fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_STRING,
+                  Pattern.compile("^<(?:(?:(?:\\.\\.\\/)*|\\/?)(?:[\\w-]+(?:\\/[\\w-]+)+)?[\\w-]+\\.h|[a-z]\\w*)>"),
+                  null}));
+      } else {
+        shortcutStylePatterns.add(Arrays.asList(new Object[]{PR_COMMENT,
+                  Pattern.compile("^#[^\r\n]*"),
+                  null,
+                  "#"}));
+      }
+    }
+    if (options.get("cStyleComments") != null) {
+      fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_COMMENT,
+                Pattern.compile("^\\/\\/[^\r\n]*"),
+                null}));
+
+      fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_COMMENT,
+                Pattern.compile("^\\/\\*[\\s\\S]*?(?:\\*\\/|$)"),
+                null}));
+    }
+    if (options.get("regexLiterals") != null) {
+      /**
+       * @const
+       */
+      String REGEX_LITERAL =
+              // A regular expression literal starts with a slash that is
+              // not followed by * or / so that it is not confused with
+              // comments.
+              "/(?=[^/*])"
+              // and then contains any number of raw characters,
+              + "(?:[^/\\x5B\\x5C]"
+              // escape sequences (\x5C),
+              + "|\\x5C[\\s\\S]"
+              // or non-nesting character sets (\x5B\x5D);
+              + "|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+"
+              // finally closed by a /.
+              + "/";
+      fallthroughStylePatterns.add(Arrays.asList(new Object[]{"lang-regex",
+                Pattern.compile("^" + REGEXP_PRECEDER_PATTERN + "(" + REGEX_LITERAL + ")")}));
+    }
+
+    Pattern types = (Pattern) options.get("types");
+    if (types != null) {
+      fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_TYPE, types}));
+    }
+
+    if (options.get("keywords") != null) {
+      String keywords = ((String) options.get("keywords")).replaceAll("^ | $", "");
+      if (keywords.length() != 0) {
+        fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_KEYWORD,
+                  Pattern.compile("^(?:" + keywords.replaceAll("[\\s,]+", "|") + ")\\b"),
+                  null}));
+      }
+    }
+
+    shortcutStylePatterns.add(Arrays.asList(new Object[]{PR_PLAIN,
+              Pattern.compile("^\\s+"),
+              null,
+              " \r\n\t" + Character.toString((char) 0xA0)
+            }));
+
+    // TODO(mikesamuel): recognize non-latin letters and numerals in idents
+    fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_LITERAL,
+              Pattern.compile("^@[a-z_$][a-z_$@0-9]*", Pattern.CASE_INSENSITIVE),
+              null}));
+    fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_TYPE,
+              Pattern.compile("^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\\w+_t\\b)"),
+              null}));
+    fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_PLAIN,
+              Pattern.compile("^[a-z_$][a-z_$@0-9]*", Pattern.CASE_INSENSITIVE),
+              null}));
+    fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_LITERAL,
+              Pattern.compile("^(?:"
+              // A hex number
+              + "0x[a-f0-9]+"
+              // or an octal or decimal number,
+              + "|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)"
+              // possibly in scientific notation
+              + "(?:e[+\\-]?\\d+)?"
+              + ')'
+              // with an optional modifier like UL for unsigned long
+              + "[a-z]*", Pattern.CASE_INSENSITIVE),
+              null,
+              "0123456789"}));
+    // Don't treat escaped quotes in bash as starting strings.  See issue 144.
+    fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_PLAIN,
+              Pattern.compile("^\\\\[\\s\\S]?"),
+              null}));
+    fallthroughStylePatterns.add(Arrays.asList(new Object[]{PR_PUNCTUATION,
+              Pattern.compile("^.[^\\s\\w\\.$@\\'\\\"\\`\\/\\#\\\\]*"),
+              null}));
+
+    return new CreateSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns);
+  }
+  /** Maps language-specific file extensions to handlers. */
+  protected Map<String, Object> langHandlerRegistry = new HashMap<String, Object>();
+
+  /** Register a language handler for the given file extensions.
+   * @param handler a function from source code to a list
+   *      of decorations.  Takes a single argument job which describes the
+   *      state of the computation.   The single parameter has the form
+   *      {@code {
+   *        sourceCode: {string} as plain text.
+   *        decorations: {Array.<number|string>} an array of style classes
+   *                     preceded by the position at which they start in
+   *                     job.sourceCode in order.
+   *                     The language handler should assigned this field.
+   *        basePos: {int} the position of source in the larger source chunk.
+   *                 All positions in the output decorations array are relative
+   *                 to the larger source chunk.
+   *      } }
+   * @param fileExtensions
+   */
+  protected void registerLangHandler(CreateSimpleLexer handler, List<String> fileExtensions) throws Exception {
+    for (int i = fileExtensions.size(); --i >= 0;) {
+      String ext = fileExtensions.get(i);
+      if (langHandlerRegistry.get(ext) == null) {
+        langHandlerRegistry.put(ext, handler);
+      } else {
+        throw new Exception("cannot override language handler " + ext);
+      }
+    }
+  }
+
+  /**
+   * Register language handler. The clazz will not be instantiated
+   * @param clazz the class of the language
+   * @throws Exception cannot instantiate the object using the class,
+   * or language handler with specified extension exist already
+   */
+  public void register(Class<? extends Lang> clazz) throws Exception {
+    if (clazz == null) {
+      throw new NullPointerException("argument 'clazz' cannot be null");
+    }
+    List<String> fileExtensions = getFileExtensionsFromClass(clazz);
+    for (int i = fileExtensions.size(); --i >= 0;) {
+      String ext = fileExtensions.get(i);
+      if (langHandlerRegistry.get(ext) == null) {
+        langHandlerRegistry.put(ext, clazz);
+      } else {
+        throw new Exception("cannot override language handler " + ext);
+      }
+    }
+  }
+
+  protected List<String> getFileExtensionsFromClass(Class<? extends Lang> clazz) throws Exception {
+    Method getExtensionsMethod = clazz.getMethod("getFileExtensions", (Class<?>[]) null);
+    return (List<String>) getExtensionsMethod.invoke(null, null);
+  }
+
+  /**
+   * Get the parser for the extension specified. 
+   * @param extension the file extension, if null, default parser will be returned
+   * @param source the source code
+   * @return the parser
+   */
+  public CreateSimpleLexer langHandlerForExtension(String extension, String source) {
+    if (!(extension != null && langHandlerRegistry.get(extension) != null)) {
+      // Treat it as markup if the first non whitespace character is a < and
+      // the last non-whitespace character is a >.
+      extension = Util.test(Pattern.compile("^\\s*<"), source)
+              ? "default-markup"
+              : "default-code";
+    }
+
+    Object handler = langHandlerRegistry.get(extension);
+    if (handler instanceof CreateSimpleLexer) {
+      return (CreateSimpleLexer) handler;
+    } else {
+      CreateSimpleLexer _simpleLexer;
+      try {
+        Lang _lang = ((Class<Lang>) handler).newInstance();
+        _simpleLexer = new CreateSimpleLexer(_lang.getShortcutStylePatterns(), _lang.getFallthroughStylePatterns());
+
+        List<Lang> extendedLangs = _lang.getExtendedLangs();
+        for (Lang _extendedLang : extendedLangs) {
+          register(_extendedLang.getClass());
+        }
+
+        List<String> fileExtensions = getFileExtensionsFromClass((Class<Lang>) handler);
+        for (String _extension : fileExtensions) {
+          langHandlerRegistry.put(_extension, _simpleLexer);
+        }
+      } catch (Exception ex) {
+        LOG.log(Level.SEVERE, null, ex);
+        return null;
+      }
+
+      return _simpleLexer;
+    }
+  }
+}
diff --git a/src/prettify/parser/package-info.java b/src/prettify/parser/package-info.java
new file mode 100644
index 0000000..32d7970
--- /dev/null
+++ b/src/prettify/parser/package-info.java
@@ -0,0 +1,5 @@
+/**

+ * The Prettify parser. Classes in this package are all port from JavaScript 

+ * Prettify.

+ */

+package prettify.parser;
\ No newline at end of file
diff --git a/src/prettify/theme/Style.java b/src/prettify/theme/Style.java
new file mode 100644
index 0000000..a29f3ab
--- /dev/null
+++ b/src/prettify/theme/Style.java
@@ -0,0 +1,216 @@
+// Copyright (C) 2011 Chan Wai Shing
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package prettify.theme;
+
+import java.awt.Color;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.StyleConstants;
+
+/**
+ * The style used by {@link prettify.theme} as those of CSS styles.
+ * 
+ * @author Chan Wai Shing <cws1989@gmail.com>
+ */
+public class Style {
+
+  protected boolean bold;
+  protected Color color;
+  /**
+   * The background color, null means no background color is set.
+   */
+  protected Color background;
+  protected boolean underline;
+  protected boolean italic;
+
+  /**
+   * Constructor.
+   * <p>
+   * <b>Default values:</b><br />
+   * <ul>
+   * <li>bold: false;</li>
+   * <li>color: black;</li>
+   * <li>background: null;</li>
+   * <li>underline: false;</li>
+   * <li>italic: false;</li>
+   * </ul>
+   * </p>
+   */
+  public Style() {
+    bold = false;
+    color = Color.black;
+    background = null;
+    underline = false;
+    italic = false;
+  }
+
+  /**
+   * Apply the style to the AttributeSet.
+   * Note that the AttributeSet should only be set by this function once or some unexpected style may appear.
+   * @param attributeSet the AttributeSet to set the style on
+   */
+  public void setAttributeSet(SimpleAttributeSet attributeSet) {
+    if (attributeSet == null) {
+      return;
+    }
+    StyleConstants.setBold(attributeSet, bold);
+    StyleConstants.setForeground(attributeSet, color);
+    if (background != null) {
+      StyleConstants.setBackground(attributeSet, background);
+    } else {
+      attributeSet.removeAttribute(StyleConstants.Background);
+    }
+    StyleConstants.setUnderline(attributeSet, underline);
+    StyleConstants.setItalic(attributeSet, italic);
+  }
+
+  /**
+   * Get the AttributeSet from this style.
+   * @return the AttributeSet
+   */
+  public SimpleAttributeSet getAttributeSet() {
+    SimpleAttributeSet attributeSet = new SimpleAttributeSet();
+    StyleConstants.setBold(attributeSet, bold);
+    StyleConstants.setForeground(attributeSet, color);
+    if (background != null) {
+      StyleConstants.setBackground(attributeSet, background);
+    }
+    StyleConstants.setUnderline(attributeSet, underline);
+    StyleConstants.setItalic(attributeSet, italic);
+    return attributeSet;
+  }
+
+  /**
+   * Get the background color.
+   * @return the background color or null if no color is set
+   */
+  public Color getBackground() {
+    return background;
+  }
+
+  /**
+   * Set the background color.
+   * @param background input null means do not set the background
+   */
+  public void setBackground(Color background) {
+    if (background == null) {
+      throw new NullPointerException("argument 'background' cannot be null");
+    }
+    this.background = background;
+  }
+
+  public boolean isBold() {
+    return bold;
+  }
+
+  public void setBold(boolean bold) {
+    this.bold = bold;
+  }
+
+  public Color getColor() {
+    return color;
+  }
+
+  public void setColor(Color color) {
+    if (color == null) {
+      throw new NullPointerException("argument 'color' cannot be null");
+    }
+    this.color = color;
+  }
+
+  public boolean isItalic() {
+    return italic;
+  }
+
+  public void setItalic(boolean italic) {
+    this.italic = italic;
+  }
+
+  public boolean isUnderline() {
+    return underline;
+  }
+
+  public void setUnderline(boolean underline) {
+    this.underline = underline;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int hashCode() {
+    int hash = 5;
+    hash = 59 * hash + (this.bold ? 1 : 0);
+    hash = 59 * hash + (this.color != null ? this.color.hashCode() : 0);
+    hash = 59 * hash + (this.background != null ? this.background.hashCode() : 0);
+    hash = 59 * hash + (this.underline ? 1 : 0);
+    hash = 59 * hash + (this.italic ? 1 : 0);
+    return hash;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == null || !(obj instanceof Style)) {
+      return false;
+    }
+    if (obj == this) {
+      return true;
+    }
+    Style _object = (Style) obj;
+    return _object.bold == bold && _object.color.equals(color) && _object.background.equals(background)
+            && _object.underline == underline && _object.italic == italic;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Style clone() {
+    Style object = null;
+    try {
+      object = (Style) super.clone();
+      object.bold = bold;
+      object.color = color;
+      object.background = background;
+      object.underline = underline;
+      object.italic = italic;
+    } catch (CloneNotSupportedException ex) {
+    }
+    return object;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+
+    sb.append(getClass().getName());
+    sb.append(": ");
+    sb.append("bold: ").append(bold);
+    sb.append(", ");
+    sb.append("color: ").append(color);
+    sb.append(", ");
+    sb.append("bg: ").append(background);
+    sb.append(", ");
+    sb.append("underline: ").append(underline);
+    sb.append(", ");
+    sb.append("italic: ").append(italic);
+
+    return sb.toString();
+  }
+}
\ No newline at end of file
diff --git a/src/prettify/theme/Theme.java b/src/prettify/theme/Theme.java
new file mode 100644
index 0000000..8de460d
--- /dev/null
+++ b/src/prettify/theme/Theme.java
@@ -0,0 +1,676 @@
+// Copyright (C) 2011 Chan Wai Shing
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package prettify.theme;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import prettify.gui.JTextComponentRowHeader;
+
+/**
+ * Theme for the Java Prettify.
+ * <p>To make a new theme, either extending this class of initiate this class 
+ * and set the parameter by the setter. For the default value, please refer to 
+ * the constructor.</p>
+ * @author Chan Wai Shing <cws1989@gmail.com>
+ */
+public class Theme {
+
+  /**
+   * Indicate whether it is in debug mode or not.
+   */
+  protected final static boolean debug;
+
+  static {
+    String debugMode = System.getProperty("PrettifyDebugMode");
+    debug = debugMode == null || !debugMode.equals("true") ? false : true;
+  }
+  /**
+   * The font of the script text.
+   */
+  protected Font font;
+  /**
+   * The background color of the script text area.
+   */
+  protected Color background;
+  /**
+   * The background color of the highlighted line of script text.
+   */
+  protected Color highlightedBackground;
+  /**
+   * Gutter (line number column on the left)
+   */
+  /**
+   * The color of the gutter text.
+   */
+  protected Color gutterText;
+  /**
+   * The color of the border that joint the gutter and the script text panel.
+   */
+  protected Color gutterBorderColor;
+  /**
+   * The width of the border that joint the gutter and the script text panel.
+   */
+  protected int gutterBorderWidth;
+  /**
+   * The font of the gutter text.
+   */
+  protected Font gutterTextFont;
+  /**
+   * The minimum padding from 'the leftmost of the line number text' to 
+   * 'the left margin'.
+   */
+  protected int gutterTextPaddingLeft;
+  /**
+   * The minimum padding from 'the rightmost of the line number text' to 
+   * 'the right margin' (not to the gutter border).
+   */
+  protected int gutterTextPaddingRight;
+  protected Style string;
+  protected Style keyword;
+  protected Style comment;
+  protected Style type;
+  protected Style literal;
+  protected Style punctuation;
+  protected Style plain;
+  protected Style tag;
+  protected Style declaration;
+  protected Style source;
+  protected Style attributeName;
+  protected Style attributeValue;
+  protected Style noCode;
+  protected Style openBracket;
+  protected Style closeBracket;
+  protected Style variable;
+  protected Style function;
+
+  /**
+   * Constructor.<br />
+   * <p>
+   * <b>Default value:</b><br />
+   * <ul>
+   * <li>font: Consolas 12pt</li>
+   * <li>background: white</li>
+   * <li>gutter text: black</li>
+   * <li>gutter border: R: 184, G: 184, B: 184</li>
+   * <li>gutter border width: 3px</li>
+   * <li>gutter text font: Consolas 12pt</li>
+   * <li>gutter text padding-left: 7px</li>
+   * <li>gutter text padding-right: 7px</li>
+   * <li>plain, comments, string, keyword, preprocessor, variable, value, 
+   * functions, constants, script, scriptBackground, color1, color2, color3: 
+   * no style set</li>
+   * </ul>
+   * </p>
+   */
+  public Theme() {
+    font = new Font("Consolas", Font.PLAIN, 12);
+    background = Color.white;
+
+    highlightedBackground = Color.gray;
+
+    gutterText = Color.black;
+    gutterBorderColor = new Color(184, 184, 184);
+    gutterBorderWidth = 3;
+    gutterTextFont = new Font("Consolas", Font.PLAIN, 12);
+    gutterTextPaddingLeft = 7;
+    gutterTextPaddingRight = 7;
+
+    string = new Style();
+    keyword = new Style();
+    comment = new Style();
+    type = new Style();
+    literal = new Style();
+    punctuation = new Style();
+    plain = new Style();
+    tag = new Style();
+    declaration = new Style();
+    source = new Style();
+    attributeName = new Style();
+    attributeValue = new Style();
+    noCode = new Style();
+    openBracket = new Style();
+    closeBracket = new Style();
+    variable = new Style();
+    function = new Style();
+  }
+
+  /**
+   * Apply the theme to the row header panel.
+   * @param rowHeader the row header to apply theme on
+   */
+  public void setTheme(JTextComponentRowHeader rowHeader) {
+    rowHeader.setBackground(background);
+    rowHeader.setHighlightedColor(background);
+
+    rowHeader.setForeground(gutterText);
+    rowHeader.setBorderColor(gutterBorderColor);
+    rowHeader.setBorderWidth(gutterBorderWidth);
+    rowHeader.setFont(gutterTextFont);
+    rowHeader.setPaddingLeft(gutterTextPaddingLeft);
+    rowHeader.setPaddingRight(gutterTextPaddingRight);
+  }
+
+  /**
+   * Get the {@link prettify.Theme.Style} by keyword.
+   * @param key the keyword, possible values: plain, comments, string, keyword, 
+   * preprocessor, variable, value, functions, constants, script, 
+   * scriptBackground, color1, color2 or color3
+   * @return the {@link prettify.Theme.Style} related to the {@code key}; if 
+   * the style related to the <code>key</code> not exist, the style of 'plain'
+   * will return.
+   */
+  public Style getStyle(String key) {
+    if (key == null) {
+      return plain;
+    }
+    if (key.equals("str")) {
+      return string;
+    } else if (key.equals("kwd")) {
+      return keyword;
+    } else if (key.equals("com")) {
+      return comment;
+    } else if (key.equals("typ")) {
+      return type;
+    } else if (key.equals("lit")) {
+      return literal;
+    } else if (key.equals("pun")) {
+      return punctuation;
+    } else if (key.equals("pln")) {
+      return plain;
+    } else if (key.equals("tag")) {
+      return tag;
+    } else if (key.equals("dec")) {
+      return declaration;
+    } else if (key.equals("src")) {
+      return source;
+    } else if (key.equals("atn")) {
+      return attributeName;
+    } else if (key.equals("atv")) {
+      return attributeValue;
+    } else if (key.equals("nocode")) {
+      return noCode;
+    } else if (key.equals("opn")) {
+      return openBracket;
+    } else if (key.equals("clo")) {
+      return closeBracket;
+    } else if (key.equals("var")) {
+      return variable;
+    } else if (key.equals("fun")) {
+      return function;
+    } else {
+      // key not exist
+      return plain;
+    }
+  }
+
+  /**
+   * The font of the script text.
+   * @return the font
+   */
+  public Font getFont() {
+    return font;
+  }
+
+  /**
+   * The font of the script text.
+   * @param font the font
+   */
+  public void setFont(Font font) {
+    if (font == null) {
+      throw new NullPointerException("argument 'font' cannot be null");
+    }
+    this.font = font;
+  }
+
+  /**
+   * The background color of the script text area.
+   * @return the color
+   */
+  public Color getBackground() {
+    return background;
+  }
+
+  /**
+   * The background color of the script text area.
+   * @param background the color
+   */
+  public void setBackground(Color background) {
+    if (background == null) {
+      throw new NullPointerException("argument 'background' cannot be null");
+    }
+    this.background = background;
+  }
+
+  /**
+   * The background color of the highlighted line of script text.
+   * @return the color
+   */
+  public Color getHighlightedBackground() {
+    return highlightedBackground;
+  }
+
+  /**
+   * The background color of the highlighted line of script text.
+   * @param highlightedBackground the color
+   */
+  public void setHighlightedBackground(Color highlightedBackground) {
+    if (highlightedBackground == null) {
+      throw new NullPointerException("argument 'highlightedBackground' cannot be null");
+    }
+    this.highlightedBackground = highlightedBackground;
+  }
+
+  /**
+   * The color of the gutter text.
+   * @return the color
+   */
+  public Color getGutterText() {
+    return gutterText;
+  }
+
+  /**
+   * The color of the gutter text.
+   * @param gutterText the color
+   */
+  public void setGutterText(Color gutterText) {
+    if (gutterText == null) {
+      throw new NullPointerException("argument 'gutterText' cannot be null");
+    }
+    this.gutterText = gutterText;
+  }
+
+  /**
+   * The color of the border that joint the gutter and the script text panel.
+   * @return the color
+   */
+  public Color getGutterBorderColor() {
+    return gutterBorderColor;
+  }
+
+  /**
+   * The color of the border that joint the gutter and the script text panel.
+   * @param gutterBorderColor the color
+   */
+  public void setGutterBorderColor(Color gutterBorderColor) {
+    if (gutterBorderColor == null) {
+      throw new NullPointerException("argument 'gutterBorderColor' cannot be null");
+    }
+    this.gutterBorderColor = gutterBorderColor;
+  }
+
+  /**
+   * The width of the border that joint the gutter and the script text panel.
+   * @return the width in pixel
+   */
+  public int getGutterBorderWidth() {
+    return gutterBorderWidth;
+  }
+
+  /**
+   * The width of the border that joint the gutter and the script text panel.
+   * @param gutterBorderWidth in pixel
+   */
+  public void setGutterBorderWidth(int gutterBorderWidth) {
+    this.gutterBorderWidth = gutterBorderWidth;
+  }
+
+  /**
+   * The font of the gutter text.
+   * @return the font
+   */
+  public Font getGutterTextFont() {
+    return gutterTextFont;
+  }
+
+  /**
+   * The font of the gutter text.
+   * @param gutterTextFont the font
+   */
+  public void setGutterTextFont(Font gutterTextFont) {
+    if (gutterTextFont == null) {
+      throw new NullPointerException("argument 'gutterTextFont' cannot be null");
+    }
+    this.gutterTextFont = gutterTextFont;
+  }
+
+  /**
+   * The minimum padding from 'the leftmost of the line number text' to 'the left margin'.
+   * @return the padding in pixel
+   */
+  public int getGutterTextPaddingLeft() {
+    return gutterTextPaddingLeft;
+  }
+
+  /**
+   * The minimum padding from 'the leftmost of the line number text' to 
+   * 'the left margin'.
+   * @param gutterTextPaddingLeft in pixel
+   */
+  public void setGutterTextPaddingLeft(int gutterTextPaddingLeft) {
+    this.gutterTextPaddingLeft = gutterTextPaddingLeft;
+  }
+
+  /**
+   * The minimum padding from 'the rightmost of the line number text' to 
+   * 'the right margin' (not to the gutter border).
+   * @return the padding in pixel
+   */
+  public int getGutterTextPaddingRight() {
+    return gutterTextPaddingRight;
+  }
+
+  /**
+   * The minimum padding from 'the rightmost of the line number text' to 
+   * 'the right margin' (not to the gutter border).
+   * @param gutterTextPaddingRight in pixel
+   */
+  public void setGutterTextPaddingRight(int gutterTextPaddingRight) {
+    this.gutterTextPaddingRight = gutterTextPaddingRight;
+  }
+
+  public Style getString() {
+    return string;
+  }
+
+  public void setString(Style string) {
+    if (string == null) {
+      throw new NullPointerException("argument 'string' cannot be null");
+    }
+    this.string = string;
+  }
+
+  public Style getKeyword() {
+    return keyword;
+  }
+
+  public void setKeyword(Style keyword) {
+    if (keyword == null) {
+      throw new NullPointerException("argument 'keyword' cannot be null");
+    }
+    this.keyword = keyword;
+  }
+
+  public Style getComment() {
+    return comment;
+  }
+
+  public void setComment(Style comment) {
+    if (comment == null) {
+      throw new NullPointerException("argument 'comment' cannot be null");
+    }
+    this.comment = comment;
+  }
+
+  public Style getType() {
+    return type;
+  }
+
+  public void setType(Style type) {
+    if (type == null) {
+      throw new NullPointerException("argument 'type' cannot be null");
+    }
+    this.type = type;
+  }
+
+  public Style getLiteral() {
+    return literal;
+  }
+
+  public void setLiteral(Style literal) {
+    if (literal == null) {
+      throw new NullPointerException("argument 'literal' cannot be null");
+    }
+    this.literal = literal;
+  }
+
+  public Style getPunctuation() {
+    return punctuation;
+  }
+
+  public void setPunctuation(Style punctuation) {
+    if (punctuation == null) {
+      throw new NullPointerException("argument 'punctuation' cannot be null");
+    }
+    this.punctuation = punctuation;
+  }
+
+  public Style getPlain() {
+    return plain;
+  }
+
+  public void setPlain(Style plain) {
+    if (plain == null) {
+      throw new NullPointerException("argument 'plain' cannot be null");
+    }
+    this.plain = plain;
+  }
+
+  public Style getTag() {
+    return tag;
+  }
+
+  public void setTag(Style tag) {
+    if (tag == null) {
+      throw new NullPointerException("argument 'tag' cannot be null");
+    }
+    this.tag = tag;
+  }
+
+  public Style getDeclaration() {
+    return declaration;
+  }
+
+  public void setDeclaration(Style declaration) {
+    if (declaration == null) {
+      throw new NullPointerException("argument 'declaration' cannot be null");
+    }
+    this.declaration = declaration;
+  }
+
+  public Style getSource() {
+    return source;
+  }
+
+  public void setSource(Style source) {
+    if (source == null) {
+      throw new NullPointerException("argument 'source' cannot be null");
+    }
+    this.source = source;
+  }
+
+  public Style getAttributeName() {
+    return attributeName;
+  }
+
+  public void setAttributeName(Style attributeName) {
+    if (attributeName == null) {
+      throw new NullPointerException("argument 'attributeName' cannot be null");
+    }
+    this.attributeName = attributeName;
+  }
+
+  public Style getAttributeValue() {
+    return attributeValue;
+  }
+
+  public void setAttributeValue(Style attributeValue) {
+    if (attributeValue == null) {
+      throw new NullPointerException("argument 'attributeValue' cannot be null");
+    }
+    this.attributeValue = attributeValue;
+  }
+
+  public Style getNoCode() {
+    return noCode;
+  }
+
+  public void setNoCode(Style noCode) {
+    if (noCode == null) {
+      throw new NullPointerException("argument 'noCode' cannot be null");
+    }
+    this.noCode = noCode;
+  }
+
+  public Style getOpenBracket() {
+    return openBracket;
+  }
+
+  public void setOpenBracket(Style openBracket) {
+    if (openBracket == null) {
+      throw new NullPointerException("argument 'openBracket' cannot be null");
+    }
+    this.openBracket = openBracket;
+  }
+
+  public Style getCloseBracket() {
+    return closeBracket;
+  }
+
+  public void setCloseBracket(Style closeBracket) {
+    if (closeBracket == null) {
+      throw new NullPointerException("argument 'closeBracket' cannot be null");
+    }
+    this.closeBracket = closeBracket;
+  }
+
+  public Style getVariable() {
+    return variable;
+  }
+
+  public void setVariable(Style variable) {
+    if (variable == null) {
+      throw new NullPointerException("argument 'variable' cannot be null");
+    }
+    this.variable = variable;
+  }
+
+  public Style getFunction() {
+    return function;
+  }
+
+  public void setFunction(Style function) {
+    if (function == null) {
+      throw new NullPointerException("argument 'function' cannot be null");
+    }
+    this.function = function;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Theme clone() {
+    Theme object = null;
+    try {
+      object = (Theme) super.clone();
+      object.font = font;
+      object.background = background;
+      object.highlightedBackground = highlightedBackground;
+      object.gutterText = gutterText;
+      object.gutterBorderColor = gutterBorderColor;
+      object.gutterBorderWidth = gutterBorderWidth;
+      object.gutterTextFont = gutterTextFont;
+      object.gutterTextPaddingLeft = gutterTextPaddingLeft;
+      object.gutterTextPaddingRight = gutterTextPaddingRight;
+      object.string = string.clone();
+      object.keyword = keyword.clone();
+      object.comment = comment.clone();
+      object.type = type.clone();
+      object.literal = literal.clone();
+      object.punctuation = punctuation.clone();
+      object.plain = plain.clone();
+      object.tag = tag.clone();
+      object.declaration = declaration.clone();
+      object.source = source.clone();
+      object.attributeName = attributeName.clone();
+      object.attributeValue = attributeValue.clone();
+      object.noCode = noCode.clone();
+      object.openBracket = openBracket.clone();
+      object.closeBracket = closeBracket.clone();
+      object.variable = variable.clone();
+      object.function = function.clone();
+    } catch (CloneNotSupportedException ex) {
+      if (debug) {
+        Logger.getLogger(Theme.class.getName()).log(Level.WARNING, null, ex);
+      }
+    }
+    return object;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+
+    sb.append(getClass().getName());
+    sb.append(": ");
+    sb.append("font: ").append(getFont());
+    sb.append(", ");
+    sb.append("background: ").append(getBackground());
+    sb.append(", ");
+    sb.append("highlightedBackground: ").append(getHighlightedBackground());
+    sb.append(", ");
+    sb.append("gutterText: ").append(getGutterText());
+    sb.append(", ");
+    sb.append("gutterBorderColor: ").append(getGutterBorderColor());
+    sb.append(", ");
+    sb.append("gutterBorderWidth: ").append(getGutterBorderWidth());
+    sb.append(", ");
+    sb.append("gutterTextFont: ").append(getGutterTextFont());
+    sb.append(", ");
+    sb.append("gutterTextPaddingLeft: ").append(getGutterTextPaddingLeft());
+    sb.append(", ");
+    sb.append("gutterTextPaddingRight: ").append(getGutterTextPaddingRight());
+    sb.append(", ");
+    sb.append("string: ").append(getString());
+    sb.append(", ");
+    sb.append("keyword: ").append(getKeyword());
+    sb.append(", ");
+    sb.append("comment: ").append(getComment());
+    sb.append(", ");
+    sb.append("type: ").append(getType());
+    sb.append(", ");
+    sb.append("literal: ").append(getLiteral());
+    sb.append(", ");
+    sb.append("punctuation: ").append(getPunctuation());
+    sb.append(", ");
+    sb.append("plain: ").append(getPlain());
+    sb.append(", ");
+    sb.append("tag: ").append(getTag());
+    sb.append(", ");
+    sb.append("declaration: ").append(getDeclaration());
+    sb.append(", ");
+    sb.append("source: ").append(getSource());
+    sb.append(", ");
+    sb.append("attributeName: ").append(getAttributeName());
+    sb.append(", ");
+    sb.append("attributeValue: ").append(getAttributeValue());
+    sb.append(", ");
+    sb.append("noCode: ").append(getNoCode());
+    sb.append(", ");
+    sb.append("openBracket: ").append(getOpenBracket());
+    sb.append(", ");
+    sb.append("closeBracket: ").append(getCloseBracket());
+    sb.append(", ");
+    sb.append("variable: ").append(getVariable());
+    sb.append(", ");
+    sb.append("function: ").append(getFunction());
+
+    return sb.toString();
+  }
+}
diff --git a/src/prettify/theme/ThemeDefault.java b/src/prettify/theme/ThemeDefault.java
index 868ac94..8a903a5 100644
--- a/src/prettify/theme/ThemeDefault.java
+++ b/src/prettify/theme/ThemeDefault.java
@@ -15,88 +15,87 @@
 
 import java.awt.Color;
 import java.awt.Font;
-import prettify.Theme;
 
 /**
  * Default theme.
  */
 public class ThemeDefault extends Theme {
 
-    public ThemeDefault() {
-        super();
+  public ThemeDefault() {
+    super();
 
-        setFont(new Font("Consolas", Font.PLAIN, 12));
-        setBackground(Color.white);
+    setFont(new Font("Consolas", Font.PLAIN, 12));
+    setBackground(Color.white);
 
-        setHighlightedBackground(Color.decode("0xcccccc"));
+    setHighlightedBackground(Color.decode("0xcccccc"));
 
-        setGutterText(Color.decode("0x000000"));
-        setGutterBorderColor(Color.decode("0xaaaaaa"));
-        setGutterBorderWidth(3);
-        setGutterTextFont(new Font("Verdana", Font.PLAIN, 11));
-        setGutterTextPaddingLeft(7);
-        setGutterTextPaddingRight(7);
+    setGutterText(Color.decode("0x000000"));
+    setGutterBorderColor(Color.decode("0xaaaaaa"));
+    setGutterBorderWidth(3);
+    setGutterTextFont(new Font("Verdana", Font.PLAIN, 11));
+    setGutterTextPaddingLeft(7);
+    setGutterTextPaddingRight(7);
 
-        Style plainStyle = new Style();
-        plainStyle.setColor(Color.decode("0x000000"));
-        setPlain(plainStyle);
+    Style plainStyle = new Style();
+    plainStyle.setColor(Color.decode("0x000000"));
+    setPlain(plainStyle);
 
-        Style style;
+    Style style;
 
-        style = new Style();
-        style.setColor(Color.decode("0x008800"));
-        setString(style);
+    style = new Style();
+    style.setColor(Color.decode("0x008800"));
+    setString(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0x000088"));
-        setKeyword(style);
+    style = new Style();
+    style.setColor(Color.decode("0x000088"));
+    setKeyword(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0x880000"));
-        setComment(style);
+    style = new Style();
+    style.setColor(Color.decode("0x880000"));
+    setComment(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0x660066"));
-        setType(style);
+    style = new Style();
+    style.setColor(Color.decode("0x660066"));
+    setType(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0x006666"));
-        setLiteral(style);
+    style = new Style();
+    style.setColor(Color.decode("0x006666"));
+    setLiteral(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0x666600"));
-        setPunctuation(style);
+    style = new Style();
+    style.setColor(Color.decode("0x666600"));
+    setPunctuation(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0x000088"));
-        setTag(style);
+    style = new Style();
+    style.setColor(Color.decode("0x000088"));
+    setTag(style);
 
-        setDeclaration(plainStyle);
+    setDeclaration(plainStyle);
 
-        style = new Style();
-        style.setColor(Color.decode("0x660066"));
-        setAttributeName(style);
+    style = new Style();
+    style.setColor(Color.decode("0x660066"));
+    setAttributeName(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0x008800"));
-        setAttributeValue(style);
+    style = new Style();
+    style.setColor(Color.decode("0x008800"));
+    setAttributeValue(style);
 
-        setNoCode(plainStyle);
+    setNoCode(plainStyle);
 
-        style = new Style();
-        style.setColor(Color.decode("0x666600"));
-        setOpenBracket(style);
+    style = new Style();
+    style.setColor(Color.decode("0x666600"));
+    setOpenBracket(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0x666600"));
-        setCloseBracket(style);
+    style = new Style();
+    style.setColor(Color.decode("0x666600"));
+    setCloseBracket(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0x660066"));
-        setVariable(style);
+    style = new Style();
+    style.setColor(Color.decode("0x660066"));
+    setVariable(style);
 
-        style = new Style();
-        style.setColor(Color.red);
-        setFunction(style);
-    }
+    style = new Style();
+    style.setColor(Color.red);
+    setFunction(style);
+  }
 }
diff --git a/src/prettify/theme/ThemeDesert.java b/src/prettify/theme/ThemeDesert.java
index 67d2e3a..77149ea 100644
--- a/src/prettify/theme/ThemeDesert.java
+++ b/src/prettify/theme/ThemeDesert.java
@@ -15,90 +15,89 @@
 
 import java.awt.Color;
 import java.awt.Font;
-import prettify.Theme;
 
 /**
  * Desert theme.
  */
 public class ThemeDesert extends Theme {
 
-    public ThemeDesert() {
-        super();
+  public ThemeDesert() {
+    super();
 
-        /* desert scheme ported from vim to google prettify */
+    /* desert scheme ported from vim to google prettify */
 
-        setFont(new Font("Consolas", Font.PLAIN, 12));
-        setBackground(Color.decode("0x111111"));
+    setFont(new Font("Consolas", Font.PLAIN, 12));
+    setBackground(Color.decode("0x111111"));
 
-        setHighlightedBackground(Color.decode("0x444444"));
+    setHighlightedBackground(Color.decode("0x444444"));
 
-        setGutterText(Color.decode("0xffffff"));
-        setGutterBorderColor(Color.decode("0x888888"));
-        setGutterBorderWidth(3);
-        setGutterTextFont(new Font("Verdana", Font.PLAIN, 11));
-        setGutterTextPaddingLeft(7);
-        setGutterTextPaddingRight(7);
+    setGutterText(Color.decode("0xffffff"));
+    setGutterBorderColor(Color.decode("0x888888"));
+    setGutterBorderWidth(3);
+    setGutterTextFont(new Font("Verdana", Font.PLAIN, 11));
+    setGutterTextPaddingLeft(7);
+    setGutterTextPaddingRight(7);
 
-        Style plainStyle = new Style();
-        plainStyle.setColor(Color.decode("0xffffff"));
-        setPlain(plainStyle);
+    Style plainStyle = new Style();
+    plainStyle.setColor(Color.decode("0xffffff"));
+    setPlain(plainStyle);
 
-        Style style;
+    Style style;
 
-        style = new Style();
-        style.setColor(Color.decode("0xffa0a0")); /* string  - pink */
-        setString(style);
+    style = new Style();
+    style.setColor(Color.decode("0xffa0a0")); /* string  - pink */
+    setString(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0xf0e68c"));
-        style.setBold(true);
-        setKeyword(style);
+    style = new Style();
+    style.setColor(Color.decode("0xf0e68c"));
+    style.setBold(true);
+    setKeyword(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0x87ceeb")); /* comment - skyblue */
-        setComment(style);
+    style = new Style();
+    style.setColor(Color.decode("0x87ceeb")); /* comment - skyblue */
+    setComment(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0x98fb98")); /* type    - lightgreen */
-        setType(style);
+    style = new Style();
+    style.setColor(Color.decode("0x98fb98")); /* type    - lightgreen */
+    setType(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0xcd5c5c")); /* literal - darkred */
-        setLiteral(style);
+    style = new Style();
+    style.setColor(Color.decode("0xcd5c5c")); /* literal - darkred */
+    setLiteral(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0xffffff"));
-        setPunctuation(style);
+    style = new Style();
+    style.setColor(Color.decode("0xffffff"));
+    setPunctuation(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0xf0e68c"));/* html/xml tag    - lightyellow */
-        style.setBold(true);
-        setTag(style);
+    style = new Style();
+    style.setColor(Color.decode("0xf0e68c"));/* html/xml tag    - lightyellow */
+    style.setBold(true);
+    setTag(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0x98fb98")); /* decimal         - lightgreen */
-        setDeclaration(style);
+    style = new Style();
+    style.setColor(Color.decode("0x98fb98")); /* decimal         - lightgreen */
+    setDeclaration(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0xbdb76b")); /* attribute name  - khaki */
-        style.setBold(true);
-        setAttributeName(style);
+    style = new Style();
+    style.setColor(Color.decode("0xbdb76b")); /* attribute name  - khaki */
+    style.setBold(true);
+    setAttributeName(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0xffa0a0")); /* attribute value - pink */
-        style.setBold(true);
-        setAttributeValue(style);
+    style = new Style();
+    style.setColor(Color.decode("0xffa0a0")); /* attribute value - pink */
+    style.setBold(true);
+    setAttributeValue(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0x333333"));
-        setNoCode(style);
+    style = new Style();
+    style.setColor(Color.decode("0x333333"));
+    setNoCode(style);
 
-        setOpenBracket(plainStyle);
+    setOpenBracket(plainStyle);
 
-        setCloseBracket(plainStyle);
+    setCloseBracket(plainStyle);
 
-        setVariable(plainStyle);
+    setVariable(plainStyle);
 
-        setFunction(plainStyle);
-    }
+    setFunction(plainStyle);
+  }
 }
diff --git a/src/prettify/theme/ThemeSonsOfObsidian.java b/src/prettify/theme/ThemeSonsOfObsidian.java
index 7ea31a7..6faa494 100644
--- a/src/prettify/theme/ThemeSonsOfObsidian.java
+++ b/src/prettify/theme/ThemeSonsOfObsidian.java
@@ -15,90 +15,90 @@
 
 import java.awt.Color;
 import java.awt.Font;
-import prettify.Theme;
 
 /**
  * Son of Obsidian theme.
+ * 
  * @author Alex Ford
  */
 public class ThemeSonsOfObsidian extends Theme {
 
-    public ThemeSonsOfObsidian() {
-        super();
+  public ThemeSonsOfObsidian() {
+    super();
 
-        /*
-         * Derived from einaros's Sons of Obsidian theme at
-         * http://studiostyl.es/schemes/son-of-obsidian by
-         * Alex Ford of CodeTunnel:
-         * http://CodeTunnel.com/blog/post/71/google-code-prettify-obsidian-theme
-         */
+    /*
+     * Derived from einaros's Sons of Obsidian theme at
+     * http://studiostyl.es/schemes/son-of-obsidian by
+     * Alex Ford of CodeTunnel:
+     * http://CodeTunnel.com/blog/post/71/google-code-prettify-obsidian-theme
+     */
 
-        setFont(new Font("Consolas", Font.PLAIN, 12));
-        setBackground(Color.decode("0x000000"));
+    setFont(new Font("Consolas", Font.PLAIN, 12));
+    setBackground(Color.decode("0x000000"));
 
-        setHighlightedBackground(Color.decode("0x333333"));
+    setHighlightedBackground(Color.decode("0x333333"));
 
-        setGutterText(Color.decode("0x555555"));
-        setGutterBorderColor(Color.decode("0x666666"));
-        setGutterBorderWidth(3);
-        setGutterTextFont(new Font("Verdana", Font.PLAIN, 11));
-        setGutterTextPaddingLeft(7);
-        setGutterTextPaddingRight(7);
+    setGutterText(Color.decode("0x555555"));
+    setGutterBorderColor(Color.decode("0x666666"));
+    setGutterBorderWidth(3);
+    setGutterTextFont(new Font("Verdana", Font.PLAIN, 11));
+    setGutterTextPaddingLeft(7);
+    setGutterTextPaddingRight(7);
 
-        Style plainStyle = new Style();
-        plainStyle.setColor(Color.decode("0xF1F2F3"));
-        setPlain(plainStyle);
+    Style plainStyle = new Style();
+    plainStyle.setColor(Color.decode("0xF1F2F3"));
+    setPlain(plainStyle);
 
-        Style style;
+    Style style;
 
-        style = new Style();
-        style.setColor(Color.decode("0xEC7600"));
-        setString(style);
+    style = new Style();
+    style.setColor(Color.decode("0xEC7600"));
+    setString(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0x93C763"));
-        setKeyword(style);
+    style = new Style();
+    style.setColor(Color.decode("0x93C763"));
+    setKeyword(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0x66747B"));
-        setComment(style);
+    style = new Style();
+    style.setColor(Color.decode("0x66747B"));
+    setComment(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0x678CB1"));
-        setType(style);
+    style = new Style();
+    style.setColor(Color.decode("0x678CB1"));
+    setType(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0xFACD22"));
-        setLiteral(style);
+    style = new Style();
+    style.setColor(Color.decode("0xFACD22"));
+    setLiteral(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0xF1F2F3"));
-        setPunctuation(style);
+    style = new Style();
+    style.setColor(Color.decode("0xF1F2F3"));
+    setPunctuation(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0x8AC763"));
-        setTag(style);
+    style = new Style();
+    style.setColor(Color.decode("0x8AC763"));
+    setTag(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0x800080"));
-        setDeclaration(style);
+    style = new Style();
+    style.setColor(Color.decode("0x800080"));
+    setDeclaration(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0xE0E2E4"));
-        setAttributeName(style);
+    style = new Style();
+    style.setColor(Color.decode("0xE0E2E4"));
+    setAttributeName(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0xEC7600"));
-        setAttributeValue(style);
+    style = new Style();
+    style.setColor(Color.decode("0xEC7600"));
+    setAttributeValue(style);
 
-        setNoCode(plainStyle);
+    setNoCode(plainStyle);
 
-        setOpenBracket(plainStyle);
+    setOpenBracket(plainStyle);
 
-        setCloseBracket(plainStyle);
+    setCloseBracket(plainStyle);
 
-        setVariable(plainStyle);
+    setVariable(plainStyle);
 
-        setFunction(plainStyle);
-    }
+    setFunction(plainStyle);
+  }
 }
diff --git a/src/prettify/theme/ThemeSunburst.java b/src/prettify/theme/ThemeSunburst.java
index 2073874..435bf7c 100644
--- a/src/prettify/theme/ThemeSunburst.java
+++ b/src/prettify/theme/ThemeSunburst.java
@@ -15,87 +15,87 @@
 
 import java.awt.Color;
 import java.awt.Font;
-import prettify.Theme;
 
 /**
  * Sunbrust theme.
  * Vim sunburst theme by David Leibovic.
+ * 
  * @author David Leibovic
  */
 public class ThemeSunburst extends Theme {
 
-    public ThemeSunburst() {
-        super();
+  public ThemeSunburst() {
+    super();
 
-        /* Vim sunburst theme by David Leibovic */
+    /* Vim sunburst theme by David Leibovic */
 
-        setFont(new Font("Consolas", Font.PLAIN, 12));
-        setBackground(Color.black);
+    setFont(new Font("Consolas", Font.PLAIN, 12));
+    setBackground(Color.black);
 
-        setHighlightedBackground(Color.decode("0x444444"));
+    setHighlightedBackground(Color.decode("0x444444"));
 
-        setGutterText(Color.decode("0xffffff"));
-        setGutterBorderColor(Color.decode("0x777777"));
-        setGutterBorderWidth(3);
-        setGutterTextFont(new Font("Verdana", Font.PLAIN, 11));
-        setGutterTextPaddingLeft(7);
-        setGutterTextPaddingRight(7);
+    setGutterText(Color.decode("0xffffff"));
+    setGutterBorderColor(Color.decode("0x777777"));
+    setGutterBorderWidth(3);
+    setGutterTextFont(new Font("Verdana", Font.PLAIN, 11));
+    setGutterTextPaddingLeft(7);
+    setGutterTextPaddingRight(7);
 
-        Style plainStyle = new Style();
-        plainStyle.setColor(Color.decode("0xffffff"));
-        setPlain(plainStyle);
+    Style plainStyle = new Style();
+    plainStyle.setColor(Color.decode("0xffffff"));
+    setPlain(plainStyle);
 
-        Style style;
+    Style style;
 
-        style = new Style();
-        style.setColor(Color.decode("0x65B042")); /* string  - green */
-        setString(style);
+    style = new Style();
+    style.setColor(Color.decode("0x65B042")); /* string  - green */
+    setString(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0xE28964")); /* keyword - dark pink */
-        setKeyword(style);
+    style = new Style();
+    style.setColor(Color.decode("0xE28964")); /* keyword - dark pink */
+    setKeyword(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0xAEAEAE")); /* comment - gray */
-        style.setItalic(true);
-        setComment(style);
+    style = new Style();
+    style.setColor(Color.decode("0xAEAEAE")); /* comment - gray */
+    style.setItalic(true);
+    setComment(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0x89bdff")); /* type - light blue */
-        setType(style);
+    style = new Style();
+    style.setColor(Color.decode("0x89bdff")); /* type - light blue */
+    setType(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0x3387CC")); /* literal - blue */
-        setLiteral(style);
+    style = new Style();
+    style.setColor(Color.decode("0x3387CC")); /* literal - blue */
+    setLiteral(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0xffffff")); /* punctuation - white */
-        setPunctuation(style);
+    style = new Style();
+    style.setColor(Color.decode("0xffffff")); /* punctuation - white */
+    setPunctuation(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0x89bdff")); /* html/xml tag    - light blue */
-        setTag(style);
+    style = new Style();
+    style.setColor(Color.decode("0x89bdff")); /* html/xml tag    - light blue */
+    setTag(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0x3387CC")); /* decimal - blue */
-        setDeclaration(style);
+    style = new Style();
+    style.setColor(Color.decode("0x3387CC")); /* decimal - blue */
+    setDeclaration(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0xbdb76b")); /* html/xml attribute name  - khaki */
-        setAttributeName(style);
+    style = new Style();
+    style.setColor(Color.decode("0xbdb76b")); /* html/xml attribute name  - khaki */
+    setAttributeName(style);
 
-        style = new Style();
-        style.setColor(Color.decode("0x65B042")); /* html/xml attribute value - green */
-        setAttributeValue(style);
+    style = new Style();
+    style.setColor(Color.decode("0x65B042")); /* html/xml attribute value - green */
+    setAttributeValue(style);
 
-        setNoCode(plainStyle);
+    setNoCode(plainStyle);
 
-        setOpenBracket(plainStyle);
+    setOpenBracket(plainStyle);
 
-        setCloseBracket(plainStyle);
+    setCloseBracket(plainStyle);
 
-        setVariable(plainStyle);
+    setVariable(plainStyle);
 
-        setFunction(plainStyle);
-    }
+    setFunction(plainStyle);
+  }
 }
diff --git a/test/prettify/PrettifyTest.java b/test/prettify/PrettifyTest.java
index a42a908..11b5e60 100644
--- a/test/prettify/PrettifyTest.java
+++ b/test/prettify/PrettifyTest.java
@@ -13,6 +13,8 @@
 // limitations under the License.
 package prettify;
 
+import prettify.parser.Job;
+import prettify.parser.Prettify;
 import java.util.ListIterator;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;