1) Fixed issue #764: Ignore strings when looking for single line comments.
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / ui / text / PHPCodeReader.java
index a989296..96d65c8 100644 (file)
@@ -7,73 +7,74 @@ package net.sourceforge.phpdt.internal.ui.text;
 
 import java.io.IOException;
 
+import net.sourceforge.phpdt.internal.corext.phpdoc.SingleCharReader;
+
 import org.eclipse.jface.text.BadLocationException;
 import org.eclipse.jface.text.IDocument;
 
-import net.sourceforge.phpdt.internal.corext.phpdoc.SingleCharReader;
-
 /**
- * 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;
+       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;
-       
        
+       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.
+        * 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;
+               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 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;
+
+       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);
+                       fCachedLineNumber = fDocument.getLineOfOffset(fOffset);
                } catch (BadLocationException x) {
                        throw new IOException(x.getMessage());
                }
        }
-       
+
        /*
         * @see Reader#close()
         */
        public void close() throws IOException {
-               fDocument= null;
+               fDocument = null;
        }
-       
+
        /*
         * @see SingleCharReader#read()
         */
@@ -84,163 +85,246 @@ public class PHPCodeReader extends SingleCharReader {
                        throw new IOException(x.getMessage());
                }
        }
-       
-       private void gotoCommentEnd() throws BadLocationException {
-               while (fOffset < fEnd) {
-                       char current= fDocument.getChar(fOffset++);
+
+       private int gotoCommentEnd (int nTextPos) throws BadLocationException {
+               while (nTextPos < fEnd) {
+                       char current = fDocument.getChar (nTextPos++);
+                       
                        if (current == '*') {
-                               if (fOffset < fEnd && fDocument.getChar(fOffset) == '/') {
-                                       ++ fOffset;
-                                       return;
+                               if ((nTextPos < fEnd) && 
+                                   (fDocument.getChar (nTextPos) == '/')) {
+                                       ++nTextPos;
+                                       
+                                       return nTextPos;
                                }
                        }
                }
+               
+               return nTextPos;
        }
-       
-       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;
+
+       /**
+        * 
+        * @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
        }
-       
-       private void gotoLineEnd() throws BadLocationException {
-               int line= fDocument.getLineOfOffset(fOffset);
-               fOffset= fDocument.getLineOffset(line + 1);
+
+       /**
+        * 
+        * @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 {
+
+       private int readForwards () throws BadLocationException {
                while (fOffset < fEnd) {
-                       char current= fDocument.getChar(fOffset++);
-                       
+                       char current = fDocument.getChar(fOffset++);
+
                        switch (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;
-                                       
-                               case '"':
-                               case '\'':
+                           case '"':
+                           case '\'':
+                               if (fSkipStrings) {
+                                   fOffset = gotoStringEnd (fOffset, current);
+                                   continue;
+                               }
+                               return current;
                                
-                                       if (fSkipStrings) {
-                                               gotoStringEnd(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 {
-               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;
+       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;
                                }
-        if (current == '#' && fCachedLineOffset <= offset) {
-          fOffset= offset;
-          return;
-        }
+                               
+                               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 void gotoCommentStart() throws BadLocationException {
-               while (0 < fOffset) {
-                       char current= fDocument.getChar(fOffset--);
-                       if (current == '*' && 0 <= fOffset && fDocument.getChar(fOffset) == '/')
-                               return;
+       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;
        }
-       
-       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;
+
+       /**
+        * 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
                        }
-                       -- fOffset;
+                                                                           
+                       --nTextPos;                                          // Go one character back
                }
-       }
                
+               return nTextPos;
+       }
+
+       /**
+        * Read the editor text backwards
+        * 
+        */
+       
        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;
+           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;
+
+                       return current;                                          // No string and no comment
                }
-               
-               return EOF;
+
+               return EOF;                                                  // When the start of the text has been found
        }
 }
-