X-Git-Url: http://git.phpeclipse.com diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/text/PHPCodeReader.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/text/PHPCodeReader.java index 1b16749..96d65c8 100644 --- a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/text/PHPCodeReader.java +++ b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/text/PHPCodeReader.java @@ -13,239 +13,318 @@ import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; /** - * Reads from a document either forwards or backwards. May be configured to skip comments and strings. + * Reads from a document either forwards or backwards. May be configured to skip + * comments and strings. */ public class PHPCodeReader extends SingleCharReader { - /** The EOF character */ - public static final int EOF = -1; - - private boolean fSkipComments = false; - - private boolean fSkipStrings = false; - - private boolean fForward = false; - - private IDocument fDocument; - - private int fOffset; - - private int fEnd = -1; - - private int fCachedLineNumber = -1; - - private int fCachedLineOffset = -1; - - public PHPCodeReader() { - } - - /** - * Returns the offset of the last read character. Should only be called after read has been called. - */ - public int getOffset() { - return fForward ? fOffset - 1 : fOffset; - } - - public void configureForwardReader(IDocument document, int offset, int length, boolean skipComments, boolean skipStrings) - throws IOException { - fDocument = document; - fOffset = offset; - fSkipComments = skipComments; - fSkipStrings = skipStrings; - - fForward = true; - fEnd = Math.min(fDocument.getLength(), fOffset + length); - } - - public void configureBackwardReader(IDocument document, int offset, boolean skipComments, boolean skipStrings) throws IOException { - fDocument = document; - fOffset = offset; - fSkipComments = skipComments; - fSkipStrings = skipStrings; - - fForward = false; - try { - fCachedLineNumber = fDocument.getLineOfOffset(fOffset); - } catch (BadLocationException x) { - throw new IOException(x.getMessage()); - } - } - - /* - * @see Reader#close() - */ - public void close() throws IOException { - fDocument = null; - } - - /* - * @see SingleCharReader#read() - */ - public int read() throws IOException { - try { - return fForward ? readForwards() : readBackwards(); - } catch (BadLocationException x) { - throw new IOException(x.getMessage()); - } - } - - private void gotoCommentEnd() throws BadLocationException { - while (fOffset < fEnd) { - char current = fDocument.getChar(fOffset++); - if (current == '*') { - if (fOffset < fEnd && fDocument.getChar(fOffset) == '/') { - ++fOffset; - return; - } - } - } - } - - private void gotoStringEnd(char delimiter) throws BadLocationException { - while (fOffset < fEnd) { - char current = fDocument.getChar(fOffset++); - if (current == '\\') { - // ignore escaped characters - ++fOffset; - } else if (current == delimiter) { - return; - } - } - } - - private void gotoLineEnd() throws BadLocationException { - int line = fDocument.getLineOfOffset(fOffset); - fOffset = fDocument.getLineOffset(line + 1); - } - - private int readForwards() throws BadLocationException { - while (fOffset < fEnd) { - char current = fDocument.getChar(fOffset++); - - switch (current) { - case '"': - case '\'': - - if (fSkipStrings) { - gotoStringEnd(current); - continue; - } - - return current; - case '#': - - if (fSkipComments && fOffset < fEnd) { - gotoLineEnd(); - continue; - } - - return current; - - case '/': - - if (fSkipComments && fOffset < fEnd) { - char next = fDocument.getChar(fOffset); - if (next == '*') { - // a comment starts, advance to the comment end - ++fOffset; - gotoCommentEnd(); - continue; - } else if (next == '/') { - // '//'-comment starts, advance to the line end - gotoLineEnd(); - continue; - } - } - - return current; - - } - - return current; - } - - return EOF; - } - - private void handleSingleLineComment() throws BadLocationException { - int line = fDocument.getLineOfOffset(fOffset); - if (line < fCachedLineNumber) { - fCachedLineNumber = line; - fCachedLineOffset = fDocument.getLineOffset(line); - int offset = fOffset; - while (fCachedLineOffset < offset) { - char current = fDocument.getChar(offset--); - - if (current == '/' && fCachedLineOffset <= offset && fDocument.getChar(offset) == '/') { - fOffset = offset; - return; - } - - if (current == '#' && fCachedLineOffset <= offset) { - fOffset = offset; - return; - } - } - } - } - - private void gotoCommentStart() throws BadLocationException { - while (0 < fOffset) { - char current = fDocument.getChar(fOffset--); - if (current == '*' && 0 <= fOffset && fDocument.getChar(fOffset) == '/') - return; - } - } - - private void gotoStringStart(char delimiter) throws BadLocationException { - while (0 < fOffset) { - char current = fDocument.getChar(fOffset); - if (current == delimiter) { - if (!(0 <= fOffset && fDocument.getChar(fOffset - 1) == '\\')) - return; - } - --fOffset; - } - } - - private int readBackwards() throws BadLocationException { - - while (0 < fOffset) { - --fOffset; - - handleSingleLineComment(); - - char current = fDocument.getChar(fOffset); - switch (current) { - case '/': - - if (fSkipComments && fOffset > 1) { - char next = fDocument.getChar(fOffset - 1); - if (next == '*') { - // a comment ends, advance to the comment start - fOffset -= 2; - gotoCommentStart(); - continue; - } - } - - return current; - case '"': - case '\'': - - if (fSkipStrings) { - --fOffset; - gotoStringStart(current); - continue; - } - - return current; - } - - return current; - } - - return EOF; - } + /** The EOF character */ + public static final int EOF = -1; + private boolean fSkipComments = false; + private boolean fSkipStrings = false; + private boolean fForward = false; + + private IDocument fDocument; + + private int fOffset; // The current text position within the editor's text + private int fEnd = -1; + private int fCachedLineNumber = -1; // Holds the last line we checked for single line comments + + public PHPCodeReader() { + } + + /** + * Returns the offset of the last read character. Should only be called + * after read has been called. + */ + public int getOffset() { + return fForward ? fOffset - 1 : fOffset; + } + + public void configureForwardReader(IDocument document, int offset, + int length, boolean skipComments, boolean skipStrings) + throws IOException { + fDocument = document; + fOffset = offset; + fSkipComments = skipComments; + fSkipStrings = skipStrings; + + fForward = true; + fEnd = Math.min(fDocument.getLength(), fOffset + length); + } + + public void configureBackwardReader(IDocument document, int offset, + boolean skipComments, boolean skipStrings) throws IOException { + fDocument = document; + fOffset = offset; + fSkipComments = skipComments; + fSkipStrings = skipStrings; + + fForward = false; + try { + fCachedLineNumber = fDocument.getLineOfOffset(fOffset); + } catch (BadLocationException x) { + throw new IOException(x.getMessage()); + } + } + + /* + * @see Reader#close() + */ + public void close() throws IOException { + fDocument = null; + } + + /* + * @see SingleCharReader#read() + */ + public int read() throws IOException { + try { + return fForward ? readForwards() : readBackwards(); + } catch (BadLocationException x) { + throw new IOException(x.getMessage()); + } + } + + private int gotoCommentEnd (int nTextPos) throws BadLocationException { + while (nTextPos < fEnd) { + char current = fDocument.getChar (nTextPos++); + + if (current == '*') { + if ((nTextPos < fEnd) && + (fDocument.getChar (nTextPos) == '/')) { + ++nTextPos; + + return nTextPos; + } + } + } + + return nTextPos; + } + + /** + * + * @param delimiter + * @throws BadLocationException + */ + private int gotoStringEnd (int nTextPos, char delimiter) throws BadLocationException { + while (nTextPos < fEnd) { // If long as we are not at the end of text + char current = fDocument.getChar (nTextPos++); + + if (current == '\\') { // ignore escaped characters + ++nTextPos; + } + else if (current == delimiter) { + return nTextPos; + } + } + + return nTextPos; // End position + } + + /** + * + * @param nTextPos The current text position + * + * @return The position of the start of next line + * + * @throws BadLocationException + */ + private int gotoLineEnd (int nTextPos) throws BadLocationException { + int line = fDocument.getLineOfOffset (nTextPos); // Get the line number of the current text position + + return fDocument.getLineOffset (line + 1); + } + + private int readForwards () throws BadLocationException { + while (fOffset < fEnd) { + char current = fDocument.getChar(fOffset++); + + switch (current) { + case '"': + case '\'': + if (fSkipStrings) { + fOffset = gotoStringEnd (fOffset, current); + continue; + } + return current; + + case '#': + if (fSkipComments && fOffset < fEnd) { + fOffset = gotoLineEnd (fOffset); + continue; + } + return current; + + case '/': + if (fSkipComments && fOffset < fEnd) { + char next = fDocument.getChar(fOffset); + + if (next == '*') { // A comment starts, advance to the comment end + ++fOffset; + fOffset = gotoCommentEnd (fOffset); + continue; + } else if (next == '/') { // '//'-comment starts, advance to the line end + fOffset = gotoLineEnd (fOffset); + continue; + } + } + return current; + } + + return current; + } + + return EOF; + } + + /** + * Check whether the current line contains a single line comment. + * If it contains a single line comment (// or #), than set fOffset (the global character index) + * to the character just before the single line comment. + * + * @throws BadLocationException + */ + + private void handleSingleLineComment () throws BadLocationException { + char current; + int line = fDocument.getLineOfOffset (fOffset); // Get the line number which belongs to the current text position fOffset + + if (line < fCachedLineNumber) { // If we didn't parse this line already + fCachedLineNumber = line; // Remember this line for the next time (so we don't search again!) + int nOffset = fDocument.getLineOffset (line); // Get the start position of current line + + while (nOffset < fOffset) { // As long as the text position is within the current line + current = fDocument.getChar (nOffset); // Get the character from the current text position + + switch (current) { + case '/': + if (fDocument.getChar (nOffset + 1) == '/') { // If the following character is '/' + fOffset = nOffset - 1; + + return; + } + break; + + case '#': + fOffset = nOffset - 1; + return; + + case '"': // It's a string start quote + case '\'': + nOffset++; // Set to next character + + while (nOffset < fOffset) { // As long as we are within the same line + char cChar = fDocument.getChar (nOffset++); + + if (cChar == '\\') { // Ignore escaped characters + ++nOffset; + } + else if (cChar == current) { // If end of string found + break; + } + } + break; + } + + nOffset++; // Go for the next character + } + } + } + + /** + * We search the for the block comment start sequence "/*" + * + * The returned value points to the '/' + * + * @throws BadLocationException + * + * @return The new text position + */ + + private int gotoCommentStart (int nTextPos) throws BadLocationException { + while (0 < nTextPos) { // As long as we are not at the start of the editor text + char current = fDocument.getChar (nTextPos--); // Get the character from the current text position + + if ((current == '*') && // If current character is a '*' + (0 <= nTextPos) && // and if we are not yet at the start of the editor text + (fDocument.getChar (nTextPos) == '/')) { // and the previous character is a '/', + return nTextPos; // We found the block comment start "/*" + } + } + + return nTextPos; + } + + /** + * The string closing quote has been found, when reading the editor text backwards. + * So we have to search for the start of the string + * + * The returned value points to '"' or ''' + * + * @param delimiter The string double quote '"' or single ''' quote we search for + * + * @throws BadLocationException + */ + private int gotoStringStart (int nTextPos, char delimiter) throws BadLocationException { + while (0 < nTextPos) { // As long as we are not at the start of the editor text + char current = fDocument.getChar (nTextPos); // Get the character from the current text position + + if (current == delimiter) { // If we found the same character ('"' or ''') again, we have found the string start + if (!(0 <= nTextPos && + fDocument.getChar (nTextPos - 1) == '\\')) // If the character before the string quote is not an '/' + return nTextPos; // we found the string start + } + + --nTextPos; // Go one character back + } + + return nTextPos; + } + + /** + * Read the editor text backwards + * + */ + + private int readBackwards() throws BadLocationException { + char current; // The character on position fOffset + + while (0 < fOffset) { // As long as we are not at the beginning of the editor text + --fOffset; // Step back one character + + handleSingleLineComment (); // Search for a single line comment within the current line + // If there is a single line comment, fOffset is set to position just before the single line comment + + current = fDocument.getChar (fOffset); // Read the current character + switch (current) { // Process the current character + case '/': // Is it a single line comment? + if (fSkipComments && // When comments should be skipped when parsing + fOffset > 1) { // and if there is indeed an additional character before that '/' + char prev = fDocument.getChar (fOffset - 1); // Look at the character in front of the '/' + + if (prev == '*') { // If '*' than a block comment ends here, advance to the comment start + fOffset -= 2; // Set character pointer to character before "*/" + fOffset = gotoCommentStart (fOffset); // and search for the starting "/*" of the block comment + continue; + } + } + return current; // No block comment end, so return the current character + + case '"': // Is it a string double quote closing '"'? + case '\'': // or a string single quote closing '''? + if (fSkipStrings) { // If we should skip strings when parsing + --fOffset; // Set pointer one before the string closing quote + fOffset = gotoStringStart (fOffset, current);// and search the start of string ('"' or ''') + continue; + } + return current; // No string skip, so return the current character + } + + return current; // No string and no comment + } + + return EOF; // When the start of the text has been found + } } -