1 /******************************************************************************* 2 * Copyright (c) 2000, 2004 IBM Corporation 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 API and implementation 10 *******************************************************************************/ 11 12 package net.sourceforge.phpdt.internal.ui.text.phpdoc; 13 14 import java.text.BreakIterator; 15 16 import net.sourceforge.phpdt.core.ICompilationUnit; 17 import net.sourceforge.phpdt.core.IJavaElement; 18 import net.sourceforge.phpdt.core.IMethod; 19 import net.sourceforge.phpdt.core.ISourceRange; 20 import net.sourceforge.phpdt.core.IType; 21 import net.sourceforge.phpdt.internal.corext.util.Strings; 22 import net.sourceforge.phpdt.ui.CodeGeneration; 23 import net.sourceforge.phpdt.ui.IWorkingCopyManager; 24 import net.sourceforge.phpdt.ui.PreferenceConstants; 25 import net.sourceforge.phpeclipse.PHPeclipsePlugin; 26 27 import org.eclipse.core.runtime.CoreException; 28 import org.eclipse.core.runtime.Preferences; 29 import org.eclipse.jface.preference.IPreferenceStore; 30 import org.eclipse.jface.text.BadLocationException; 31 import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; 32 import org.eclipse.jface.text.DocumentCommand; 33 import org.eclipse.jface.text.IDocument; 34 import org.eclipse.jface.text.IRegion; 35 import org.eclipse.jface.text.ITypedRegion; 36 import org.eclipse.jface.text.TextUtilities; 37 import org.eclipse.ui.IEditorPart; 38 import org.eclipse.ui.IWorkbenchPage; 39 import org.eclipse.ui.IWorkbenchWindow; 40 import org.eclipse.ui.PlatformUI; 41 import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants; 42 import org.eclipse.ui.texteditor.ITextEditorExtension3; 43 44 /** 45 * Auto indent strategy for java doc comments 46 */ 47 public class JavaDocAutoIndentStrategy extends 48 DefaultIndentLineAutoEditStrategy { 49 50 private String fPartitioning; 51 52 /** 53 * Creates a new Javadoc auto indent strategy for the given document 54 * partitioning. 55 * 56 * @param partitioning 57 * the document partitioning 58 */ JavaDocAutoIndentStrategy(String partitioning)59 public JavaDocAutoIndentStrategy(String partitioning) { 60 fPartitioning = partitioning; 61 } 62 getLineDelimiter(IDocument document)63 private static String getLineDelimiter(IDocument document) { 64 try { 65 if (document.getNumberOfLines() > 1) 66 return document.getLineDelimiter(0); 67 } catch (BadLocationException e) { 68 PHPeclipsePlugin.log(e); 69 } 70 71 return System.getProperty("line.separator"); //$NON-NLS-1$ 72 } 73 74 /** 75 * Copies the indentation of the previous line and add a star. If the 76 * javadoc just started on this line add standard method tags and close the 77 * javadoc. 78 * 79 * @param d 80 * the document to work on 81 * @param c 82 * the command to deal with 83 */ jdocIndentAfterNewLine(IDocument d, DocumentCommand c)84 private void jdocIndentAfterNewLine(IDocument d, DocumentCommand c) { 85 86 if (c.offset == -1 || d.getLength() == 0) 87 return; 88 89 try { 90 // find start of line 91 int p = (c.offset == d.getLength() ? c.offset - 1 : c.offset); 92 IRegion info = d.getLineInformationOfOffset(p); 93 int start = info.getOffset(); 94 95 // find white spaces 96 int end = findEndOfWhiteSpace(d, start, c.offset); 97 98 StringBuffer buf = new StringBuffer(c.text); 99 if (end >= start) { // 1GEYL1R: ITPJUI:ALL - java doc edit smartness 100 // not work for class comments 101 // append to input 102 String indentation = jdocExtractLinePrefix(d, d 103 .getLineOfOffset(c.offset)); 104 buf.append(indentation); 105 if (end < c.offset) { 106 if (d.getChar(end) == '/') { 107 // javadoc started on this line 108 buf.append(" * "); //$NON-NLS-1$ 109 110 if (PHPeclipsePlugin 111 .getDefault() 112 .getPreferenceStore() 113 .getBoolean( 114 PreferenceConstants.EDITOR_CLOSE_JAVADOCS) 115 && isNewComment(d, c.offset, fPartitioning)) { 116 String lineDelimiter = getLineDelimiter(d); 117 118 String endTag = lineDelimiter + indentation + " */"; //$NON-NLS-1$ 119 d.replace(c.offset, 0, endTag); //$NON-NLS-1$ 120 // evaluate method signature 121 ICompilationUnit unit = getCompilationUnit(); 122 123 // if 124 // (PHPeclipsePlugin.getDefault().getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_ADD_JAVADOC_TAGS) 125 // && 126 // unit != null) 127 // { 128 // try { 129 // JavaModelUtil.reconcile(unit); 130 // String string= createJavaDocTags(d, c, 131 // indentation, lineDelimiter, unit); 132 // if (string != null) { 133 // d.replace(c.offset, 0, string); 134 // } 135 // } catch (CoreException e) { 136 // // ignore 137 // } 138 // } 139 } 140 141 } 142 } 143 } 144 145 c.text = buf.toString(); 146 147 } catch (BadLocationException excp) { 148 // stop work 149 } 150 } 151 createJavaDocTags(IDocument document, DocumentCommand command, String indentation, String lineDelimiter, ICompilationUnit unit)152 private String createJavaDocTags(IDocument document, 153 DocumentCommand command, String indentation, String lineDelimiter, 154 ICompilationUnit unit) throws CoreException, BadLocationException { 155 IJavaElement element = unit.getElementAt(command.offset); 156 if (element == null) 157 return null; 158 159 switch (element.getElementType()) { 160 case IJavaElement.TYPE: 161 return createTypeTags(document, command, indentation, 162 lineDelimiter, (IType) element); 163 164 case IJavaElement.METHOD: 165 return createMethodTags(document, command, indentation, 166 lineDelimiter, (IMethod) element); 167 168 default: 169 return null; 170 } 171 } 172 173 /* 174 * Removes start and end of a comment and corrects indentation and line 175 * delimiters. 176 */ prepareTemplateComment(String comment, String indentation, String lineDelimiter)177 private String prepareTemplateComment(String comment, String indentation, 178 String lineDelimiter) { 179 // trim comment start and end if any 180 if (comment.endsWith("*/")) //$NON-NLS-1$ 181 comment = comment.substring(0, comment.length() - 2); 182 comment = comment.trim(); 183 if (comment.startsWith("/*")) { //$NON-NLS-1$ 184 if (comment.length() > 2 && comment.charAt(2) == '*') { 185 comment = comment.substring(3); // remove '/**' 186 } else { 187 comment = comment.substring(2); // remove '/*' 188 } 189 } 190 // return Strings.changeIndent(comment, 0, 191 // CodeFormatterUtil.getTabWidth(), indentation, lineDelimiter); 192 return Strings.changeIndent(comment, 0, getTabWidth(), indentation, 193 lineDelimiter); 194 } 195 getTabWidth()196 public static int getTabWidth() { 197 Preferences preferences = PHPeclipsePlugin.getDefault() 198 .getPluginPreferences(); 199 return preferences 200 .getInt(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_TAB_WIDTH); 201 } 202 createTypeTags(IDocument document, DocumentCommand command, String indentation, String lineDelimiter, IType type)203 private String createTypeTags(IDocument document, DocumentCommand command, 204 String indentation, String lineDelimiter, IType type) 205 throws CoreException { 206 String comment = CodeGeneration.getTypeComment(type 207 .getCompilationUnit(), type.getTypeQualifiedName('.'), 208 lineDelimiter); 209 if (comment != null) { 210 return prepareTemplateComment(comment.trim(), indentation, 211 lineDelimiter); 212 } 213 return null; 214 } 215 createMethodTags(IDocument document, DocumentCommand command, String indentation, String lineDelimiter, IMethod method)216 private String createMethodTags(IDocument document, 217 DocumentCommand command, String indentation, String lineDelimiter, 218 IMethod method) throws CoreException, BadLocationException { 219 IRegion partition = TextUtilities.getPartition(document, fPartitioning, 220 command.offset, false); 221 ISourceRange sourceRange = method.getSourceRange(); 222 if (sourceRange == null 223 || sourceRange.getOffset() != partition.getOffset()) 224 return null; 225 226 // IMethod inheritedMethod= getInheritedMethod(method); 227 // String comment= CodeGeneration.getMethodComment(method, 228 // inheritedMethod, lineDelimiter); 229 // if (comment != null) { 230 // comment= comment.trim(); 231 // boolean javadocComment= comment.startsWith("/**"); //$NON-NLS-1$ 232 // boolean isJavaDoc= partition.getLength() >= 3 && 233 // document.get(partition.getOffset(), 3).equals("/**"); //$NON-NLS-1$ 234 // if (javadocComment == isJavaDoc) { 235 // return prepareTemplateComment(comment, indentation, lineDelimiter); 236 // } 237 // } 238 return null; 239 } 240 241 /** 242 * Returns the method inherited from, <code>null</code> if method is newly 243 * defined. 244 */ 245 // private static IMethod getInheritedMethod(IMethod method) throws 246 // JavaModelException { 247 // IType declaringType= method.getDeclaringType(); 248 // ITypeHierarchy typeHierarchy= 249 // SuperTypeHierarchyCache.getTypeHierarchy(declaringType); 250 // return JavaModelUtil.findMethodDeclarationInHierarchy(typeHierarchy, 251 // declaringType, 252 // method.getElementName(), method.getParameterTypes(), 253 // method.isConstructor()); 254 // } jdocIndentForCommentEnd(IDocument d, DocumentCommand c)255 protected void jdocIndentForCommentEnd(IDocument d, DocumentCommand c) { 256 if (c.offset < 2 || d.getLength() == 0) { 257 return; 258 } 259 try { 260 if ("* ".equals(d.get(c.offset - 2, 2))) { //$NON-NLS-1$ 261 // modify document command 262 c.length++; 263 c.offset--; 264 } 265 } catch (BadLocationException excp) { 266 // stop work 267 } 268 } 269 270 /** 271 * Guesses if the command operates within a newly created javadoc comment or 272 * not. If in doubt, it will assume that the javadoc is new. 273 */ isNewComment(IDocument document, int commandOffset, String partitioning)274 private static boolean isNewComment(IDocument document, int commandOffset, 275 String partitioning) { 276 277 try { 278 int lineIndex = document.getLineOfOffset(commandOffset) + 1; 279 if (lineIndex >= document.getNumberOfLines()) 280 return true; 281 282 IRegion line = document.getLineInformation(lineIndex); 283 ITypedRegion partition = TextUtilities.getPartition(document, 284 partitioning, commandOffset, false); 285 int partitionEnd = partition.getOffset() + partition.getLength(); 286 if (line.getOffset() >= partitionEnd) 287 return false; 288 289 if (document.getLength() == partitionEnd) 290 return true; // partition goes to end of document - probably 291 // a new comment 292 293 String comment = document.get(partition.getOffset(), partition 294 .getLength()); 295 if (comment.indexOf("/*", 2) != -1) //$NON-NLS-1$ 296 return true; // enclosed another comment -> probably a new 297 // comment 298 299 return false; 300 301 } catch (BadLocationException e) { 302 return false; 303 } 304 } 305 isSmartMode()306 private boolean isSmartMode() { 307 IWorkbenchPage page = PHPeclipsePlugin.getActivePage(); 308 if (page != null) { 309 IEditorPart part = page.getActiveEditor(); 310 if (part instanceof ITextEditorExtension3) { 311 ITextEditorExtension3 extension = (ITextEditorExtension3) part; 312 return extension.getInsertMode() == ITextEditorExtension3.SMART_INSERT; 313 } 314 } 315 return false; 316 } 317 318 /* 319 * @see IAutoIndentStrategy#customizeDocumentCommand 320 */ customizeDocumentCommand(IDocument document, DocumentCommand command)321 public void customizeDocumentCommand(IDocument document, 322 DocumentCommand command) { 323 324 if (!isSmartMode()) 325 return; 326 327 try { 328 329 if (command.text != null && command.length == 0) { 330 String[] lineDelimiters = document.getLegalLineDelimiters(); 331 int index = TextUtilities 332 .endsWith(lineDelimiters, command.text); 333 if (index > -1) { 334 // ends with line delimiter 335 if (lineDelimiters[index].equals(command.text)) 336 // just the line delimiter 337 jdocIndentAfterNewLine(document, command); 338 return; 339 } 340 } 341 342 if (command.text != null && command.text.equals("/")) { //$NON-NLS-1$ 343 jdocIndentForCommentEnd(document, command); 344 return; 345 } 346 347 ITypedRegion partition = TextUtilities.getPartition(document, 348 fPartitioning, command.offset, true); 349 int partitionStart = partition.getOffset(); 350 int partitionEnd = partition.getLength() + partitionStart; 351 352 String text = command.text; 353 int offset = command.offset; 354 int length = command.length; 355 356 // partition change 357 final int PREFIX_LENGTH = "/*".length(); //$NON-NLS-1$ 358 final int POSTFIX_LENGTH = "*/".length(); //$NON-NLS-1$ 359 if ((offset < partitionStart + PREFIX_LENGTH || offset + length > partitionEnd 360 - POSTFIX_LENGTH) 361 || text != null 362 && text.length() >= 2 363 && ((text.indexOf("*/") != -1) || (document.getChar(offset) == '*' && text.startsWith("/")))) //$NON-NLS-1$ //$NON-NLS-2$ 364 return; 365 366 if (command.text == null || command.text.length() == 0) 367 jdocHandleBackspaceDelete(document, command); 368 369 else if (command.text != null && command.length == 0 370 && command.text.length() > 0) 371 jdocWrapParagraphOnInsert(document, command); 372 373 } catch (BadLocationException e) { 374 PHPeclipsePlugin.log(e); 375 } 376 } 377 flushCommand(IDocument document, DocumentCommand command)378 private void flushCommand(IDocument document, DocumentCommand command) 379 throws BadLocationException { 380 381 if (!command.doit) 382 return; 383 384 document.replace(command.offset, command.length, command.text); 385 386 command.doit = false; 387 if (command.text != null) 388 command.offset += command.text.length(); 389 command.length = 0; 390 command.text = null; 391 } 392 jdocWrapParagraphOnInsert(IDocument document, DocumentCommand command)393 protected void jdocWrapParagraphOnInsert(IDocument document, 394 DocumentCommand command) throws BadLocationException { 395 396 // Assert.isTrue(command.length == 0); 397 // Assert.isTrue(command.text != null && command.text.length() == 1); 398 399 if (!getPreferenceStore().getBoolean( 400 PreferenceConstants.EDITOR_FORMAT_JAVADOCS)) 401 return; 402 403 int line = document.getLineOfOffset(command.offset); 404 IRegion region = document.getLineInformation(line); 405 int lineOffset = region.getOffset(); 406 int lineLength = region.getLength(); 407 408 String lineContents = document.get(lineOffset, lineLength); 409 StringBuffer buffer = new StringBuffer(lineContents); 410 int start = command.offset - lineOffset; 411 int end = command.length + start; 412 buffer.replace(start, end, command.text); 413 414 // handle whitespace 415 if (command.text != null && command.text.length() != 0 416 && command.text.trim().length() == 0) { 417 418 String endOfLine = document.get(command.offset, lineOffset 419 + lineLength - command.offset); 420 421 // end of line 422 if (endOfLine.length() == 0) { 423 // move caret to next line 424 flushCommand(document, command); 425 426 if (isLineTooShort(document, line)) { 427 int[] caretOffset = { command.offset }; 428 jdocWrapParagraphFromLine(document, line, caretOffset, 429 false); 430 command.offset = caretOffset[0]; 431 return; 432 } 433 434 // move caret to next line if possible 435 if (line < document.getNumberOfLines() - 1 436 && isJavaDocLine(document, line + 1)) { 437 String lineDelimiter = document.getLineDelimiter(line); 438 String nextLinePrefix = jdocExtractLinePrefix(document, 439 line + 1); 440 command.offset += lineDelimiter.length() 441 + nextLinePrefix.length(); 442 } 443 return; 444 445 // inside whitespace at end of line 446 } else if (endOfLine.trim().length() == 0) { 447 // simply insert space 448 return; 449 } 450 } 451 452 // change in prefix region 453 String prefix = jdocExtractLinePrefix(document, line); 454 boolean wrapAlways = command.offset >= lineOffset 455 && command.offset <= lineOffset + prefix.length(); 456 457 // must insert the text now because it may include whitepace 458 flushCommand(document, command); 459 460 if (wrapAlways 461 || calculateDisplayedWidth(buffer.toString()) > getMargin() 462 || isLineTooShort(document, line)) { 463 int[] caretOffset = { command.offset }; 464 jdocWrapParagraphFromLine(document, line, caretOffset, wrapAlways); 465 466 if (!wrapAlways) 467 command.offset = caretOffset[0]; 468 } 469 } 470 471 /** 472 * Method jdocWrapParagraphFromLine. 473 * 474 * @param document 475 * @param line 476 * @param always 477 */ jdocWrapParagraphFromLine(IDocument document, int line, int[] caretOffset, boolean always)478 private void jdocWrapParagraphFromLine(IDocument document, int line, 479 int[] caretOffset, boolean always) throws BadLocationException { 480 481 String indent = jdocExtractLinePrefix(document, line); 482 if (!always) { 483 if (!indent.trim().startsWith("*")) //$NON-NLS-1$ 484 return; 485 486 if (indent.trim().startsWith("*/")) //$NON-NLS-1$ 487 return; 488 489 if (!isLineTooLong(document, line) 490 && !isLineTooShort(document, line)) 491 return; 492 } 493 494 boolean caretRelativeToParagraphOffset = false; 495 int caret = caretOffset[0]; 496 497 int caretLine = document.getLineOfOffset(caret); 498 int lineOffset = document.getLineOffset(line); 499 int paragraphOffset = lineOffset + indent.length(); 500 if (paragraphOffset < caret) { 501 caret -= paragraphOffset; 502 caretRelativeToParagraphOffset = true; 503 } else { 504 caret -= lineOffset; 505 } 506 507 StringBuffer buffer = new StringBuffer(); 508 int currentLine = line; 509 while (line == currentLine || isJavaDocLine(document, currentLine)) { 510 511 if (buffer.length() != 0 512 && !Character.isWhitespace(buffer 513 .charAt(buffer.length() - 1))) { 514 buffer.append(' '); 515 if (currentLine <= caretLine) { 516 // in this case caretRelativeToParagraphOffset is always 517 // true 518 ++caret; 519 } 520 } 521 522 String string = getLineContents(document, currentLine); 523 buffer.append(string); 524 currentLine++; 525 } 526 String paragraph = buffer.toString(); 527 528 if (paragraph.trim().length() == 0) 529 return; 530 531 caretOffset[0] = caretRelativeToParagraphOffset ? caret : 0; 532 String delimiter = document.getLineDelimiter(0); 533 String wrapped = formatParagraph(paragraph, caretOffset, indent, 534 delimiter, getMargin()); 535 536 int beginning = document.getLineOffset(line); 537 int end = document.getLineOffset(currentLine); 538 document.replace(beginning, end - beginning, wrapped.toString()); 539 540 caretOffset[0] = caretRelativeToParagraphOffset ? caretOffset[0] 541 + beginning : caret + beginning; 542 } 543 544 /** 545 * Line break iterator to handle whitespaces as first class citizens. 546 */ 547 private static class LineBreakIterator { 548 549 private final String fString; 550 551 private final BreakIterator fIterator = BreakIterator.getLineInstance(); 552 553 private int fStart; 554 555 private int fEnd; 556 557 private int fBufferedEnd; 558 LineBreakIterator(String string)559 public LineBreakIterator(String string) { 560 fString = string; 561 fIterator.setText(string); 562 } 563 first()564 public int first() { 565 fBufferedEnd = -1; 566 fStart = fIterator.first(); 567 return fStart; 568 } 569 next()570 public int next() { 571 572 if (fBufferedEnd != -1) { 573 fStart = fEnd; 574 fEnd = fBufferedEnd; 575 fBufferedEnd = -1; 576 return fEnd; 577 } 578 579 fStart = fEnd; 580 fEnd = fIterator.next(); 581 582 if (fEnd == BreakIterator.DONE) 583 return fEnd; 584 585 final String string = fString.substring(fStart, fEnd); 586 587 // whitespace 588 if (string.trim().length() == 0) 589 return fEnd; 590 591 final String word = string.trim(); 592 if (word.length() == string.length()) 593 return fEnd; 594 595 // suspected whitespace 596 fBufferedEnd = fEnd; 597 return fStart + word.length(); 598 } 599 } 600 601 /** 602 * Formats a paragraph, using break iterator. 603 * 604 * @param offset 605 * an offset within the paragraph, which will be updated with 606 * respect to formatting. 607 */ formatParagraph(String paragraph, int[] offset, String prefix, String lineDelimiter, int margin)608 private static String formatParagraph(String paragraph, int[] offset, 609 String prefix, String lineDelimiter, int margin) { 610 611 LineBreakIterator iterator = new LineBreakIterator(paragraph); 612 613 StringBuffer paragraphBuffer = new StringBuffer(); 614 StringBuffer lineBuffer = new StringBuffer(); 615 StringBuffer whiteSpaceBuffer = new StringBuffer(); 616 617 int index = offset[0]; 618 int indexBuffer = -1; 619 620 // line delimiter could be null 621 if (lineDelimiter == null) 622 lineDelimiter = ""; //$NON-NLS-1$ 623 624 for (int start = iterator.first(), end = iterator.next(); end != BreakIterator.DONE; start = end, end = iterator 625 .next()) { 626 627 String word = paragraph.substring(start, end); 628 629 // word is whitespace 630 if (word.trim().length() == 0) { 631 whiteSpaceBuffer.append(word); 632 633 // first word of line is always appended 634 } else if (lineBuffer.length() == 0) { 635 lineBuffer.append(prefix); 636 lineBuffer.append(whiteSpaceBuffer.toString()); 637 lineBuffer.append(word); 638 639 } else { 640 String line = lineBuffer.toString() 641 + whiteSpaceBuffer.toString() + word.toString(); 642 643 // margin exceeded 644 if (calculateDisplayedWidth(line) > margin) { 645 // flush line buffer and wrap paragraph 646 paragraphBuffer.append(lineBuffer.toString()); 647 paragraphBuffer.append(lineDelimiter); 648 lineBuffer.setLength(0); 649 lineBuffer.append(prefix); 650 lineBuffer.append(word); 651 652 // flush index buffer 653 if (indexBuffer != -1) { 654 offset[0] = indexBuffer; 655 // correct for caret in whitespace at the end of line 656 if (whiteSpaceBuffer.length() != 0 && index < start 657 && index >= start - whiteSpaceBuffer.length()) 658 offset[0] -= (index - (start - whiteSpaceBuffer 659 .length())); 660 indexBuffer = -1; 661 } 662 663 whiteSpaceBuffer.setLength(0); 664 665 // margin not exceeded 666 } else { 667 lineBuffer.append(whiteSpaceBuffer.toString()); 668 lineBuffer.append(word); 669 whiteSpaceBuffer.setLength(0); 670 } 671 } 672 673 if (index >= start && index < end) { 674 indexBuffer = paragraphBuffer.length() + lineBuffer.length() 675 + (index - start); 676 if (word.trim().length() != 0) 677 indexBuffer -= word.length(); 678 } 679 } 680 681 // flush line buffer 682 paragraphBuffer.append(lineBuffer.toString()); 683 paragraphBuffer.append(lineDelimiter); 684 685 // flush index buffer 686 if (indexBuffer != -1) 687 offset[0] = indexBuffer; 688 689 // last position is not returned by break iterator 690 else if (offset[0] == paragraph.length()) 691 offset[0] = paragraphBuffer.length() - lineDelimiter.length(); 692 693 return paragraphBuffer.toString(); 694 } 695 getPreferenceStore()696 private static IPreferenceStore getPreferenceStore() { 697 return PHPeclipsePlugin.getDefault().getPreferenceStore(); 698 } 699 700 /** 701 * Returns the displayed width of a string, taking in account the displayed 702 * tab width. The result can be compared against the print margin. 703 */ calculateDisplayedWidth(String string)704 private static int calculateDisplayedWidth(String string) { 705 706 int tabWidth = getPreferenceStore() 707 .getInt( 708 AbstractDecoratedTextEditorPreferenceConstants.EDITOR_TAB_WIDTH); 709 if (tabWidth <= 0) { 710 tabWidth = 2; 711 } 712 int column = 0; 713 for (int i = 0; i < string.length(); i++) 714 if ('\t' == string.charAt(i)) 715 column += tabWidth - (column % tabWidth); 716 else 717 column++; 718 719 return column; 720 } 721 jdocExtractLinePrefix(IDocument d, int line)722 private String jdocExtractLinePrefix(IDocument d, int line) 723 throws BadLocationException { 724 725 IRegion region = d.getLineInformation(line); 726 int lineOffset = region.getOffset(); 727 int index = findEndOfWhiteSpace(d, lineOffset, lineOffset 728 + d.getLineLength(line)); 729 if (d.getChar(index) == '*') { 730 index++; 731 if (index != lineOffset + region.getLength() 732 && d.getChar(index) == ' ') 733 index++; 734 } 735 return d.get(lineOffset, index - lineOffset); 736 } 737 getLineContents(IDocument d, int line)738 private String getLineContents(IDocument d, int line) 739 throws BadLocationException { 740 int offset = d.getLineOffset(line); 741 int length = d.getLineLength(line); 742 String lineDelimiter = d.getLineDelimiter(line); 743 if (lineDelimiter != null) 744 length = length - lineDelimiter.length(); 745 String lineContents = d.get(offset, length); 746 int trim = jdocExtractLinePrefix(d, line).length(); 747 return lineContents.substring(trim); 748 } 749 getLine(IDocument document, int line)750 private static String getLine(IDocument document, int line) 751 throws BadLocationException { 752 IRegion region = document.getLineInformation(line); 753 return document.get(region.getOffset(), region.getLength()); 754 } 755 756 /** 757 * Returns <code>true</code> if the javadoc line is too short, 758 * <code>false</code> otherwise. 759 */ isLineTooShort(IDocument document, int line)760 private boolean isLineTooShort(IDocument document, int line) 761 throws BadLocationException { 762 763 if (!isJavaDocLine(document, line + 1)) 764 return false; 765 766 String nextLine = getLineContents(document, line + 1); 767 if (nextLine.trim().length() == 0) 768 return false; 769 770 return true; 771 } 772 773 /** 774 * Returns <code>true</code> if the line is too long, <code>false</code> 775 * otherwise. 776 */ isLineTooLong(IDocument document, int line)777 private boolean isLineTooLong(IDocument document, int line) 778 throws BadLocationException { 779 String lineContents = getLine(document, line); 780 return calculateDisplayedWidth(lineContents) > getMargin(); 781 } 782 getMargin()783 private static int getMargin() { 784 return getPreferenceStore() 785 .getInt( 786 AbstractDecoratedTextEditorPreferenceConstants.EDITOR_PRINT_MARGIN_COLUMN); 787 } 788 789 private static final String[] fgInlineTags = { 790 "<b>", "<i>", "<em>", "<strong>", "<code>" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ 791 }; 792 isInlineTag(String string)793 private boolean isInlineTag(String string) { 794 for (int i = 0; i < fgInlineTags.length; i++) 795 if (string.startsWith(fgInlineTags[i])) 796 return true; 797 return false; 798 } 799 800 /** 801 * returns true if the specified line is part of a paragraph and should be 802 * merged with the previous line. 803 */ isJavaDocLine(IDocument document, int line)804 private boolean isJavaDocLine(IDocument document, int line) 805 throws BadLocationException { 806 807 if (document.getNumberOfLines() < line) 808 return false; 809 810 int offset = document.getLineOffset(line); 811 int length = document.getLineLength(line); 812 int firstChar = findEndOfWhiteSpace(document, offset, offset + length); 813 length -= firstChar - offset; 814 String lineContents = document.get(firstChar, length); 815 816 String prefix = lineContents.trim(); 817 if (!prefix.startsWith("*") || prefix.startsWith("*/")) //$NON-NLS-1$ //$NON-NLS-2$ 818 return false; 819 820 lineContents = lineContents.substring(1).trim().toLowerCase(); 821 822 // preserve empty lines 823 if (lineContents.length() == 0) 824 return false; 825 826 // preserve @TAGS 827 if (lineContents.startsWith("@")) //$NON-NLS-1$ 828 return false; 829 830 // preserve HTML tags which are not inline 831 if (lineContents.startsWith("<") && !isInlineTag(lineContents)) //$NON-NLS-1$ 832 return false; 833 834 return true; 835 } 836 jdocHandleBackspaceDelete(IDocument document, DocumentCommand c)837 protected void jdocHandleBackspaceDelete(IDocument document, 838 DocumentCommand c) { 839 840 if (!getPreferenceStore().getBoolean( 841 PreferenceConstants.EDITOR_FORMAT_JAVADOCS)) 842 return; 843 844 try { 845 String text = document.get(c.offset, c.length); 846 int line = document.getLineOfOffset(c.offset); 847 int lineOffset = document.getLineOffset(line); 848 849 // erase line delimiter 850 String lineDelimiter = document.getLineDelimiter(line); 851 if (lineDelimiter != null && lineDelimiter.equals(text)) { 852 853 String prefix = jdocExtractLinePrefix(document, line + 1); 854 855 // strip prefix if any 856 if (prefix.length() > 0) { 857 int length = document.getLineDelimiter(line).length() 858 + prefix.length(); 859 document.replace(c.offset, length, null); 860 861 c.doit = false; 862 c.length = 0; 863 return; 864 } 865 866 // backspace: beginning of a javadoc line 867 } else if (document.getChar(c.offset - 1) == '*' 868 && jdocExtractLinePrefix(document, line).length() - 1 >= c.offset 869 - lineOffset) { 870 871 lineDelimiter = document.getLineDelimiter(line - 1); 872 String prefix = jdocExtractLinePrefix(document, line); 873 int length = (lineDelimiter != null ? lineDelimiter.length() 874 : 0) 875 + prefix.length(); 876 document.replace(c.offset - length + 1, length, null); 877 878 c.doit = false; 879 c.offset -= length - 1; 880 c.length = 0; 881 return; 882 883 } else { 884 document.replace(c.offset, c.length, null); 885 c.doit = false; 886 c.length = 0; 887 } 888 889 } catch (BadLocationException e) { 890 PHPeclipsePlugin.log(e); 891 } 892 893 try { 894 int line = document.getLineOfOffset(c.offset); 895 int lineOffset = document.getLineOffset(line); 896 String prefix = jdocExtractLinePrefix(document, line); 897 boolean always = c.offset > lineOffset 898 && c.offset <= lineOffset + prefix.length(); 899 int[] caretOffset = { c.offset }; 900 jdocWrapParagraphFromLine(document, document 901 .getLineOfOffset(c.offset), caretOffset, always); 902 c.offset = caretOffset[0]; 903 904 } catch (BadLocationException e) { 905 PHPeclipsePlugin.log(e); 906 } 907 } 908 909 /** 910 * Returns the compilation unit of the CompilationUnitEditor invoking the 911 * AutoIndentStrategy, might return <code>null</code> on error. 912 */ getCompilationUnit()913 private static ICompilationUnit getCompilationUnit() { 914 915 IWorkbenchWindow window = PlatformUI.getWorkbench() 916 .getActiveWorkbenchWindow(); 917 if (window == null) 918 return null; 919 920 IWorkbenchPage page = window.getActivePage(); 921 if (page == null) 922 return null; 923 924 IEditorPart editor = page.getActiveEditor(); 925 if (editor == null) 926 return null; 927 928 IWorkingCopyManager manager = PHPeclipsePlugin.getDefault() 929 .getWorkingCopyManager(); 930 ICompilationUnit unit = manager.getWorkingCopy(editor.getEditorInput()); 931 if (unit == null) 932 return null; 933 934 return unit; 935 } 936 937 } 938