Small fix to tidy up the run menu. Axel will likely roll this back once he gets more...
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpeclipse / phpeditor / php / PHPCodeScanner.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  www.phpeclipse.de
11  **********************************************************************/
12 package net.sourceforge.phpeclipse.phpeditor.php;
13
14 import java.util.ArrayList;
15 import java.util.List;
16
17 import net.sourceforge.phpdt.internal.ui.text.AbstractJavaScanner;
18 import net.sourceforge.phpdt.ui.text.IColorManager;
19 import net.sourceforge.phpeclipse.IPreferenceConstants;
20 import net.sourceforge.phpeclipse.phpeditor.PHPSyntaxRdr;
21 import net.sourceforge.phpeclipse.phpeditor.util.PHPWhitespaceDetector;
22 import net.sourceforge.phpeclipse.phpeditor.util.PHPWordDetector;
23
24 import org.eclipse.jface.preference.IPreferenceStore;
25 import org.eclipse.jface.text.BadLocationException;
26 import org.eclipse.jface.text.rules.EndOfLineRule;
27 import org.eclipse.jface.text.rules.ICharacterScanner;
28 import org.eclipse.jface.text.rules.IRule;
29 import org.eclipse.jface.text.rules.IToken;
30 import org.eclipse.jface.text.rules.IWordDetector;
31 import org.eclipse.jface.text.rules.MultiLineRule;
32 import org.eclipse.jface.text.rules.Token;
33 import org.eclipse.jface.text.rules.WhitespaceRule;
34 import org.eclipse.jface.text.rules.WordRule;
35
36 /**
37  * PHP Code Scanner
38  */
39 public class PHPCodeScanner extends AbstractJavaScanner {
40
41   /**
42    * Rule to detect java operators.
43    * 
44    * @since 3.0
45    */
46   protected class OperatorRule implements IRule {
47
48     /** Java operators */
49     private final char[] PHP_OPERATORS = {
50         ';',
51         '(',
52         ')',
53         '.',
54         '=',
55         '/',
56         '\\',
57         '+',
58         '-',
59         '*',
60         '[',
61         ']',
62         '<',
63         '>',
64         ':',
65         '?',
66         '!',
67         ',',
68         '|',
69         '&',
70         '^',
71         '%',
72         '~',
73         '@' };
74
75     /** Token to return for this rule */
76     private final IToken fToken;
77
78     /** Token to return for braces */
79     private final IToken fTokenBraces;
80
81     /**
82      * Creates a new operator rule.
83      * 
84      * @param token
85      *          Token to use for this rule
86      */
87     public OperatorRule(IToken token, IToken tokenBraces) {
88       fToken = token;
89       fTokenBraces = tokenBraces;
90
91     }
92
93     /**
94      * Is this character an operator character?
95      * 
96      * @param character
97      *          Character to determine whether it is an operator character
98      * @return <code>true</code> iff the character is an operator, <code>false</code> otherwise.
99      */
100     public boolean isOperator(char character) {
101       for (int index = 0; index < PHP_OPERATORS.length; index++) {
102         if (PHP_OPERATORS[index] == character)
103           return true;
104       }
105       return false;
106     }
107
108     /*
109      * @see org.eclipse.jface.text.rules.IRule#evaluate(org.eclipse.jface.text.rules.ICharacterScanner)
110      */
111     public IToken evaluate(ICharacterScanner scanner) {
112
113       int character = scanner.read();
114       if (character == '{' || character == '}') {
115         return fTokenBraces;
116       }
117       if (isOperator((char) character)) {
118         int lastCharacter = character;
119         character = scanner.read();
120         if (!isOperator((char) character)) {
121           scanner.unread();
122           return fToken;
123         }
124         if (checkPHPTag(scanner, lastCharacter, character)) {
125           return Token.UNDEFINED;
126         }
127         do {
128           lastCharacter = character;
129           character = scanner.read();
130           if (checkPHPTag(scanner, lastCharacter, character)) {
131             return fToken;
132           }
133         } while (isOperator((char) character));
134         scanner.unread();
135         return fToken;
136       } else {
137         scanner.unread();
138         return Token.UNDEFINED;
139       }
140     }
141
142     /**
143      * Check if lastCharacter/character are a PHP start or end token ( &lt;? ... ?&gt; )
144      * 
145      * @param scanner
146      * @param lastCharacter
147      * @param character
148      * @return
149      */
150     private boolean checkPHPTag(ICharacterScanner scanner, int lastCharacter, int character) {
151       if (lastCharacter == '<' && character == '?') {
152         scanner.unread();
153         scanner.unread();
154         return true;
155       } else if (lastCharacter == '?' && character == '>') {
156         scanner.unread();
157         scanner.unread();
158         return true;
159       }
160       return false;
161     }
162   }
163
164
165   protected class AccentStringRule implements IRule {
166
167     /** Token to return for this rule */
168     private final IToken fToken;
169
170     public AccentStringRule(IToken token) {
171       fToken = token;
172
173     }
174
175     /*
176      * @see org.eclipse.jface.text.rules.IRule#evaluate(org.eclipse.jface.text.rules.ICharacterScanner)
177      */
178     public IToken evaluate(ICharacterScanner scanner) {
179
180       int character = scanner.read();
181
182       if (character == '`') {
183
184         while (true) {
185           character = scanner.read();
186           if (character == '\\') {
187             character = scanner.read();
188           } else if (character == '`') {
189             return fToken;
190           }
191         }
192
193       } else {
194         scanner.unread();
195         return Token.UNDEFINED;
196       }
197     }
198
199   }
200
201   private class PHPWordRule extends WordRule {
202     private StringBuffer fBuffer = new StringBuffer();
203
204     public PHPWordRule(IWordDetector detector) {
205       super(detector, Token.UNDEFINED);
206     }
207
208     public PHPWordRule(IWordDetector detector, IToken defaultToken) {
209       super(detector, defaultToken);
210     }
211
212     public IToken evaluate(ICharacterScanner scanner) {
213       int c = scanner.read();
214       boolean isVariable = false;
215       if (c == '<') {
216         c = scanner.read();
217         if (c != '?') {
218           scanner.unread();
219           scanner.unread();
220           return Token.UNDEFINED;
221         } else {
222           c = scanner.read();
223           if (c == '=') { // <?=
224             return getToken(IPreferenceConstants.PHP_TAG);
225           }
226           if (c != 'p' && c != 'P') {
227             scanner.unread();
228             return getToken(IPreferenceConstants.PHP_TAG);
229           } else {
230             c = scanner.read();
231             if (c != 'h' && c != 'H') {
232               scanner.unread();
233               scanner.unread();
234               return getToken(IPreferenceConstants.PHP_TAG);
235             } else {
236               c = scanner.read();
237               if (c != 'p' && c != 'P') {
238                 scanner.unread();
239                 scanner.unread();
240                 scanner.unread();
241                 return getToken(IPreferenceConstants.PHP_TAG);
242               } else {
243                 return getToken(IPreferenceConstants.PHP_TAG);
244               }
245             }
246           }
247         }
248       }
249       if (c == '?') {
250         c = scanner.read();
251         if (c == '>') {
252           return getToken(IPreferenceConstants.PHP_TAG);
253         }
254         scanner.unread();
255         scanner.unread();
256         return Token.UNDEFINED;
257       }
258       if (fDetector.isWordStart((char) c)) {
259         if (c == '$') {
260           isVariable = true;
261         }
262         if (fColumn == UNDEFINED || (fColumn == scanner.getColumn() - 1)) {
263
264           fBuffer.setLength(0);
265           do {
266             fBuffer.append((char) c);
267             c = scanner.read();
268           } while (c != ICharacterScanner.EOF && fDetector.isWordPart((char) c));
269           scanner.unread();
270
271           if (isVariable) {
272             return getToken(IPreferenceConstants.PHP_VARIABLE);
273           }
274           IToken token = (IToken) fWords.get(fBuffer.toString());
275           if (token != null)
276             return token;
277
278           if (fDefaultToken.isUndefined())
279             unreadBuffer(scanner);
280
281           return fDefaultToken;
282         }
283       }
284
285       scanner.unread();
286       return Token.UNDEFINED;
287     }
288   }
289
290   //private PHPColorProvider fColorProvider;
291
292   private static String[] fgTokenProperties = {
293       IPreferenceConstants.PHP_MULTILINE_COMMENT,
294       IPreferenceConstants.PHP_SINGLELINE_COMMENT,
295       IPreferenceConstants.PHP_TAG,
296       IPreferenceConstants.PHP_KEYWORD,
297       IPreferenceConstants.PHP_FUNCTIONNAME,
298       IPreferenceConstants.PHP_VARIABLE,
299       IPreferenceConstants.PHP_STRING_DQ,
300       IPreferenceConstants.PHP_STRING_SQ,
301       IPreferenceConstants.PHP_TYPE,
302       IPreferenceConstants.PHP_CONSTANT,
303       IPreferenceConstants.PHP_DEFAULT,
304       IPreferenceConstants.PHP_OPERATOR,
305       IPreferenceConstants.PHP_BRACE_OPERATOR,
306       IPreferenceConstants.PHP_KEYWORD_RETURN };
307
308   /**
309    * Creates a PHP code scanner
310    */
311   // public PHPCodeScanner(JavaColorManager provider, IPreferenceStore store) {
312   public PHPCodeScanner(IColorManager manager, IPreferenceStore store) {
313     super(manager, store);
314     initialize();
315   }
316
317   /*
318    * @see AbstractJavaScanner#getTokenProperties()
319    */
320   protected String[] getTokenProperties() {
321     return fgTokenProperties;
322   }
323
324   /*
325    * @see AbstractJavaScanner#createRules()
326    */
327   protected List createRules() {
328     List rules = new ArrayList();
329     Token token = getToken(IPreferenceConstants.PHP_SINGLELINE_COMMENT);
330     // Add rule for single line comments.
331     //    rules.add(new EndOfLineRule("//", token)); //$NON-NLS-1$
332     //    rules.add(new EndOfLineRule("#", token)); //$NON-NLS-1$
333     // Add rule for strings and character constants.
334     //    token = getToken(IPreferenceConstants.PHP_STRING_SQ);
335     //    rules.add(new SingleQuoteStringRule(token));
336     //    token = getToken(IPreferenceConstants.PHP_STRING_DQ);
337     //    rules.add(new DoubleQuoteStringRule(token));
338     rules.add(new AccentStringRule(token));
339
340     token = getToken(IPreferenceConstants.PHP_MULTILINE_COMMENT);
341     rules.add(new MultiLineRule("/*", "*/", token)); //$NON-NLS-2$ //$NON-NLS-1$
342     // Add generic whitespace rule.
343     rules.add(new WhitespaceRule(new PHPWhitespaceDetector()));
344     // Add word rule for keywords, types, and constants.
345     token = getToken(IPreferenceConstants.PHP_DEFAULT);
346     PHPWordRule wordRule = new PHPWordRule(new PHPWordDetector(), token);
347
348     Token keyword = getToken(IPreferenceConstants.PHP_KEYWORD);
349     Token functionName = getToken(IPreferenceConstants.PHP_FUNCTIONNAME);
350     Token type = getToken(IPreferenceConstants.PHP_TYPE);
351     Token constant = getToken(IPreferenceConstants.PHP_CONSTANT);
352
353     ArrayList buffer = PHPSyntaxRdr.getSyntaxData();
354     //  String strbuffer = null; unused
355     PHPElement elbuffer = null;
356     String name;
357     for (int i = 0; i < buffer.size(); i++) {
358       //    while ((buffer != null)
359       //      && (!buffer.isEmpty()
360       //        && ((elbuffer = (PHPElement) buffer.remove(0)) != null))) {
361       elbuffer = (PHPElement) buffer.get(i);
362       if (elbuffer instanceof PHPKeyword) {
363         name = ((PHPKeyword) elbuffer).getName();
364         if (!name.equals("return")) {
365           wordRule.addWord(name, keyword);
366         }
367       } else if (elbuffer instanceof PHPFunction) {
368         wordRule.addWord(((PHPFunction) elbuffer).getName(), functionName);
369       } else if (elbuffer instanceof PHPType) {
370         wordRule.addWord(elbuffer.getName(), type);
371       } else if (elbuffer instanceof PHPConstant) {
372         wordRule.addWord(elbuffer.getName(), constant);
373       }
374     }
375
376     // Add word rule for keyword 'return'.
377     token = getToken(IPreferenceConstants.PHP_KEYWORD_RETURN);
378     wordRule.addWord("return", token);
379
380     //  Add rule for operators and brackets (at the end !)
381     rules.add(new OperatorRule(getToken(IPreferenceConstants.PHP_OPERATOR), getToken(IPreferenceConstants.PHP_BRACE_OPERATOR)));
382
383     rules.add(wordRule);
384
385     setDefaultReturnToken(getToken(IPreferenceConstants.PHP_DEFAULT));
386     return rules;
387   }
388 }