X-Git-Url: http://secure.phpeclipse.com diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpeclipse/phpeditor/php/PHPPartitionScanner.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpeclipse/phpeditor/php/PHPPartitionScanner.java index f571e39..eb31667 100644 --- a/net.sourceforge.phpeclipse/src/net/sourceforge/phpeclipse/phpeditor/php/PHPPartitionScanner.java +++ b/net.sourceforge.phpeclipse/src/net/sourceforge/phpeclipse/phpeditor/php/PHPPartitionScanner.java @@ -6,61 +6,85 @@ * Created on 05.03.2003 * * @author Stefan Langer (musk) - * @version $Revision: 1.13 $ + * @version $Revision: 1.20 $ */ package net.sourceforge.phpeclipse.phpeditor.php; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; -import org.eclipse.jface.text.*; -import org.eclipse.jface.text.rules.*; +import org.eclipse.jface.text.Assert; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.ITypedRegion; +import org.eclipse.jface.text.rules.ICharacterScanner; +import org.eclipse.jface.text.rules.IPartitionTokenScanner; +import org.eclipse.jface.text.rules.IToken; +import org.eclipse.jface.text.rules.Token; /** * */ public class PHPPartitionScanner implements IPartitionTokenScanner { private static final boolean DEBUG = false; + + private boolean fInString = false; + private boolean fInDoubString = false; private IDocument fDocument = null; private int fOffset = -1; private String fContentType = IPHPPartitionScannerConstants.HTML; - private String fPrevContentType; - + private String fPrevContentType = IPHPPartitionScannerConstants.HTML; private boolean partitionBorder = false; private int fTokenOffset; private int fEnd = -1; private int fLength; + private int fCurrentLength; + private int fFileType; private Map tokens = new HashMap(); - public PHPPartitionScanner() { - this.tokens.put( - IPHPPartitionScannerConstants.PHP, - new Token(IPHPPartitionScannerConstants.PHP)); + public PHPPartitionScanner(int fileType) { + this.tokens.put(IPHPPartitionScannerConstants.PHP, new Token(IPHPPartitionScannerConstants.PHP)); this.tokens.put( IPHPPartitionScannerConstants.PHP_MULTILINE_COMMENT, new Token(IPHPPartitionScannerConstants.PHP_MULTILINE_COMMENT)); - this.tokens.put( - IPHPPartitionScannerConstants.HTML, - new Token(IPHPPartitionScannerConstants.HTML)); + this.tokens.put(IPHPPartitionScannerConstants.HTML, new Token(IPHPPartitionScannerConstants.HTML)); this.tokens.put( IPHPPartitionScannerConstants.HTML_MULTILINE_COMMENT, new Token(IPHPPartitionScannerConstants.HTML_MULTILINE_COMMENT)); + + this.tokens.put(IPHPPartitionScannerConstants.SMARTY, new Token(IPHPPartitionScannerConstants.SMARTY)); this.tokens.put( - IDocument.DEFAULT_CONTENT_TYPE, - new Token(IDocument.DEFAULT_CONTENT_TYPE)); + IPHPPartitionScannerConstants.SMARTY_MULTILINE_COMMENT, + new Token(IPHPPartitionScannerConstants.SMARTY_MULTILINE_COMMENT)); + + this.tokens.put(IDocument.DEFAULT_CONTENT_TYPE, new Token(IDocument.DEFAULT_CONTENT_TYPE)); + fFileType = fileType; } private IToken getToken(String type) { - fLength = fOffset - fTokenOffset; + fLength = fCurrentLength; + if (DEBUG) { + + try { + if (fLength <= 0) { + int line = fDocument.getLineOfOffset(fOffset); + System.err.println("Error at " + line + " offset:" + String.valueOf(fOffset - fDocument.getLineOffset(line))); + } + } catch (BadLocationException e) { // should never happen + // TODO Write stacktrace to log + e.printStackTrace(); + } + } + Assert.isTrue(fLength > 0, "Partition length <= 0!"); + fCurrentLength = 0; + // String can never cross partition borders so reset string detection + fInString = false; + fInDoubString = false; IToken token = (IToken) this.tokens.get(type); Assert.isNotNull(token, "Token for type \"" + type + "\" not found!"); if (DEBUG) { - System.out.println( - "Partition: fTokenOffset=" - + fTokenOffset - + " fContentType=" - + fContentType - + " fLength=" - + fLength); + System.out.println("Partition: fTokenOffset=" + fTokenOffset + " fContentType=" + type + " fLength=" + fLength); } return token; } @@ -68,28 +92,30 @@ public class PHPPartitionScanner implements IPartitionTokenScanner { /* (non-Javadoc) * @see org.eclipse.jface.text.rules.IPartitionTokenScanner#setPartialRange(org.eclipse.jface.text.IDocument, int, int, java.lang.String, int) */ - public void setPartialRange( - IDocument document, - int offset, - int length, - String contentType, - int partitionOffset) { - this.setRange(document, offset, length); + public void setPartialRange(IDocument document, int offset, int length, String contentType, int partitionOffset) { if (DEBUG) { - System.out.println( - "PartialRange: contentType=" - + contentType - + " partitionOffset=" - + partitionOffset); + System.out.println("*****"); + System.out.println("PartialRange: contentType=" + contentType + " partitionOffset=" + partitionOffset); } - if (this.tokens.containsKey(contentType)) - fContentType = contentType; - // TODO Calculate previouse contenttype - if (partitionOffset > -1) { - partitionBorder = false; - fTokenOffset = partitionOffset; + try { + if (partitionOffset > -1) { + partitionBorder = false; + // because of strings we have to parse the whole partition + this.setRange(document, partitionOffset, offset - partitionOffset + length); + // sometimes we get a wrong partition so we retrieve the partition + // directly from the document + fContentType = fDocument.getContentType(partitionOffset); + } else + this.setRange(document, offset, length); + + } catch (BadLocationException e) { + // should never happen + // TODO print stack trace to log + // fall back just scan the whole document again + this.setRange(document, 0, fDocument.getLength()); } + } /* (non-Javadoc) @@ -128,14 +154,14 @@ public class PHPPartitionScanner implements IPartitionTokenScanner { while ((c = read()) != ICharacterScanner.EOF) { switch (c) { case '<' : - if (fContentType - != IPHPPartitionScannerConstants.PHP_MULTILINE_COMMENT + if (!isInString(IPHPPartitionScannerConstants.PHP) + && fContentType != IPHPPartitionScannerConstants.PHP_MULTILINE_COMMENT && checkPattern(new char[] { '?', 'p', 'h', 'p' }, true)) { - if (fContentType != IPHPPartitionScannerConstants.PHP - && fOffset - 5 > 0) { - fOffset -= 5; + if (fContentType != IPHPPartitionScannerConstants.PHP && fCurrentLength > 5) { + unread(5); IToken token = getToken(fContentType); // save previouse contenttype + //TODO build stack for previouse contenttype fPrevContentType = fContentType; fContentType = IPHPPartitionScannerConstants.PHP; @@ -146,47 +172,43 @@ public class PHPPartitionScanner implements IPartitionTokenScanner { // remember offset of this partition fTokenOffset = fOffset - 5; - } else if (fContentType != IPHPPartitionScannerConstants.PHP_MULTILINE_COMMENT - && (checkPattern(new char[] { '?', ' ' }, false) - || checkPattern(new char[] { '?', '\r' }, false) - || checkPattern(new char[] { '?', '\n' }, false))) { - if (fContentType != IPHPPartitionScannerConstants.PHP - && fOffset - 3 > 0) { - fOffset -= 3; + fCurrentLength = 5; + } else if ( + !isInString(IPHPPartitionScannerConstants.PHP) + && fContentType != IPHPPartitionScannerConstants.PHP_MULTILINE_COMMENT + && checkPattern(new char[] { '?' }, false)) { + if (fContentType != IPHPPartitionScannerConstants.PHP && fCurrentLength > 2) { + unread(2); IToken token = getToken(fContentType); // save previouse contenttype fPrevContentType = fContentType; - fContentType = IPHPPartitionScannerConstants.PHP; - return token; } else fContentType = IPHPPartitionScannerConstants.PHP; - // remember offset of this partition - fTokenOffset = fOffset - 3; - } else if (checkPattern(new char[] { '!', '-', '-' })) { - // return previouse partition - if (fContentType - != IPHPPartitionScannerConstants.HTML_MULTILINE_COMMENT - && fOffset - 4 > 0) { - fOffset -= 4; + fTokenOffset = fOffset - 2; + fCurrentLength = 2; + } else if ( + !isInString(IPHPPartitionScannerConstants.PHP) + && (fContentType != IPHPPartitionScannerConstants.PHP) // BUG #769044 + && (fContentType != IPHPPartitionScannerConstants.PHP_MULTILINE_COMMENT) // BUG #769044 + && checkPattern(new char[] { '!', '-', '-' })) { // return previouse partition + if (fContentType != IPHPPartitionScannerConstants.HTML_MULTILINE_COMMENT && fCurrentLength > 4) { + unread(4); IToken token = getToken(fContentType); - - fContentType = - IPHPPartitionScannerConstants.HTML_MULTILINE_COMMENT; + fContentType = IPHPPartitionScannerConstants.HTML_MULTILINE_COMMENT; return token; } else - fContentType = - IPHPPartitionScannerConstants.HTML_MULTILINE_COMMENT; + fContentType = IPHPPartitionScannerConstants.HTML_MULTILINE_COMMENT; fTokenOffset = fOffset - 4; + fCurrentLength = 4; } break; case '?' : - if (fContentType == IPHPPartitionScannerConstants.PHP) { + if (!isInString(IPHPPartitionScannerConstants.PHP) && fContentType == IPHPPartitionScannerConstants.PHP) { if ((c = read()) == '>') { - // TODO Actually calculate the previouse contenttype from the document if (fPrevContentType != null) fContentType = fPrevContentType; else @@ -198,59 +220,145 @@ public class PHPPartitionScanner implements IPartitionTokenScanner { } break; case '-' : - if (fContentType - == IPHPPartitionScannerConstants.HTML_MULTILINE_COMMENT + if (!isInString(IPHPPartitionScannerConstants.PHP) + && fContentType == IPHPPartitionScannerConstants.HTML_MULTILINE_COMMENT && checkPattern(new char[] { '-', '>' })) { fContentType = IPHPPartitionScannerConstants.HTML; partitionBorder = true; - return getToken( - IPHPPartitionScannerConstants.HTML_MULTILINE_COMMENT); + return getToken(IPHPPartitionScannerConstants.HTML_MULTILINE_COMMENT); + } + break; + case '{' : // SMARTY code starts here ? + if (fFileType == IPHPPartitionScannerConstants.SMARTY_FILE) { + if ((c = read()) == '*') { + if (DEBUG) { + System.out.println( + "SMARTYDOC_TOKEN start " + + fTokenOffset + + " fContentType=" + + fContentType + + " fLength=" + + fLength + + " fOffset=" + + fOffset + + " fCurrentLength=" + + fCurrentLength); + } + if (fContentType != IPHPPartitionScannerConstants.SMARTY_MULTILINE_COMMENT && fCurrentLength > 2) { + // SMARTY doc code starts here + unread(2); + IToken token = getToken(fContentType); + fContentType = IPHPPartitionScannerConstants.SMARTY_MULTILINE_COMMENT; + return token; + // } else if (fContentType == IPHPPartitionScannerConstants.HTML && fOffset == 2) { + // fContentType = IPHPPartitionScannerConstants.SMARTY_MULTILINE_COMMENT; + } else { // if (fContentType == IPHPPartitionScannerConstants.SMARTY_MULTILINE_COMMENT) { + fContentType = IPHPPartitionScannerConstants.SMARTY_MULTILINE_COMMENT; + fTokenOffset = fOffset - 2; + fCurrentLength = 2; + } + break; + } + if (DEBUG) { + System.out.println( + "SMARTY_TOKEN start " + + fTokenOffset + + " fContentType=" + + fContentType + + " fLength=" + + fLength + + " fOffset=" + + fOffset); + } + unread(); + if (fContentType != IPHPPartitionScannerConstants.SMARTY && fCurrentLength > 1) { + unread(1); + IToken token = getToken(fContentType); + fContentType = IPHPPartitionScannerConstants.SMARTY; + return token; + // } else if (fContentType == IPHPPartitionScannerConstants.HTML && fOffset==1) { + // fContentType = IPHPPartitionScannerConstants.SMARTY; + } else { + fContentType = IPHPPartitionScannerConstants.SMARTY; + fTokenOffset = fOffset - 1; + fCurrentLength = 1; + } + } + break; + case '}' : // SMARTY code ends here ? + if (fFileType == IPHPPartitionScannerConstants.SMARTY_FILE && fContentType == IPHPPartitionScannerConstants.SMARTY) { + if (DEBUG) { + System.out.println( + "SMARTY_TOKEN end " + + fTokenOffset + + " fContentType=" + + fContentType + + " fLength=" + + fLength + + " fOffset=" + + fOffset); + } + fContentType = IPHPPartitionScannerConstants.HTML; + partitionBorder = true; + return getToken(IPHPPartitionScannerConstants.SMARTY); } break; case '/' : - if ((c = read()) == '*') { // MULTINE COMMENT JAVASCRIPT, CSS, PHP - if (fContentType == IPHPPartitionScannerConstants.PHP - && fOffset - 2 > 0) { - fOffset -= 2; + if (!isInString(IPHPPartitionScannerConstants.PHP) && (c = read()) == '*') { // MULTINE COMMENT JAVASCRIPT, CSS, PHP + if (fContentType == IPHPPartitionScannerConstants.PHP && fCurrentLength > 2) { + unread(2); IToken token = getToken(fContentType); - - fContentType = - IPHPPartitionScannerConstants.PHP_MULTILINE_COMMENT; - + fContentType = IPHPPartitionScannerConstants.PHP_MULTILINE_COMMENT; return token; - } else if ( - fContentType - == IPHPPartitionScannerConstants.PHP_MULTILINE_COMMENT) { - + } else if (fContentType == IPHPPartitionScannerConstants.PHP_MULTILINE_COMMENT) { fTokenOffset = fOffset - 2; + fCurrentLength = 2; } - } else if (c != ICharacterScanner.EOF) + } else if (!isInString(IPHPPartitionScannerConstants.PHP) && c != ICharacterScanner.EOF) unread(); break; case '*' : - if ((c = read()) == '/') { - if (fContentType - == IPHPPartitionScannerConstants.PHP_MULTILINE_COMMENT) { + if (!isInString(IPHPPartitionScannerConstants.PHP) && (c = read()) == '/') { + if (fContentType == IPHPPartitionScannerConstants.PHP_MULTILINE_COMMENT) { fContentType = IPHPPartitionScannerConstants.PHP; partitionBorder = true; - - return getToken( - IPHPPartitionScannerConstants.PHP_MULTILINE_COMMENT); - } else if ( - fContentType - == IPHPPartitionScannerConstants.CSS_MULTILINE_COMMENT) { - } else if ( - fContentType - == IPHPPartitionScannerConstants.JS_MULTILINE_COMMENT) { + return getToken(IPHPPartitionScannerConstants.PHP_MULTILINE_COMMENT); + } else if (fContentType == IPHPPartitionScannerConstants.CSS_MULTILINE_COMMENT) { + } else if (fContentType == IPHPPartitionScannerConstants.JS_MULTILINE_COMMENT) { + } + } else if (fFileType == IPHPPartitionScannerConstants.SMARTY_FILE && (c = read()) == '}') { + if (DEBUG) { + System.out.println( + "SMARTYDOC_TOKEN end " + + fTokenOffset + + " fContentType=" + + fContentType + + " fLength=" + + fLength + + " fOffset=" + + fOffset); + } + if (fContentType == IPHPPartitionScannerConstants.SMARTY_MULTILINE_COMMENT) { + fContentType = IPHPPartitionScannerConstants.HTML; + partitionBorder = true; + return getToken(IPHPPartitionScannerConstants.SMARTY_MULTILINE_COMMENT); } - } else if (c != ICharacterScanner.EOF) + } else if (!isInString(IPHPPartitionScannerConstants.PHP) && c != ICharacterScanner.EOF) { unread(); + } + break; + case '\'' : + if (!fInDoubString) + fInString = !fInString; + break; + case '"' : + // toggle String mode + if (!fInString) + fInDoubString = !fInDoubString; break; } - } - - // end of file reached but we have to return the + } // end of file reached but we have to return the // last partition. return getToken(fContentType); } @@ -265,14 +373,19 @@ public class PHPPartitionScanner implements IPartitionTokenScanner { fDocument = document; fOffset = offset; fTokenOffset = offset; + fCurrentLength = 0; fLength = 0; fEnd = fOffset + length; - //partitionBorder = false; + fInString = false; + fInDoubString = false; + fContentType = IPHPPartitionScannerConstants.HTML; + // String[] prev = getPartitionStack(offset); } private int read() { try { if (fOffset < fEnd) { + fCurrentLength++; return fDocument.getChar(fOffset++); } return ICharacterScanner.EOF; @@ -286,6 +399,12 @@ public class PHPPartitionScanner implements IPartitionTokenScanner { private void unread() { --fOffset; + --fCurrentLength; + } + + private void unread(int num) { + fOffset -= num; + fCurrentLength -= num; } private boolean checkPattern(char[] pattern) { @@ -302,12 +421,13 @@ public class PHPPartitionScanner implements IPartitionTokenScanner { */ private boolean checkPattern(char[] pattern, boolean ignoreCase) { int prevOffset = fOffset; + int prevLength = fCurrentLength; for (int i = 0; i < pattern.length; i++) { int c = read(); - if (c == ICharacterScanner.EOF - || !letterEquals(c, pattern[i], ignoreCase)) { + if (c == ICharacterScanner.EOF || !letterEquals(c, pattern[i], ignoreCase)) { fOffset = prevOffset; + fCurrentLength = prevLength; return false; } } @@ -318,18 +438,58 @@ public class PHPPartitionScanner implements IPartitionTokenScanner { private boolean letterEquals(int test, char letter, boolean ignoreCase) { if (test == letter) return true; - else if ( - ignoreCase - && Character.isLowerCase(letter) - && test == Character.toUpperCase(letter)) + else if (ignoreCase && Character.isLowerCase(letter) && test == Character.toUpperCase(letter)) return true; - else if ( - ignoreCase - && Character.isUpperCase(letter) - && test == Character.toLowerCase(letter)) + else if (ignoreCase && Character.isUpperCase(letter) && test == Character.toLowerCase(letter)) return true; return false; } + /** + * Checks wether the offset is in a String and the specified + * contenttype is the current content type. + * Strings are delimited, mutual exclusive, by a " or by a '. + * + * @param contentType The contenttype to check. + * @return true if the current offset is in a string else + * returns false. + */ + private boolean isInString(String contentType) { + if (fContentType == contentType) + return (fInString || fInDoubString); + else + return false; + } + + /** + * Returns the previouse partition stack for the given offset. + * + * @param offset The offset to return the previouse partitionstack for. + * + * @return The stack as a string array. + */ + private String[] getPartitionStack(int offset) { + ArrayList types = new ArrayList(); + int tmpOffset = 0; + try { + ITypedRegion region = fDocument.getPartition(offset); + tmpOffset = region.getOffset(); + while (tmpOffset - 1 > 0) { + region = fDocument.getPartition(tmpOffset - 1); + tmpOffset = region.getOffset(); + types.add(0, region.getType()); + } + } catch (BadLocationException e) { + if (DEBUG) { + e.printStackTrace(); + } + } + + String[] retVal = new String[types.size()]; + + retVal = (String[]) types.toArray(retVal); + return retVal; + } + }