detect uppercase *.PHP extension
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpeclipse / phpeditor / php / PHPAutoIndentStrategy.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 org.eclipse.jface.text.BadLocationException;
15 import org.eclipse.jface.text.DefaultAutoIndentStrategy;
16 import org.eclipse.jface.text.DocumentCommand;
17 import org.eclipse.jface.text.IDocument;
18
19 /**
20  * Auto indent strategy sensitive to brackets.
21  */
22 public class PHPAutoIndentStrategy extends DefaultAutoIndentStrategy {
23
24         public PHPAutoIndentStrategy() {
25         }
26         
27         /* (non-Javadoc)
28          * Method declared on IAutoIndentStrategy
29          */
30         public void customizeDocumentCommand(IDocument d, DocumentCommand c) {
31                 if (c.length == 0 && c.text != null && endsWithDelimiter(d, c.text))
32                         smartIndentAfterNewLine(d, c);
33                 else if ("}".equals(c.text)) {  
34                         smartInsertAfterBracket(d, c);
35                 }
36         }
37         
38         /**
39          * Returns whether or not the text ends with one of the given search strings.
40          */
41         private boolean endsWithDelimiter(IDocument d, String txt) {
42
43                 String[] delimiters= d.getLegalLineDelimiters();
44
45                 for (int i= 0; i < delimiters.length; i++) {
46                         if (txt.endsWith(delimiters[i]))
47                                 return true;
48                 }
49
50                 return false;
51         }
52         
53         /**
54          * Returns the line number of the next bracket after end.
55          * @returns the line number of the next matching bracket after end
56          * @param document - the document being parsed
57          * @param line - the line to start searching back from
58          * @param end - the end position to search back from
59          * @param closingBracketIncrease - the number of brackets to skip
60          */
61          protected int findMatchingOpenBracket(IDocument document, int line, int end, int closingBracketIncrease) throws BadLocationException {
62
63                 int start= document.getLineOffset(line);
64                 int brackcount= getBracketCount(document, start, end, false) - closingBracketIncrease;
65
66                 // sum up the brackets counts of each line (closing brackets count negative, 
67                 // opening positive) until we find a line the brings the count to zero
68                 while (brackcount < 0) {
69                         line--;
70                         if (line < 0) {
71                                 return -1;
72                         }
73                         start= document.getLineOffset(line);
74                         end= start + document.getLineLength(line) - 1;
75                         brackcount += getBracketCount(document, start, end, false);
76                 }
77                 return line;
78         }
79         
80         /**
81          * Returns the bracket value of a section of text. Closing brackets have a value of -1 and 
82          * open brackets have a value of 1.
83          * @returns the line number of the next matching bracket after end
84          * @param document - the document being parsed
85          * @param start - the start position for the search
86          * @param end - the end position for the search
87          * @param ignoreCloseBrackets - whether or not to ignore closing brackets in the count
88          */
89          private int getBracketCount(IDocument document, int start, int end, boolean ignoreCloseBrackets) throws BadLocationException {
90
91                 int begin = start;
92                 int bracketcount= 0;
93                 while (begin < end) {
94                         char curr= document.getChar(begin);
95                         begin++;
96                         switch (curr) {
97                                 case '/' :
98                                         if (begin < end) {
99                                                 char next= document.getChar(begin);
100                                                 if (next == '*') {
101                                                         // a comment starts, advance to the comment end
102                                                         begin= getCommentEnd(document, begin + 1, end);
103                                                 } else if (next == '/') {
104                                                         // '//'-comment: nothing to do anymore on this line 
105                                                         begin= end;
106                                                 }
107                                         }
108                                         break;
109                                 case '*' :
110                                         if (begin < end) {
111                                                 char next= document.getChar(begin);
112                                                 if (next == '/') {
113                                                         // we have been in a comment: forget what we read before
114                                                         bracketcount= 0;
115                                                         begin++;
116                                                 }
117                                         }
118                                         break;
119                                 case '{' :
120                                         bracketcount++;
121                                         ignoreCloseBrackets= false;
122                                         break;
123                                 case '}' :
124                                         if (!ignoreCloseBrackets) {
125                                                 bracketcount--;
126                                         }
127                                         break;
128                                 case '"' :
129                                 case '\'' :
130                                         begin= getStringEnd(document, begin, end, curr);
131                                         break;
132                                 default :
133                                         }
134                 }
135                 return bracketcount;
136         }
137         
138         /**
139          * Returns the end position a comment starting at pos.
140          * @returns the end position a comment starting at pos
141          * @param document - the document being parsed
142          * @param position - the start position for the search
143          * @param end - the end position for the search
144          */
145          private int getCommentEnd(IDocument document, int position, int end) throws BadLocationException {
146                 int currentPosition = position;
147                 while (currentPosition < end) {
148                         char curr= document.getChar(currentPosition);
149                         currentPosition++;
150                         if (curr == '*') {
151                                 if (currentPosition < end && document.getChar(currentPosition) == '/') {
152                                         return currentPosition + 1;
153                                 }
154                         }
155                 }
156                 return end;
157         }
158         
159         /**
160          * Returns the String at line with the leading whitespace removed.
161          * @returns the String at line with the leading whitespace removed.
162          * @param document - the document being parsed
163          * @param line - the line being searched
164          */
165          protected String getIndentOfLine(IDocument document, int line) throws BadLocationException {
166                 if (line > -1) {
167                         int start= document.getLineOffset(line);
168                         int end= start + document.getLineLength(line) - 1;
169                         int whiteend= findEndOfWhiteSpace(document, start, end);
170                         return document.get(start, whiteend - start);
171                 } else {
172                         return ""; //$NON-NLS-1$
173                 }
174         }
175         
176         /**
177          * Returns the position of the character in the document after position.
178          * @returns the next location of character.
179          * @param document - the document being parsed
180          * @param position - the position to start searching from
181          * @param end - the end of the document
182          * @param character - the character you are trying to match
183          */
184          private int getStringEnd(IDocument document, int position, int end, char character) throws BadLocationException {
185                 int currentPosition = position;
186                 while (currentPosition < end) {
187                         char currentCharacter= document.getChar(currentPosition);
188                         currentPosition++;
189                         if (currentCharacter == '\\') {
190                                 // ignore escaped characters
191                                 currentPosition++;
192                         } else if (currentCharacter == character) {
193                                 return currentPosition;
194                         }
195                 }
196                 return end;
197         }
198         
199         /**
200          * Set the indent of a new line based on the command provided in the supplied document.
201          * @param document - the document being parsed
202          * @param command - the command being performed
203          */
204          protected void smartIndentAfterNewLine(IDocument document, DocumentCommand command) {
205
206                 int docLength= document.getLength();
207                 if (command.offset == -1 || docLength == 0)
208                         return;
209
210                 try {
211                         int p= (command.offset == docLength ? command.offset - 1 : command.offset);
212                         int line= document.getLineOfOffset(p);
213
214                         StringBuffer buf= new StringBuffer(command.text);
215                         if (command.offset < docLength && document.getChar(command.offset) == '}') {
216                                 int indLine= findMatchingOpenBracket(document, line, command.offset, 0);
217                                 if (indLine == -1) {
218                                         indLine= line;
219                                 }
220                                 buf.append(getIndentOfLine(document, indLine));
221                         } else {
222                                 int start= document.getLineOffset(line);
223                                 int whiteend= findEndOfWhiteSpace(document, start, command.offset);
224                                 buf.append(document.get(start, whiteend - start));
225                                 if (getBracketCount(document, start, command.offset, true) > 0) {
226                                         buf.append('\t');
227                                 }
228                         }
229                         command.text= buf.toString();
230
231                 } catch (BadLocationException excp) {
232                         System.out.println(PHPEditorMessages.getString("AutoIndent.error.bad_location_1")); //$NON-NLS-1$
233                 }
234         }
235         
236         /**
237          * Set the indent of a bracket based on the command provided in the supplied document.
238          * @param document - the document being parsed
239          * @param command - the command being performed
240          */
241          protected void smartInsertAfterBracket(IDocument document, DocumentCommand command) {
242                 if (command.offset == -1 || document.getLength() == 0)
243                         return;
244
245                 try {
246                         int p= (command.offset == document.getLength() ? command.offset - 1 : command.offset);
247                         int line= document.getLineOfOffset(p);
248                         int start= document.getLineOffset(line);
249                         int whiteend= findEndOfWhiteSpace(document, start, command.offset);
250
251                         // shift only when line does not contain any text up to the closing bracket
252                         if (whiteend == command.offset) {
253                                 // evaluate the line with the opening bracket that matches out closing bracket
254                                 int indLine= findMatchingOpenBracket(document, line, command.offset, 1);
255                                 if (indLine != -1 && indLine != line) {
256                                         // take the indent of the found line
257                                         StringBuffer replaceText= new StringBuffer(getIndentOfLine(document, indLine));
258                                         // add the rest of the current line including the just added close bracket
259                                         replaceText.append(document.get(whiteend, command.offset - whiteend));
260                                         replaceText.append(command.text);
261                                         // modify document command
262                                         command.length= command.offset - start;
263                                         command.offset= start;
264                                         command.text= replaceText.toString();
265                                 }
266                         }
267                 } catch (BadLocationException excp) {
268                         System.out.println(PHPEditorMessages.getString("AutoIndent.error.bad_location_2")); //$NON-NLS-1$
269                 }
270         }
271 }