Far better but not finished, it will now get function and class declaration in outline
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpeclipse / phpeditor / php / PHPPartitionScanner.java
1 /**********************************************************************
2 Copyright (c) 2000, 2002 IBM Corp. and others.
3 All rights reserved. This program and the accompanying materials
4 are made available under the terms of the Common Public License v1.0
5 which accompanies this distribution, and is available at
6 http://www.eclipse.org/legal/cpl-v10.html
7
8 Contributors:
9     IBM Corporation - Initial implementation
10     Klaus Hartlage - www.eclipseproject.de
11 **********************************************************************/
12 package net.sourceforge.phpeclipse.phpeditor.php;
13
14 import java.util.ArrayList;
15 import java.util.List;
16
17 import org.eclipse.jface.text.rules.ICharacterScanner;
18 import org.eclipse.jface.text.rules.IPredicateRule;
19 import org.eclipse.jface.text.rules.IToken;
20 import org.eclipse.jface.text.rules.MultiLineRule;
21 import org.eclipse.jface.text.rules.RuleBasedPartitionScanner;
22 import org.eclipse.jface.text.rules.Token;
23
24 /**
25  * This scanner recognizes the JavaDoc comments and Java multi line comments.
26  */
27 public class PHPPartitionScanner extends RuleBasedPartitionScanner {
28
29   private final static String SKIP = "__skip"; //$NON-NLS-1$
30   public final static String HTML_MULTILINE_COMMENT = "__html_multiline_comment"; //$NON-NLS-1$
31   //    public final static String JAVA_DOC= "__java_javadoc"; //$NON-NLS-1$
32   public final static String PHP = "__php"; //$NON-NLS-1$
33   //public final static String HTML = "__html"; //$NON-NLS-1$
34
35   public final static IToken php = new Token(PHP);
36   //public final static IToken html = new Token(HTML);
37   public final static IToken comment = new Token(HTML_MULTILINE_COMMENT);
38
39   protected final static char[] php0EndSequence = { '<', '?' };
40   protected final static char[] php1EndSequence = { '<', '?', 'p', 'h', 'p' };
41   protected final static char[] php2EndSequence = { '<', '?', 'P', 'H', 'P' };
42
43   private StringBuffer test;
44
45   public class PHPMultiLineRule extends MultiLineRule {
46
47     public PHPMultiLineRule(String startSequence, String endSequence, IToken token) {
48       super(startSequence, endSequence, token);
49     }
50
51     public PHPMultiLineRule(String startSequence, String endSequence, IToken token, char escapeCharacter) {
52       super(startSequence, endSequence, token, escapeCharacter);
53     }
54
55     protected boolean endSequenceDetected(ICharacterScanner scanner) {
56       int c;
57       int c2;
58
59       boolean lineCommentMode = false;
60       boolean multiLineCommentMode = false;
61       boolean stringMode = false;
62
63       char[][] delimiters = scanner.getLegalLineDelimiters();
64       while ((c = scanner.read()) != ICharacterScanner.EOF) {
65         if (c == '#') {
66           // read until end of line
67           while ((c = scanner.read()) != ICharacterScanner.EOF) {
68             if (fEndSequence.length > 0 && c == fEndSequence[0]) {
69               // Check if the specified end sequence has been found.
70               if (sequenceDetected(scanner, fEndSequence, true))
71                 return true;
72             } else if (c == '\n') {
73               break;
74             }
75           }
76           continue;
77         } else if (c == '/' && (c = scanner.read()) != ICharacterScanner.EOF) {
78           if (c == '/') {
79             // read until end of line
80             while ((c = scanner.read()) != ICharacterScanner.EOF) {
81               if (fEndSequence.length > 0 && c == fEndSequence[0]) {
82                 // Check if the specified end sequence has been found.
83                 if (sequenceDetected(scanner, fEndSequence, true))
84                   return true;
85               } else if (c == '\n') {
86                 break;
87               }
88             }
89             continue;
90           } else if (c == '*') {
91             // multi-line comment
92             while ((c = scanner.read()) != ICharacterScanner.EOF) {
93               if (c == '*' && (c = scanner.read()) != ICharacterScanner.EOF) {
94                 if (c == '/') {
95                   break;
96                 }
97                 scanner.unread();
98               }
99             }
100
101             continue;
102           } else {
103             scanner.unread();
104           }
105         } else if (c == '"') {
106           // string mode
107           while ((c = scanner.read()) != ICharacterScanner.EOF) {
108             if (c == '\\') {
109               c = scanner.read();
110             } else if (c == '"') {
111               break;
112             }
113           }
114           continue;
115         } else if (c == '\'') {
116           // string mode
117           while ((c = scanner.read()) != ICharacterScanner.EOF) {
118             if (c == '\\') {
119               c = scanner.read();
120             } else if (c == '\'') {
121               break;
122             }
123           }
124           continue;
125         }
126
127         if (c == fEscapeCharacter) {
128           // Skip the escaped character.
129           scanner.read();
130         } else if (fEndSequence.length > 0 && c == fEndSequence[0]) {
131           // Check if the specified end sequence has been found.
132           if (sequenceDetected(scanner, fEndSequence, true))
133             return true;
134         } else if (fBreaksOnEOL) {
135           // Check for end of line since it can be used to terminate the pattern.
136           for (int i = 0; i < delimiters.length; i++) {
137             if (c == delimiters[i][0] && sequenceDetected(scanner, delimiters[i], false))
138               return true;
139           }
140         }
141       } 
142       boolean phpMode = false;
143       if (c == ICharacterScanner.EOF) {
144         phpMode = true;
145       }
146       scanner.unread();
147       return phpMode;
148     }
149   }
150
151   //  public class HTMLMultiLineRule extends MultiLineRule {
152   //
153   //    public HTMLMultiLineRule(String startSequence, String endSequence, IToken token) {
154   //      super(startSequence, endSequence, token);
155   //    }
156   //
157   //    public HTMLMultiLineRule(String startSequence, String endSequence, IToken token, char escapeCharacter) {
158   //      super(startSequence, endSequence, token, escapeCharacter);
159   //    }
160   //
161   //    protected boolean endSequenceDetected(ICharacterScanner scanner) {
162   //      int c;
163   //
164   //      char[][] delimiters = scanner.getLegalLineDelimiters();
165   //      while ((c = scanner.read()) != ICharacterScanner.EOF) {
166   //        if (c == '<') {
167   //          //       scanner.unread();
168   //          if (sequenceDetected(scanner, php2EndSequence, true)) {
169   //            // <?PHP
170   //            scanner.unread();
171   //            scanner.unread();
172   //            scanner.unread();
173   //            scanner.unread();
174   //            scanner.unread();
175   //            return true;
176   //          }
177   //          if (sequenceDetected(scanner, php1EndSequence, true)) {
178   //            // <?php
179   //            scanner.unread();
180   //            scanner.unread();
181   //            scanner.unread();
182   //            scanner.unread();
183   //            scanner.unread();
184   //            return true;
185   //          }
186   //          if (sequenceDetected(scanner, php0EndSequence, true)) {
187   //            // <?
188   //            scanner.unread();
189   //            scanner.unread();
190   //            return true;
191   //          }
192   //          //      scanner.read();
193   //        }
194   //
195   //      }
196   //      scanner.unread();
197   //      return false;
198   //    }
199   //
200   //    protected IToken doEvaluate(ICharacterScanner scanner, boolean resume) {
201   //
202   //      if (resume) {
203   //
204   //        if (endSequenceDetected(scanner))
205   //          return fToken;
206   //
207   //      } else {
208   //
209   //        int c = scanner.read();
210   //        //     if (c == fStartSequence[0]) {
211   //        //       if (sequenceDetected(scanner, fStartSequence, false)) {
212   //        if (endSequenceDetected(scanner))
213   //          return fToken;
214   //        //       }
215   //        //     }
216   //      }
217   //
218   //      scanner.unread();
219   //      return Token.UNDEFINED;
220   //    }
221   //
222   //    public IToken evaluate(ICharacterScanner scanner, boolean resume) {
223   //      if (fColumn == UNDEFINED)
224   //        return doEvaluate(scanner, resume);
225   //
226   //      int c = scanner.read();
227   //      scanner.unread();
228   //      //    if (c == fStartSequence[0])
229   //      return (fColumn == scanner.getColumn() ? doEvaluate(scanner, resume) : Token.UNDEFINED);
230   //      //    else
231   //      //      return Token.UNDEFINED;
232   //    }
233   //  }
234
235 //  public class HTMLPatternRule implements IPredicateRule {
236 //
237 //    protected static final int UNDEFINED = -1;
238 //
239 //    /** The token to be returned on success */
240 //    protected IToken fToken;
241 //
242 //    /** The pattern's column constrain */
243 //    protected int fColumn = UNDEFINED;
244 //    /** The pattern's escape character */
245 //    protected char fEscapeCharacter;
246 //    /** Indicates whether end of line termines the pattern */
247 //    protected boolean fBreaksOnEOL;
248 //
249 //    /**
250 //     * Creates a rule for the given starting and ending sequence.
251 //     * When these sequences are detected the rule will return the specified token.
252 //     * Alternatively, the sequence can also be ended by the end of the line.
253 //     * Any character which follows the given escapeCharacter will be ignored.
254 //     *
255 //     * @param startSequence the pattern's start sequence
256 //     * @param endSequence the pattern's end sequence, <code>null</code> is a legal value
257 //     * @param token the token which will be returned on success
258 //     * @param escapeCharacter any character following this one will be ignored
259 //     * @param indicates whether the end of the line also termines the pattern
260 //     */
261 //    public HTMLPatternRule(IToken token) {
262 //      fToken = token;
263 //      fEscapeCharacter = (char) 0;
264 //      fBreaksOnEOL = false;
265 //    }
266 //
267 //    /**
268 //     * Sets a column constraint for this rule. If set, the rule's token
269 //     * will only be returned if the pattern is detected starting at the 
270 //     * specified column. If the column is smaller then 0, the column
271 //     * constraint is considered removed.
272 //     *
273 //     * @param column the column in which the pattern starts
274 //     */
275 //    public void setColumnConstraint(int column) {
276 //      if (column < 0)
277 //        column = UNDEFINED;
278 //      fColumn = column;
279 //    }
280 //
281 //    /**
282 //     * Evaluates this rules without considering any column constraints.
283 //     *
284 //     * @param scanner the character scanner to be used
285 //     * @return the token resulting from this evaluation
286 //     */
287 //    protected IToken doEvaluate(ICharacterScanner scanner) {
288 //      return doEvaluate(scanner, false);
289 //    }
290 //
291 //    /**
292 //     * Evaluates this rules without considering any column constraints. Resumes
293 //     * detection, i.e. look sonly for the end sequence required by this rule if the
294 //     * <code>resume</code> flag is set.
295 //     *
296 //     * @param scanner the character scanner to be used
297 //     * @param resume <code>true</code> if detection should be resumed, <code>false</code> otherwise
298 //     * @return the token resulting from this evaluation
299 //     * @since 2.0
300 //     */
301 //    protected IToken doEvaluate(ICharacterScanner scanner, boolean resume) {
302 //
303 //      if (resume) {
304 //
305 //        if (endSequenceDetected(scanner))
306 //          return fToken;
307 //
308 //      } else {
309 //
310 //        int c = scanner.read();
311 //        //      if (c == fStartSequence[0]) {
312 //        //        if (sequenceDetected(scanner, fStartSequence, false)) {
313 //        if (endSequenceDetected(scanner))
314 //          return fToken;
315 //        //        }
316 //        //      }
317 //      }
318 //
319 //      scanner.unread();
320 //      return Token.UNDEFINED;
321 //    }
322 //
323 //    /*
324 //     * @see IRule#evaluate
325 //     */
326 //    public IToken evaluate(ICharacterScanner scanner) {
327 //      return evaluate(scanner, false);
328 //    }
329 //
330 //    /**
331 //     * Returns whether the end sequence was detected. As the pattern can be considered 
332 //     * ended by a line delimiter, the result of this method is <code>true</code> if the 
333 //     * rule breaks on the end  of the line, or if the EOF character is read.
334 //     *
335 //     * @param scanner the character scanner to be used
336 //     * @return <code>true</code> if the end sequence has been detected
337 //     */
338 //    protected boolean endSequenceDetected(ICharacterScanner scanner) {
339 //      int c;
340 //
341 //      char[][] delimiters = scanner.getLegalLineDelimiters();
342 //      while ((c = scanner.read()) != ICharacterScanner.EOF) {
343 //        if (c == '<') {
344 //          //       scanner.unread();
345 //          if (sequenceDetected(scanner, php2EndSequence, true)) {
346 //            // <?PHP
347 //            scanner.unread();
348 //            scanner.unread();
349 //            scanner.unread();
350 //            scanner.unread();
351 //            scanner.unread();
352 //            return true;
353 //          }
354 //          if (sequenceDetected(scanner, php1EndSequence, true)) {
355 //            // <?php
356 //            scanner.unread();
357 //            scanner.unread();
358 //            scanner.unread();
359 //            scanner.unread();
360 //            scanner.unread();
361 //            return true;
362 //          }
363 //          if (sequenceDetected(scanner, php0EndSequence, true)) {
364 //            // <?
365 //            scanner.unread();
366 //            scanner.unread();
367 //            return true;
368 //          }
369 //          //      scanner.read();
370 //        }
371 //
372 //      }
373 //      scanner.unread();
374 //      return false;
375 //    }
376 //
377 //    /**
378 //     * Returns whether the next characters to be read by the character scanner
379 //     * are an exact match with the given sequence. No escape characters are allowed 
380 //     * within the sequence. If specified the sequence is considered to be found
381 //     * when reading the EOF character.
382 //     *
383 //     * @param scanner the character scanner to be used
384 //     * @param sequence the sequence to be detected
385 //     * @param eofAllowed indicated whether EOF terminates the pattern
386 //     * @return <code>true</code> if the given sequence has been detected
387 //     */
388 //    protected boolean sequenceDetected(ICharacterScanner scanner, char[] sequence, boolean eofAllowed) {
389 //      for (int i = 1; i < sequence.length; i++) {
390 //        int c = scanner.read();
391 //        if (c == ICharacterScanner.EOF && eofAllowed) {
392 //          return true;
393 //        } else if (c != sequence[i]) {
394 //          // Non-matching character detected, rewind the scanner back to the start.
395 //          scanner.unread();
396 //          for (int j = i - 1; j > 0; j--)
397 //            scanner.unread();
398 //          return false;
399 //        }
400 //      }
401 //
402 //      return true;
403 //    }
404 //
405 //    /*
406 //     * @see IPredicateRule#evaluate(ICharacterScanner, boolean)
407 //     * @since 2.0
408 //     */
409 //    public IToken evaluate(ICharacterScanner scanner, boolean resume) {
410 //      if (fColumn == UNDEFINED)
411 //        return doEvaluate(scanner, resume);
412 //
413 //      int c = scanner.read();
414 //      scanner.unread();
415 //      //    if (c == fStartSequence[0])
416 //      return (fColumn == scanner.getColumn() ? doEvaluate(scanner, resume) : Token.UNDEFINED);
417 //      //    else
418 //      //      return Token.UNDEFINED;
419 //    }
420 //
421 //    /*
422 //     * @see IPredicateRule#getSuccessToken()
423 //     * @since 2.0
424 //     */
425 //    public IToken getSuccessToken() {
426 //      return fToken;
427 //    }
428 //  }
429   /**
430    * Detector for empty comments.
431    */
432 //  static class EmptyCommentDetector implements IWordDetector {
433 //
434 //    /* (non-Javadoc)
435 //    * Method declared on IWordDetector
436 //      */
437 //    public boolean isWordStart(char c) {
438 //      return (c == '/');
439 //    }
440 //
441 //    /* (non-Javadoc)
442 //    * Method declared on IWordDetector
443 //      */
444 //    public boolean isWordPart(char c) {
445 //      return (c == '*' || c == '/');
446 //    }
447 //  };
448
449   /**
450    * 
451    */
452 //  static class WordPredicateRule extends WordRule implements IPredicateRule {
453 //
454 //    private IToken fSuccessToken;
455 //
456 //    public WordPredicateRule(IToken successToken) {
457 //      super(new EmptyCommentDetector());
458 //      fSuccessToken = successToken;
459 //      addWord("/**/", fSuccessToken);
460 //    }
461 //
462 //    /*
463 //     * @see org.eclipse.jface.text.rules.IPredicateRule#evaluate(ICharacterScanner, boolean)
464 //     */
465 //    public IToken evaluate(ICharacterScanner scanner, boolean resume) {
466 //      return super.evaluate(scanner);
467 //    }
468 //
469 //    /*
470 //     * @see org.eclipse.jface.text.rules.IPredicateRule#getSuccessToken()
471 //     */
472 //    public IToken getSuccessToken() {
473 //      return fSuccessToken;
474 //    }
475 //  };
476
477   /**
478    * Creates the partitioner and sets up the appropriate rules.
479    */
480   public PHPPartitionScanner() {
481     super();
482
483     //    IToken php = new Token(PHP);
484     //    IToken html = new Token(HTML);
485     //    IToken comment = new Token(HTML_MULTILINE_COMMENT);
486
487     List rules = new ArrayList();
488
489     // Add rule for single line comments.
490     //  rules.add(new EndOfLineRule("//", Token.UNDEFINED));
491
492     // Add rule for strings and character constants.
493     //          rules.add(new SingleLineRule("\"", "\"", Token.UNDEFINED, '\\'));
494     //  rules.add(new SingleLineRule("'", "'", Token.UNDEFINED, '\\')); 
495
496     // Add special case word rule.
497     //    rules.add(new WordPredicateRule(comment));
498
499     // Add rules for multi-line comments and javadoc.
500     //rules.add(new MultiLineRule("/**", "*/", javaDoc));
501     //  rules.add(new HTMLMultiLineRule("<", "<?", html));
502
503     rules.add(new MultiLineRule("<!--", "-->", comment));
504     rules.add(new PHPMultiLineRule("<?\r", "?>", php));
505     rules.add(new PHPMultiLineRule("<?\n", "?>", php));
506     rules.add(new PHPMultiLineRule("<?\t", "?>", php));
507     rules.add(new PHPMultiLineRule("<? ", "?>", php));
508     rules.add(new PHPMultiLineRule("<?php", "?>", php));
509     rules.add(new PHPMultiLineRule("<?PHP", "?>", php));
510
511     //    rules.add(new HTMLPatternRule(html)); // "<", "<?",
512     //Add rule for processing instructions
513
514     IPredicateRule[] result = new IPredicateRule[rules.size()];
515     rules.toArray(result);
516     setPredicateRules(result);
517    // setDefaultReturnToken(html);
518   }
519
520   //    public IToken nextToken() {
521   //      
522   //      if (fContentType == null || fRules == null)
523   //        return getNextToken();
524   //      
525   //      fTokenOffset= fOffset;
526   //      fColumn= UNDEFINED;
527   //      boolean resume= (fPartitionOffset < fOffset);
528   //          
529   //      IPredicateRule rule;
530   //      IToken token;
531   //      
532   //      for (int i= 0; i < fRules.length; i++) {
533   //        rule= (IPredicateRule) fRules[i];
534   //        token= rule.getSuccessToken();
535   //        if (fContentType.equals(token.getData())) {
536   //          if (resume)
537   //            fTokenOffset= fPartitionOffset;
538   //          token= rule.evaluate(this, resume);
539   //          if (!token.isUndefined()) {
540   //            fContentType= null;
541   //            return token;
542   //          }
543   //        }
544   //      }
545   //      
546   //      fContentType= null;
547   //      return getNextToken();
548   //    }
549   //    
550   //    public IToken getNextToken() {
551   //      
552   //      IToken token;
553   //      
554   //      while (true) {
555   //        
556   //        fTokenOffset= fOffset;
557   //        fColumn= UNDEFINED;
558   //        
559   //        if (fRules != null) {
560   //          for (int i= 0; i < fRules.length; i++) {
561   //            token= (fRules[i].evaluate(this));
562   //            if (!token.isUndefined())
563   //              return token;
564   //          }
565   //        }
566   //        
567   //        if (read() == EOF)
568   //          return Token.EOF;
569   //        else
570   //          return fDefaultReturnToken;
571   //      }
572   //    }
573 }