Mega Code Archive

 
Categories / Java Tutorial / SWT
 

A simple editor

//Code revised from /* The Definitive Guide to SWT and JFace by Robert Harris and Rob Warner  Apress 2004 */ import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collection; import java.util.Hashtable; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.MissingResourceException; import java.util.ResourceBundle; import java.util.Stack; import java.util.StringTokenizer; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.ExtendedModifyEvent; import org.eclipse.swt.custom.ExtendedModifyListener; import org.eclipse.swt.custom.LineStyleEvent; import org.eclipse.swt.custom.LineStyleListener; import org.eclipse.swt.custom.ST; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.custom.StyledTextPrintOptions; import org.eclipse.swt.events.ArmEvent; import org.eclipse.swt.events.ArmListener; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.layout.FormAttachment; import org.eclipse.swt.layout.FormData; import org.eclipse.swt.layout.FormLayout; import org.eclipse.swt.printing.Printer; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.FileDialog; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.MessageBox; import org.eclipse.swt.widgets.Shell; public class PmpEditor {   // The number of operations that can be undone   private static final int UNDO_LIMIT = 500;   // Contains a reference to this application   private static PmpEditor app;   // Contains a reference to the main window   private Shell shell;   // Displays the file   private StyledText st;   // The full path of the current file   private String filename;   // The font for the StyledText   private Font font;   // The label to display statistics   private Label status;   // The print options and printer   private StyledTextPrintOptions options;   private Printer printer;   // The stack used to store the undo information   private Stack changes;   // Flag to set before performaing an undo, so the undo   // operation doesn't get stored with the rest of the undo   // information   private boolean ignoreUndo = false;   // Syntax data for the current extension   private SyntaxData sd;   // Line style listener   private PmpeLineStyleListener lineStyleListener;   /**    * Gets the reference to this application    *     * @return HexEditor    */   public static PmpEditor getApp() {     return app;   }   /**    * Constructs a PmpEditor    */   public PmpEditor() {     app = this;     changes = new Stack();     // Set up the printing options     options = new StyledTextPrintOptions();     options.footer = StyledTextPrintOptions.SEPARATOR + StyledTextPrintOptions.PAGE_TAG         + StyledTextPrintOptions.SEPARATOR + "Confidential";   }   /**    * Runs the application    */   public void run() {     Display display = new Display();     shell = new Shell(display);     // Choose a monospaced font     font = new Font(display, "Terminal", 12, SWT.NONE);     createContents(shell);     shell.open();     while (!shell.isDisposed()) {       if (!display.readAndDispatch()) {         display.sleep();       }     }     font.dispose();     display.dispose();     if (printer != null)       printer.dispose();   }   /**    * Creates the main window's contents    *     * @param shell    *          the main window    */   private void createContents(Shell shell) {     // Set the layout and the menu bar     shell.setLayout(new FormLayout());     shell.setMenuBar(new PmpEditorMenu(shell).getMenu());     // Create the status bar     status = new Label(shell, SWT.BORDER);     FormData data = new FormData();     data.left = new FormAttachment(0, 0);     data.right = new FormAttachment(100, 0);     data.bottom = new FormAttachment(100, 0);     data.height = status.computeSize(SWT.DEFAULT, SWT.DEFAULT).y;     status.setLayoutData(data);     // Create the styled text     st = new StyledText(shell, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);     data = new FormData();     data.left = new FormAttachment(0);     data.right = new FormAttachment(100);     data.top = new FormAttachment(0);     data.bottom = new FormAttachment(status);     st.setLayoutData(data);     // Set the font     st.setFont(font);     // Add Brief delete next word     // Use SWT.MOD1 instead of SWT.CTRL for portability     st.setKeyBinding('k' | SWT.MOD1, ST.DELETE_NEXT);     // Add vi end of line (kind of)     // Use SWT.MOD1 instead of SWT.CTRL for portability     // Use SWT.MOD2 instead of SWT.SHIFT for portability     // Shift+4 is $     st.setKeyBinding('4' | SWT.MOD1 | SWT.MOD2, ST.LINE_END);     // Handle key presses     st.addKeyListener(new KeyAdapter() {       public void keyPressed(KeyEvent event) {         // Update the status bar         updateStatus();       }     });     // Handle text modifications     st.addModifyListener(new ModifyListener() {       public void modifyText(ModifyEvent event) {         // Update the status bar         updateStatus();         // Update the comments         if (lineStyleListener != null) {           lineStyleListener.refreshMultilineComments(st.getText());           st.redraw();         }       }     });     // Store undo information     st.addExtendedModifyListener(new ExtendedModifyListener() {       public void modifyText(ExtendedModifyEvent event) {         if (!ignoreUndo) {           // Push this change onto the changes stack           changes.push(new TextChange(event.start, event.length, event.replacedText));           if (changes.size() > UNDO_LIMIT)             changes.remove(0);         }       }     });     // Update the title bar and the status bar     updateTitle();     updateStatus();   }   /**    * Opens a file    */   public void openFile() {     FileDialog dlg = new FileDialog(shell);     String temp = dlg.open();     if (temp != null) {       try {         // Get the file's contents         String text = PmpeIoManager.getFile(temp);         // File loaded, so save the file name         filename = temp;         // Update the syntax properties to use         updateSyntaxData();         // Put the new file's data in the StyledText         st.setText(text);         // Update the title bar         updateTitle();         // Delete any undo information         changes.clear();       } catch (IOException e) {         showError(e.getMessage());       }     }   }   /**    * Saves a file    */   public void saveFile() {     if (filename == null) {       saveFileAs();     } else {       try {         // Save the file and update the title bar based on the new file name         PmpeIoManager.saveFile(filename, st.getText().getBytes());         updateTitle();       } catch (IOException e) {         showError(e.getMessage());       }     }   }   /**    * Saves a file under a different name    */   public void saveFileAs() {     FileDialog dlg = new FileDialog(shell);     if (filename != null) {       dlg.setFileName(filename);     }     String temp = dlg.open();     if (temp != null) {       filename = temp;       // The extension may have changed; update the syntax data accordingly       updateSyntaxData();       saveFile();     }   }   /**    * Prints the document to the default printer    */   public void print() {     if (printer == null)       printer = new Printer();     options.header = StyledTextPrintOptions.SEPARATOR + filename + StyledTextPrintOptions.SEPARATOR;     st.print(printer, options).run();   }   /**    * Cuts the current selection to the clipboard    */   public void cut() {     st.cut();   }   /**    * Copies the current selection to the clipboard    */   public void copy() {     st.copy();   }   /**    * Pastes the clipboard's contents    */   public void paste() {     st.paste();   }   /**    * Selects all the text    */   public void selectAll() {     st.selectAll();   }   /**    * Undoes the last change    */   public void undo() {     // Make sure undo stack isn't empty     if (!changes.empty()) {       // Get the last change       TextChange change = (TextChange) changes.pop();       // Set the flag. Otherwise, the replaceTextRange call will get placed       // on the undo stack       ignoreUndo = true;       // Replace the changed text       st.replaceTextRange(change.getStart(), change.getLength(), change.getReplacedText());       // Move the caret       st.setCaretOffset(change.getStart());       // Scroll the screen       st.setTopIndex(st.getLineAtOffset(change.getStart()));       ignoreUndo = false;     }   }   /**    * Toggles word wrap    */   public void toggleWordWrap() {     st.setWordWrap(!st.getWordWrap());   }   /**    * Gets the current word wrap settings    *     * @return boolean    */   public boolean getWordWrap() {     return st.getWordWrap();   }   /**    * Shows an about box    */   public void about() {     MessageBox mb = new MessageBox(shell, SWT.ICON_INFORMATION | SWT.OK);     mb.setMessage("Poor Man's Programming Editor");     mb.open();   }   /**    * Updates the title bar    */   private void updateTitle() {     String fn = filename == null ? "Untitled" : filename;     shell.setText(fn + " -- PmPe");   }   /**    * Updates the status bar    */   private void updateStatus() {     // Show the offset into the file, the total number of characters in the     // file,     // the current line number (1-based) and the total number of lines     StringBuffer buf = new StringBuffer();     buf.append("Offset: ");     buf.append(st.getCaretOffset());     buf.append("\tChars: ");     buf.append(st.getCharCount());     buf.append("\tLine: ");     buf.append(st.getLineAtOffset(st.getCaretOffset()) + 1);     buf.append(" of ");     buf.append(st.getLineCount());     status.setText(buf.toString());   }   /**    * Updates the syntax data based on the filename's extension    */   private void updateSyntaxData() {     // Determine the extension of the current file     String extension = "";     if (filename != null) {       int pos = filename.lastIndexOf(".");       if (pos > -1 && pos < filename.length() - 2) {         extension = filename.substring(pos + 1);       }     }     // Get the syntax data for the extension     sd = SyntaxManager.getSyntaxData(extension);     // Reset the line style listener     if (lineStyleListener != null) {       st.removeLineStyleListener(lineStyleListener);     }     lineStyleListener = new PmpeLineStyleListener(sd);     st.addLineStyleListener(lineStyleListener);     // Redraw the contents to reflect the new syntax data     st.redraw();   }   /**    * Shows an error message    *     * @param error    *          the text to show    */   private void showError(String error) {     MessageBox mb = new MessageBox(shell, SWT.ICON_ERROR | SWT.OK);     mb.setMessage(error);     mb.open();   }   /**    * The application entry point    *     * @param args    *          the command line arguments    */   public static void main(String[] args) {     new PmpEditor().run();   } } class PmpEditorMenu {   // The underlying menu this class wraps   Menu menu = null;   /**    * Constructs a PmpEditorMenu    *     * @param shell    *          the parent shell    */   public PmpEditorMenu(final Shell shell) {     // Create the menu     menu = new Menu(shell, SWT.BAR);     // Create the File top-level menu     MenuItem item = new MenuItem(menu, SWT.CASCADE);     item.setText("File");     Menu dropMenu = new Menu(shell, SWT.DROP_DOWN);     item.setMenu(dropMenu);     // Create File->Open     item = new MenuItem(dropMenu, SWT.NULL);     item.setText("Open...\tCtrl+O");     item.setAccelerator(SWT.CTRL + 'O');     item.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         PmpEditor.getApp().openFile();       }     });     // Create File->Save     item = new MenuItem(dropMenu, SWT.NULL);     item.setText("Save\tCtrl+S");     item.setAccelerator(SWT.CTRL + 'S');     item.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         PmpEditor.getApp().saveFile();       }     });     // Create File->Save As     item = new MenuItem(dropMenu, SWT.NULL);     item.setText("Save As...");     item.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         PmpEditor.getApp().saveFileAs();       }     });     new MenuItem(dropMenu, SWT.SEPARATOR);     // Create File->Print     item = new MenuItem(dropMenu, SWT.NULL);     item.setText("Print\tCtrl+P");     item.setAccelerator(SWT.CTRL + 'P');     item.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         PmpEditor.getApp().print();       }     });     new MenuItem(dropMenu, SWT.SEPARATOR);     // Create File->Exit     item = new MenuItem(dropMenu, SWT.NULL);     item.setText("Exit\tAlt+F4");     item.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         shell.close();       }     });     // Create Edit     item = new MenuItem(menu, SWT.CASCADE);     item.setText("Edit");     dropMenu = new Menu(shell, SWT.DROP_DOWN);     item.setMenu(dropMenu);     // Create Edit->Cut     item = new MenuItem(dropMenu, SWT.NULL);     item.setText("Cut\tCtrl+X");     item.setAccelerator(SWT.CTRL + 'X');     item.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         PmpEditor.getApp().cut();       }     });     // Create Edit->Copy     item = new MenuItem(dropMenu, SWT.NULL);     item.setText("Copy\tCtrl+C");     item.setAccelerator(SWT.CTRL + 'C');     item.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         PmpEditor.getApp().copy();       }     });     // Create Edit->Paste     item = new MenuItem(dropMenu, SWT.NULL);     item.setText("Paste\tCtrl+V");     item.setAccelerator(SWT.CTRL + 'V');     item.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         PmpEditor.getApp().paste();       }     });     new MenuItem(dropMenu, SWT.SEPARATOR);     // Create Select All     item = new MenuItem(dropMenu, SWT.NULL);     item.setText("Select All\tCtrl+A");     item.setAccelerator(SWT.CTRL + 'A');     item.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         PmpEditor.getApp().selectAll();       }     });     new MenuItem(dropMenu, SWT.SEPARATOR);     // Create Undo     item = new MenuItem(dropMenu, SWT.NULL);     item.setText("Undo\tCtrl+Z");     item.setAccelerator(SWT.CTRL + 'Z');     item.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         PmpEditor.getApp().undo();       }     });     new MenuItem(dropMenu, SWT.SEPARATOR);     // Create Word Wrap     final MenuItem wwItem = new MenuItem(dropMenu, SWT.CHECK);     wwItem.setText("Word Wrap\tCtrl+W");     wwItem.setAccelerator(SWT.CTRL + 'W');     wwItem.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         PmpEditor.getApp().toggleWordWrap();       }     });     wwItem.addArmListener(new ArmListener() {       public void widgetArmed(ArmEvent event) {         wwItem.setSelection(PmpEditor.getApp().getWordWrap());       }     });     // Create Help     item = new MenuItem(menu, SWT.CASCADE);     item.setText("Help");     dropMenu = new Menu(shell, SWT.DROP_DOWN);     item.setMenu(dropMenu);     // Create Help->About     item = new MenuItem(dropMenu, SWT.NULL);     item.setText("About\tCtrl+A");     item.setAccelerator(SWT.CTRL + 'A');     item.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         PmpEditor.getApp().about();       }     });   }   /**    * Gets the underlying menu    *     * @return Menu    */   public Menu getMenu() {     return menu;   } } class PmpeIoManager {   /**    * Gets a file (loads it) from the filesystem    *     * @param filename    *          the full path of the file    * @return String    * @throws IOException    *           if file cannot be loaded    */   public static String getFile(String filename) throws IOException {     InputStream in = new BufferedInputStream(new FileInputStream(filename));     StringBuffer buf = new StringBuffer();     int c;     while ((c = in.read()) != -1) {       buf.append((char) c);     }     return buf.toString();   }   /**    * Saves a file    *     * @param filename    *          the full path of the file to save    * @param data    *          the data to save    * @throws IOException    *           if file cannot be saved    */   public static void saveFile(String filename, byte[] data) throws IOException {     File outputFile = new File(filename);     FileOutputStream out = new FileOutputStream(outputFile);     out.write(data);     out.close();   } } class TextChange {   // The starting offset of the change   private int start;   // The length of the change   private int length;   // The replaced text   String replacedText;   /**    * Constructs a TextChange    *     * @param start    *          the starting offset of the change    * @param length    *          the length of the change    * @param replacedText    *          the text that was replaced    */   public TextChange(int start, int length, String replacedText) {     this.start = start;     this.length = length;     this.replacedText = replacedText;   }   /**    * Returns the start    *     * @return int    */   public int getStart() {     return start;   }   /**    * Returns the length    *     * @return int    */   public int getLength() {     return length;   }   /**    * Returns the replacedText    *     * @return String    */   public String getReplacedText() {     return replacedText;   } } /**  * This class contains information for syntax coloring and styling for an  * extension  */ class SyntaxData {   private String extension;   private Collection keywords;   private String punctuation;   private String comment;   private String multiLineCommentStart;   private String multiLineCommentEnd;   /**    * Constructs a SyntaxData    *     * @param extension    *          the extension    */   public SyntaxData(String extension) {     this.extension = extension;   }   /**    * Gets the extension    *     * @return String    */   public String getExtension() {     return extension;   }   /**    * Gets the comment    *     * @return String    */   public String getComment() {     return comment;   }   /**    * Sets the comment    *     * @param comment    *          The comment to set.    */   public void setComment(String comment) {     this.comment = comment;   }   /**    * Gets the keywords    *     * @return Collection    */   public Collection getKeywords() {     return keywords;   }   /**    * Sets the keywords    *     * @param keywords    *          The keywords to set.    */   public void setKeywords(Collection keywords) {     this.keywords = keywords;   }   /**    * Gets the multiline comment end    *     * @return String    */   public String getMultiLineCommentEnd() {     return multiLineCommentEnd;   }   /**    * Sets the multiline comment end    *     * @param multiLineCommentEnd    *          The multiLineCommentEnd to set.    */   public void setMultiLineCommentEnd(String multiLineCommentEnd) {     this.multiLineCommentEnd = multiLineCommentEnd;   }   /**    * Gets the multiline comment start    *     * @return String    */   public String getMultiLineCommentStart() {     return multiLineCommentStart;   }   /**    * Sets the multiline comment start    *     * @param multiLineCommentStart    *          The multiLineCommentStart to set.    */   public void setMultiLineCommentStart(String multiLineCommentStart) {     this.multiLineCommentStart = multiLineCommentStart;   }   /**    * Gets the punctuation    *     * @return String    */   public String getPunctuation() {     return punctuation;   }   /**    * Sets the punctuation    *     * @param punctuation    *          The punctuation to set.    */   public void setPunctuation(String punctuation) {     this.punctuation = punctuation;   } } /**  * This class manages the syntax coloring and styling data  */ class SyntaxManager {   // Lazy cache of SyntaxData objects   private static Map data = new Hashtable();   /**    * Gets the syntax data for an extension    */   public static synchronized SyntaxData getSyntaxData(String extension) {     // Check in cache     SyntaxData sd = (SyntaxData) data.get(extension);     if (sd == null) {       // Not in cache; load it and put in cache       sd = loadSyntaxData(extension);       if (sd != null)         data.put(sd.getExtension(), sd);     }     return sd;   }   /**    * Loads the syntax data for an extension    *     * @param extension    *          the extension to load    * @return SyntaxData    */   private static SyntaxData loadSyntaxData(String extension) {     SyntaxData sd = null;     try {       ResourceBundle rb = ResourceBundle.getBundle("examples.ch11." + extension);       sd = new SyntaxData(extension);       sd.setComment(rb.getString("comment"));       sd.setMultiLineCommentStart(rb.getString("multilinecommentstart"));       sd.setMultiLineCommentEnd(rb.getString("multilinecommentend"));       // Load the keywords       Collection keywords = new ArrayList();       for (StringTokenizer st = new StringTokenizer(rb.getString("keywords"), " "); st           .hasMoreTokens();) {         keywords.add(st.nextToken());       }       sd.setKeywords(keywords);       // Load the punctuation       sd.setPunctuation(rb.getString("punctuation"));     } catch (MissingResourceException e) {       // Ignore     }     return sd;   } } /**  * This class performs the syntax highlighting and styling for Pmpe  */ class PmpeLineStyleListener implements LineStyleListener {   // Colors   private static final Color COMMENT_COLOR = Display.getCurrent().getSystemColor(       SWT.COLOR_DARK_GREEN);   private static final Color COMMENT_BACKGROUND = Display.getCurrent().getSystemColor(       SWT.COLOR_GRAY);   private static final Color PUNCTUATION_COLOR = Display.getCurrent().getSystemColor(       SWT.COLOR_DARK_CYAN);   private static final Color KEYWORD_COLOR = Display.getCurrent().getSystemColor(       SWT.COLOR_DARK_MAGENTA);   // Holds the syntax data   private SyntaxData syntaxData;   // Holds the offsets for all multiline comments   List commentOffsets;   /**    * PmpeLineStyleListener constructor    *     * @param syntaxData    *          the syntax data to use    */   public PmpeLineStyleListener(SyntaxData syntaxData) {     this.syntaxData = syntaxData;     commentOffsets = new LinkedList();   }   /**    * Refreshes the offsets for all multiline comments in the parent StyledText.    * The parent StyledText should call this whenever its text is modified. Note    * that this code doesn't ignore comment markers inside strings.    *     * @param text    *          the text from the StyledText    */   public void refreshMultilineComments(String text) {     // Clear any stored offsets     commentOffsets.clear();     if (syntaxData != null) {       // Go through all the instances of COMMENT_START       for (int pos = text.indexOf(syntaxData.getMultiLineCommentStart()); pos > -1; pos = text           .indexOf(syntaxData.getMultiLineCommentStart(), pos)) {         // offsets[0] holds the COMMENT_START offset         // and COMMENT_END holds the ending offset         int[] offsets = new int[2];         offsets[0] = pos;         // Find the corresponding end comment.         pos = text.indexOf(syntaxData.getMultiLineCommentEnd(), pos);         // If no corresponding end comment, use the end of the text         offsets[1] = pos == -1 ? text.length() - 1 : pos             + syntaxData.getMultiLineCommentEnd().length() - 1;         pos = offsets[1];         // Add the offsets to the collection         commentOffsets.add(offsets);       }     }   }   /**    * Checks to see if the specified section of text begins inside a multiline    * comment. Returns the index of the closing comment, or the end of the line    * if the whole line is inside the comment. Returns -1 if the line doesn't    * begin inside a comment.    *     * @param start    *          the starting offset of the text    * @param length    *          the length of the text    * @return int    */   private int getBeginsInsideComment(int start, int length) {     // Assume section doesn't being inside a comment     int index = -1;     // Go through the multiline comment ranges     for (int i = 0, n = commentOffsets.size(); i < n; i++) {       int[] offsets = (int[]) commentOffsets.get(i);       // If starting offset is past range, quit       if (offsets[0] > start + length)         break;       // Check to see if section begins inside a comment       if (offsets[0] <= start && offsets[1] >= start) {         // It does; determine if the closing comment marker is inside         // this section         index = offsets[1] > start + length ? start + length : offsets[1]             + syntaxData.getMultiLineCommentEnd().length() - 1;       }     }     return index;   }   /**    * Called by StyledText to get styles for a line    */   public void lineGetStyle(LineStyleEvent event) {     // Only do styles if syntax data has been loaded     if (syntaxData != null) {       // Create collection to hold the StyleRanges       List styles = new ArrayList();       int start = 0;       int length = event.lineText.length();       // Check if line begins inside a multiline comment       int mlIndex = getBeginsInsideComment(event.lineOffset, event.lineText.length());       if (mlIndex > -1) {         // Line begins inside multiline comment; create the range         styles.add(new StyleRange(event.lineOffset, mlIndex - event.lineOffset, COMMENT_COLOR,             COMMENT_BACKGROUND));         start = mlIndex;       }       // Do punctuation, single-line comments, and keywords       while (start < length) {         // Check for multiline comments that begin inside this line         if (event.lineText.indexOf(syntaxData.getMultiLineCommentStart(), start) == start) {           // Determine where comment ends           int endComment = event.lineText.indexOf(syntaxData.getMultiLineCommentEnd(), start);           // If comment doesn't end on this line, extend range to end of line           if (endComment == -1)             endComment = length;           else             endComment += syntaxData.getMultiLineCommentEnd().length();           styles.add(new StyleRange(event.lineOffset + start, endComment - start, COMMENT_COLOR,               COMMENT_BACKGROUND));           // Move marker           start = endComment;         }         // Check for single line comments         else if (event.lineText.indexOf(syntaxData.getComment(), start) == start) {           // Comment rest of line           styles.add(new StyleRange(event.lineOffset + start, length - start, COMMENT_COLOR,               COMMENT_BACKGROUND));           // Move marker           start = length;         }         // Check for punctuation         else if (syntaxData.getPunctuation().indexOf(event.lineText.charAt(start)) > -1) {           // Add range for punctuation           styles.add(new StyleRange(event.lineOffset + start, 1, PUNCTUATION_COLOR, null));           ++start;         } else if (Character.isLetter(event.lineText.charAt(start))) {           // Get the next word           StringBuffer buf = new StringBuffer();           int i = start;           // Call any consecutive letters a word           for (; i < length && Character.isLetter(event.lineText.charAt(i)); i++) {             buf.append(event.lineText.charAt(i));           }           // See if the word is a keyword           if (syntaxData.getKeywords().contains(buf.toString())) {             // It's a keyword; create the StyleRange             styles.add(new StyleRange(event.lineOffset + start, i - start, KEYWORD_COLOR, null,                 SWT.BOLD));           }           // Move the marker to the last char (the one that wasn't a letter)           // so it can be retested in the next iteration through the loop           start = i;         } else           // It's nothing we're interested in; advance the marker           ++start;       }       // Copy the StyleRanges back into the event       event.styles = (StyleRange[]) styles.toArray(new StyleRange[0]);     }   } }