/* * Copyright 2010-2011, Sikuli.org * Released under the MIT License. * */ package org.sikuli.ide; import java.awt.*; import java.util.*; import javax.swing.text.*; import org.sikuli.script.Debug; public class EditorDocument extends DefaultStyledDocument{ private String word = ""; private SimpleAttributeSet keyword = new SimpleAttributeSet(); private SimpleAttributeSet string = new SimpleAttributeSet(); private SimpleAttributeSet normal = new SimpleAttributeSet(); private SimpleAttributeSet number = new SimpleAttributeSet(); private SimpleAttributeSet comments = new SimpleAttributeSet(); private int currentPos = 0; private Vector keywords = new Vector(); public static int STRING_MODE = 10; public static int TEXT_MODE = 11; public static int NUMBER_MODE = 12; public static int COMMENT_MODE = 13; private int mode = TEXT_MODE; private static String[] arrKeywords = { "and", "del", "for", "is", "raise", "assert", "elif", "from", "lambda", "return", "break", "else", "global", "not", "try", "class", "except", "if", "or", "while", "continue", "exec", "import", "pass", "yield", "def", "finally", "in", "print" }; private void initKeywords(){ for(int i=0;i<arrKeywords.length;i++) keywords.add(arrKeywords[i]); } public EditorDocument() { initKeywords(); //set the bold attribute StyleConstants.setBold(keyword, true); StyleConstants.setForeground(string, Color.magenta); StyleConstants.setForeground(number, Color.orange); StyleConstants.setForeground(comments, Color.blue); StyleConstants.setForeground(keyword,Color.red); StyleConstants.setItalic(comments, true); } private void insertKeyword(String str, int pos){ try{ //remove the old word and formatting this.remove(pos - str.length(), str.length()); /*replace it with the same word, but new formatting *we MUST call the super class insertString method here, otherwise we *would end up in an infinite loop !!!!!*/ super.insertString(pos - str.length(), str, keyword); } catch (Exception ex){ ex.printStackTrace(); } } private void insertTextString(String str, int pos){ try{ //remove the old word and formatting this.remove(pos,str.length()); super.insertString(pos, str, string); } catch (Exception ex){ ex.printStackTrace(); } } private void insertNumberString(String str, int pos){ try{ //remove the old word and formatting this.remove(pos,str.length()); super.insertString(pos, str, number); } catch (Exception ex){ ex.printStackTrace(); } } private void insertCommentString(String str, int pos){ try{ //remove the old word and formatting this.remove(pos,str.length()); super.insertString(pos, str, comments); } catch (Exception ex){ ex.printStackTrace(); } } private void checkForString(){ int offs = this.currentPos; Element element = this.getParagraphElement(offs); String elementText = ""; try{ //this gets our chuck of current text for the element we're on elementText = this.getText(element.getStartOffset(), element.getEndOffset() - element.getStartOffset()); } catch(Exception ex){ //whoops! Debug.error("no text"); ex.printStackTrace(); } int strLen = elementText.length(); if (strLen == 0) {return;} int i = 0; if (element.getStartOffset() > 0){ //translates backward if neccessary offs = offs - element.getStartOffset(); } int quoteCount = 0; if ((offs >= 0) && (offs <= strLen-1)){ i = offs; while (i >0){ //the while loop walks back until we hit a delimiter char charAt = elementText.charAt(i); if ((charAt == '"')){ quoteCount ++; } i--; } int rem = quoteCount % 2; //System.out.println(rem); mode = (rem == 0) ? TEXT_MODE: STRING_MODE; } } private void checkForKeyword(){ if (mode != TEXT_MODE) { return; } int offs = this.currentPos; Element element = this.getParagraphElement(offs); String elementText = ""; try{ //this gets our chuck of current text for the element we're on elementText = this.getText(element.getStartOffset(), element.getEndOffset() - element.getStartOffset()); } catch(Exception ex){ //whoops! Debug.error("no text"); ex.printStackTrace(); } int strLen = elementText.length(); if (strLen == 0) {return;} int i = 0; if (element.getStartOffset() > 0){ //translates backward if neccessary offs = offs - element.getStartOffset(); } if ((offs >= 0) && (offs <= strLen-1)){ i = offs; while (i >0){ //the while loop walks back until we hit a delimiter i--; char charAt = elementText.charAt(i); if ((charAt == ' ') | (i == 0) | (charAt =='(') | (charAt ==')') | (charAt == '{') | (charAt == '}')){ //if i == 0 then we're atthe begininng if(i != 0){ i++; } word = elementText.substring(i, offs);//skip the period String s = word.trim().toLowerCase(); //this is what actually checks for a matching keyword if (keywords.contains(s)){ insertKeyword(word, currentPos); } break; } } } } private void checkForNumber(){ int offs = this.currentPos; Element element = this.getParagraphElement(offs); String elementText = ""; try{ //this gets our chuck of current text for the element we're on elementText = this.getText(element.getStartOffset(), element.getEndOffset() - element.getStartOffset()); } catch(Exception ex){ //whoops! Debug.error("no text"); ex.printStackTrace(); } int strLen = elementText.length(); if (strLen == 0) {return;} int i = 0; if (element.getStartOffset() > 0){ //translates backward if neccessary offs = offs - element.getStartOffset(); } mode = TEXT_MODE; if ((offs >= 0) && (offs <= strLen-1)){ i = offs; while (i >0){ //the while loop walks back until we hit a delimiter char charAt = elementText.charAt(i); if ((charAt == ' ') | (i == 0) | (charAt =='(') | (charAt ==')') | (charAt == '{') | (charAt == '}') /*|*/){ //if i == 0 then we're at the begininng if(i != 0){ i++; } mode = NUMBER_MODE; break; } else if (!(charAt >= '0' & charAt <= '9' | charAt=='.' | charAt=='+' | charAt=='-' | charAt=='/' | charAt=='*'| charAt=='%' | charAt=='=')){ mode = TEXT_MODE; break; } i--; } } } private void checkForComment(){ int offs = this.currentPos; Element element = this.getParagraphElement(offs); String elementText = ""; try{ //this gets our chuck of current text for the element we're on elementText = this.getText(element.getStartOffset(), element.getEndOffset() - element.getStartOffset()); } catch(Exception ex){ //whoops! Debug.error("no text"); ex.printStackTrace(); } int strLen = elementText.length(); if (strLen == 0) {return;} int i = 0; if (element.getStartOffset() > 0){ //translates backward if neccessary offs = offs - element.getStartOffset(); } if ((offs >= 1) && (offs <= strLen-1)){ i = offs; char commentStartChar1 = elementText.charAt(i-1); char commentStartChar2 = elementText.charAt(i); if ((commentStartChar1 == '/' && commentStartChar2 == '*')){ mode = COMMENT_MODE; this.insertCommentString("/*", currentPos-1); } else if (commentStartChar1 == '*' && commentStartChar2 == '/'){ mode = TEXT_MODE; this.insertCommentString("*/", currentPos-1); } } } private void processChar(String str){ char strChar = str.charAt(0); if (mode != this.COMMENT_MODE){ mode = TEXT_MODE; } switch (strChar){ case ('{'):case ('}'):case (' '): case('\n'): case ('('):case (')'):case (';'):case ('.'):{ checkForKeyword(); if (mode == STRING_MODE && strChar == '\n'){ mode = TEXT_MODE; } } break; case ('"'):{ insertTextString(str, currentPos); this.checkForString(); } break; case ('0'):case ('1'):case ('2'):case ('3'):case ('4'): case ('5'):case ('6'):case ('7'):case ('8'):case ('9'):{ checkForNumber(); } break; case ('*'):case ('/'):{ checkForComment(); } break; } if (mode == this.TEXT_MODE){ this.checkForString(); } if (mode == this.STRING_MODE){ insertTextString(str, this.currentPos); } else if (mode == this.NUMBER_MODE){ insertNumberString(str, this.currentPos); } else if (mode == this.COMMENT_MODE){ insertCommentString(str, this.currentPos); } } private void processChar(char strChar){ char[] chrstr = new char[1]; chrstr[0] = strChar; String str = new String(chrstr); processChar(str); } @Override public void insertString(int offs, String str, AttributeSet a) throws BadLocationException{ super.insertString(offs, str, normal); System.out.println("insertString"); int strLen = str.length(); int endpos = offs + strLen; int strpos; for (int i=offs;i<endpos;i++){ currentPos = i; strpos = i - offs; processChar(str.charAt(strpos)); } currentPos = offs; } // line starting from 0 public int getLineStartOffset(int linenum) throws BadLocationException { Element map = this.getDefaultRootElement(); if (linenum < 0) { throw new BadLocationException("Negative line", -1); } else if (linenum >= map.getElementCount()) { throw new BadLocationException("No such line", this.getLength()+1); } else { Element lineElem = map.getElement(linenum); return lineElem.getStartOffset(); } } public int getLineLength(int linenum) throws BadLocationException { Element map = this.getDefaultRootElement(); if (linenum < 0) { throw new BadLocationException("Negative line", -1); } else if (linenum >= map.getElementCount()) { throw new BadLocationException("No such line", this.getLength()+1); } else { Element lineElem = map.getElement(linenum); return lineElem.getEndOffset() - lineElem.getStartOffset(); } } /** * Change the indentation of a line. Any existing leading whitespace is replaced by * the appropriate number of tab characters and padded with blank characters if * necessary. * @param linenum the line number (0-based) * @param columns the number of columns by which to increase the indentation (if * columns is greater than 0) or decrease the indentation (if columns is * less than 0) * @throws BadLocationException if the specified line does not exist */ public void changeIndentation(int linenum, int columns) throws BadLocationException { if (columns == 0) return; int lineStart = getLineStartOffset(linenum); int lineLength = getLineLength(linenum); String line = this.getText(lineStart, lineLength); // determine current indentation and number of whitespace characters int wsChars; int indentation = 0; int tabWidth = PreferencesUser.getInstance().getTabWidth(); for (wsChars = 0; wsChars < line.length(); wsChars++) { char c = line.charAt(wsChars); if (c == ' ') { indentation++; } else if (c == '\t') { indentation += tabWidth; } else { break; } } int newIndentation = indentation + columns; if (newIndentation <= 0) { this.remove(lineStart, wsChars); return; } // build whitespace string for new indentation StringBuilder newWs = new StringBuilder(newIndentation / tabWidth + tabWidth - 1); int ind = 0; for (; ind + tabWidth <= newIndentation; ind += tabWidth) { newWs.append('\t'); } for (; ind < newIndentation; ind++) { newWs.append(' '); } this.replace(lineStart, wsChars, newWs.toString(), null); } public Vector getKeywords(){ return this.keywords; } public void setKeywords(Vector aKeywordList){ if (aKeywordList != null){ this.keywords = aKeywordList; } } }