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
 
   9     IBM Corporation - Initial implementation
 
  11 **********************************************************************/
 
  12 package net.sourceforge.phpeclipse.phpeditor.php;
 
  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;
 
  20  * Auto indent strategy sensitive to brackets.
 
  22 public class PHPAutoIndentStrategy extends DefaultAutoIndentStrategy {
 
  24         public PHPAutoIndentStrategy() {
 
  28          * Method declared on IAutoIndentStrategy
 
  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);
 
  39          * Returns whether or not the text ends with one of the given search strings.
 
  41         private boolean endsWithDelimiter(IDocument d, String txt) {
 
  43                 String[] delimiters= d.getLegalLineDelimiters();
 
  45                 for (int i= 0; i < delimiters.length; i++) {
 
  46                         if (txt.endsWith(delimiters[i]))
 
  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
 
  61          protected int findMatchingOpenBracket(IDocument document, int line, int end, int closingBracketIncrease) throws BadLocationException {
 
  63                 int start= document.getLineOffset(line);
 
  64                 int brackcount= getBracketCount(document, start, end, false) - closingBracketIncrease;
 
  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) {
 
  73                         start= document.getLineOffset(line);
 
  74                         end= start + document.getLineLength(line) - 1;
 
  75                         brackcount += getBracketCount(document, start, end, false);
 
  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
 
  89          private int getBracketCount(IDocument document, int start, int end, boolean ignoreCloseBrackets) throws BadLocationException {
 
  94                         char curr= document.getChar(begin);
 
  99                                                 char next= document.getChar(begin);
 
 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 
 
 111                                                 char next= document.getChar(begin);
 
 113                                                         // we have been in a comment: forget what we read before
 
 121                                         ignoreCloseBrackets= false;
 
 124                                         if (!ignoreCloseBrackets) {
 
 130                                         begin= getStringEnd(document, begin, end, curr);
 
 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
 
 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);
 
 151                                 if (currentPosition < end && document.getChar(currentPosition) == '/') {
 
 152                                         return currentPosition + 1;
 
 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
 
 165          protected String getIndentOfLine(IDocument document, int line) throws BadLocationException {
 
 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);
 
 172                         return ""; //$NON-NLS-1$
 
 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
 
 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);
 
 189                         if (currentCharacter == '\\') {
 
 190                                 // ignore escaped characters
 
 192                         } else if (currentCharacter == character) {
 
 193                                 return currentPosition;
 
 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
 
 204          protected void smartIndentAfterNewLine(IDocument document, DocumentCommand command) {
 
 206                 int docLength= document.getLength();
 
 207                 if (command.offset == -1 || docLength == 0)
 
 211                         int p= (command.offset == docLength ? command.offset - 1 : command.offset);
 
 212                         int line= document.getLineOfOffset(p);
 
 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);
 
 220                                 buf.append(getIndentOfLine(document, indLine));
 
 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) {
 
 229                         command.text= buf.toString();
 
 231                 } catch (BadLocationException excp) {
 
 232                         System.out.println(PHPEditorMessages.getString("AutoIndent.error.bad_location_1")); //$NON-NLS-1$
 
 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
 
 241          protected void smartInsertAfterBracket(IDocument document, DocumentCommand command) {
 
 242                 if (command.offset == -1 || document.getLength() == 0)
 
 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);
 
 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();
 
 267                 } catch (BadLocationException excp) {
 
 268                         System.out.println(PHPEditorMessages.getString("AutoIndent.error.bad_location_2")); //$NON-NLS-1$