1 11 package org.eclipse.jdt.internal.ui.javaeditor; 12 13 import org.eclipse.jface.text.BadLocationException; 14 import org.eclipse.jface.text.IDocument; 15 import org.eclipse.jface.text.IRegion; 16 import org.eclipse.jface.text.ITypedRegion; 17 import org.eclipse.jface.text.TextUtilities; 18 import org.eclipse.jface.text.source.ILineRange; 19 20 import org.eclipse.jdt.core.IJavaProject; 21 22 import org.eclipse.jdt.internal.corext.util.CodeFormatterUtil; 23 24 import org.eclipse.jdt.ui.text.IJavaPartitions; 25 26 import org.eclipse.jdt.internal.ui.text.JavaHeuristicScanner; 27 import org.eclipse.jdt.internal.ui.text.JavaIndenter; 28 29 30 35 public final class IndentUtil { 36 37 private static final String SLASHES= "//"; 39 46 public static final class IndentResult { 47 private IndentResult(boolean[] commentLines) { 48 commentLinesAtColumnZero= commentLines; 49 } 50 private boolean[] commentLinesAtColumnZero; 51 private boolean hasChanged; 52 private int leftmostLine= -1; 53 58 public boolean hasChanged() { 59 return hasChanged; 60 } 61 } 62 63 private IndentUtil() { 64 } 66 67 84 public static IndentResult indentLines(IDocument document, ILineRange lines, IJavaProject project, IndentResult result) throws BadLocationException { 85 int numberOfLines= lines.getNumberOfLines(); 86 87 if (numberOfLines < 1) 88 return new IndentResult(null); 89 90 result= reuseOrCreateToken(result, numberOfLines); 91 92 JavaHeuristicScanner scanner= new JavaHeuristicScanner(document); 93 JavaIndenter indenter= new JavaIndenter(document, scanner, project); 94 boolean changed= false; 95 int tabSize= CodeFormatterUtil.getTabWidth(project); 96 for (int line= lines.getStartLine(), last= line + numberOfLines, i= 0; line < last; line++) { 97 changed |= indentLine(document, line, indenter, scanner, result.commentLinesAtColumnZero, i++, tabSize); 98 } 99 result.hasChanged= changed; 100 101 return result; 102 } 103 104 126 public static IndentResult shiftLines(IDocument document, ILineRange lines, IJavaProject project, IndentResult result) throws BadLocationException { 127 int numberOfLines= lines.getNumberOfLines(); 128 129 if (numberOfLines < 1) 130 return new IndentResult(null); 131 132 result= reuseOrCreateToken(result, numberOfLines); 133 result.hasChanged= false; 134 135 JavaHeuristicScanner scanner= new JavaHeuristicScanner(document); 136 JavaIndenter indenter= new JavaIndenter(document, scanner, project); 137 138 String current= getCurrentIndent(document, lines.getStartLine()); 139 StringBuffer correct= indenter.computeIndentation(document.getLineOffset(lines.getStartLine())); 140 if (correct == null) 141 return result; 143 int tabSize= CodeFormatterUtil.getTabWidth(project); 144 StringBuffer addition= new StringBuffer (); 145 int difference= subtractIndent(correct, current, addition, tabSize); 146 147 if (difference == 0) 148 return result; 149 150 if (result.leftmostLine == -1) 151 result.leftmostLine= getLeftMostLine(document, lines, tabSize); 152 153 int maxReduction= computeVisualLength(getCurrentIndent(document, result.leftmostLine + lines.getStartLine()), tabSize); 154 155 if (difference > 0) { 156 for (int line= lines.getStartLine(), last= line + numberOfLines, i= 0; line < last; line++) 157 addIndent(document, line, addition, result.commentLinesAtColumnZero, i++); 158 } else { 159 int reduction= Math.min(-difference, maxReduction); 160 for (int line= lines.getStartLine(), last= line + numberOfLines, i= 0; line < last; line++) 161 cutIndent(document, line, reduction, tabSize, result.commentLinesAtColumnZero, i++); 162 } 163 164 result.hasChanged= true; 165 166 return result; 167 168 } 169 170 180 private static void addIndent(IDocument document, int line, CharSequence indent, boolean[] commentlines, int relative) throws BadLocationException { 181 IRegion region= document.getLineInformation(line); 182 int insert= region.getOffset(); 183 int endOffset= region.getOffset() + region.getLength(); 184 185 if (!commentlines[relative]) { 187 while (insert < endOffset - 2 && document.get(insert, 2).equals(SLASHES)) 188 insert += 2; 189 } 190 191 document.replace(insert, 0, indent.toString()); 193 } 194 195 205 private static void cutIndent(IDocument document, int line, int toDelete, int tabSize, boolean[] commentLines, int relative) throws BadLocationException { 206 IRegion region= document.getLineInformation(line); 207 int from= region.getOffset(); 208 int endOffset= region.getOffset() + region.getLength(); 209 210 while (from < endOffset - 2 && document.get(from, 2).equals(SLASHES)) 212 from += 2; 213 214 int to= from; 215 while (toDelete > 0 && to < endOffset) { 216 char ch= document.getChar(to); 217 if (!Character.isWhitespace(ch)) 218 break; 219 toDelete -= computeVisualLength(ch, tabSize); 220 if (toDelete >= 0) 221 to++; 222 else 223 break; 224 } 225 226 if (endOffset > to + 1 && document.get(to, 2).equals(SLASHES)) 227 commentLines[relative]= true; 228 229 document.replace(from, to - from, null); 230 } 231 232 242 private static int subtractIndent(CharSequence correct, CharSequence current, StringBuffer difference, int tabSize) { 243 int c1= computeVisualLength(correct, tabSize); 244 int c2= computeVisualLength(current, tabSize); 245 int diff= c1 - c2; 246 if (diff <= 0) 247 return diff; 248 249 difference.setLength(0); 250 int len= 0, i= 0; 251 while (len < diff) { 252 char c= correct.charAt(i++); 253 difference.append(c); 254 len += computeVisualLength(c, tabSize); 255 } 256 257 258 return diff; 259 } 260 261 private static int computeVisualLength(char ch, int tabSize) { 262 if (ch == '\t') 263 return tabSize; 264 else 265 return 1; 266 } 267 268 275 private static int computeVisualLength(CharSequence seq, int tablen) { 276 int size= 0; 277 278 for (int i= 0; i < seq.length(); i++) { 279 char ch= seq.charAt(i); 280 if (ch == '\t') { 281 if (tablen != 0) 282 size += tablen - size % tablen; 283 } else { 285 size++; 286 } 287 } 288 return size; 289 } 290 291 302 private static String getCurrentIndent(IDocument document, int line) throws BadLocationException { 303 IRegion region= document.getLineInformation(line); 304 int from= region.getOffset(); 305 int endOffset= region.getOffset() + region.getLength(); 306 307 int to= from; 309 while (to < endOffset - 2 && document.get(to, 2).equals(SLASHES)) 310 to += 2; 311 312 while (to < endOffset) { 313 char ch= document.getChar(to); 314 if (!Character.isWhitespace(ch)) 315 break; 316 to++; 317 } 318 319 if (to > from && to < endOffset - 1 && document.get(to - 1, 2).equals(" *")) { String type= TextUtilities.getContentType(document, IJavaPartitions.JAVA_PARTITIONING, to, true); 322 if (type.equals(IJavaPartitions.JAVA_DOC) || type.equals(IJavaPartitions.JAVA_MULTI_LINE_COMMENT)) 323 to--; 324 } 325 326 return document.get(from, to - from); 327 } 328 329 private static int getLeftMostLine(IDocument document, ILineRange lines, int tabSize) throws BadLocationException { 330 int numberOfLines= lines.getNumberOfLines(); 331 int first= lines.getStartLine(); 332 int minLine= -1; 333 int minIndent= Integer.MAX_VALUE; 334 for (int line= 0; line < numberOfLines; line++) { 335 int length= computeVisualLength(getCurrentIndent(document, line + first), tabSize); 336 if (length < minIndent) { 337 minIndent= length; 338 minLine= line; 339 } 340 } 341 return minLine; 342 } 343 344 private static IndentResult reuseOrCreateToken(IndentResult token, int numberOfLines) { 345 if (token == null) 346 token= new IndentResult(new boolean[numberOfLines]); 347 else if (token.commentLinesAtColumnZero == null) 348 token.commentLinesAtColumnZero= new boolean[numberOfLines]; 349 else if (token.commentLinesAtColumnZero.length != numberOfLines) { 350 boolean[] commentBooleans= new boolean[numberOfLines]; 351 System.arraycopy(token.commentLinesAtColumnZero, 0, commentBooleans, 0, Math.min(numberOfLines, token.commentLinesAtColumnZero.length)); 352 token.commentLinesAtColumnZero= commentBooleans; 353 } 354 return token; 355 } 356 357 372 private static boolean indentLine(IDocument document, int line, JavaIndenter indenter, JavaHeuristicScanner scanner, boolean[] commentLines, int lineIndex, int tabSize) throws BadLocationException { 373 IRegion currentLine= document.getLineInformation(line); 374 final int offset= currentLine.getOffset(); 375 int wsStart= offset; 377 String indent= null; 378 if (offset < document.getLength()) { 379 ITypedRegion partition= TextUtilities.getPartition(document, IJavaPartitions.JAVA_PARTITIONING, offset, true); 380 ITypedRegion startingPartition= TextUtilities.getPartition(document, IJavaPartitions.JAVA_PARTITIONING, offset, false); 381 String type= partition.getType(); 382 if (type.equals(IJavaPartitions.JAVA_DOC) || type.equals(IJavaPartitions.JAVA_MULTI_LINE_COMMENT)) { 383 indent= computeJavadocIndent(document, line, scanner, startingPartition); 384 } else if (!commentLines[lineIndex] && startingPartition.getOffset() == offset && startingPartition.getType().equals(IJavaPartitions.JAVA_SINGLE_LINE_COMMENT)) { 385 return false; 386 } 387 } 388 389 if (indent == null) { 391 StringBuffer computed= indenter.computeIndentation(offset); 392 if (computed != null) 393 indent= computed.toString(); 394 else 395 indent= new String (); 396 } 397 398 int lineLength= currentLine.getLength(); 401 int end= scanner.findNonWhitespaceForwardInAnyPartition(wsStart, offset + lineLength); 402 if (end == JavaHeuristicScanner.NOT_FOUND) 403 end= offset + lineLength; 404 int length= end - offset; 405 String currentIndent= document.get(offset, length); 406 407 if (length > 0) { 410 ITypedRegion partition= TextUtilities.getPartition(document, IJavaPartitions.JAVA_PARTITIONING, end, false); 411 if (partition.getOffset() == end && IJavaPartitions.JAVA_SINGLE_LINE_COMMENT.equals(partition.getType())) { 412 commentLines[lineIndex]= true; 413 } 414 } 415 416 if (!indent.equals(currentIndent)) { 418 document.replace(offset, length, indent); 419 return true; 420 } 421 422 return false; 423 } 424 425 436 private static String computeJavadocIndent(IDocument document, int line, JavaHeuristicScanner scanner, ITypedRegion partition) throws BadLocationException { 437 if (line == 0) return null; 439 440 final IRegion lineInfo= document.getLineInformation(line); 443 final int lineStart= lineInfo.getOffset(); 444 final int lineLength= lineInfo.getLength(); 445 final int lineEnd= lineStart + lineLength; 446 int nonWS= scanner.findNonWhitespaceForwardInAnyPartition(lineStart, lineEnd); 447 if (nonWS == JavaHeuristicScanner.NOT_FOUND || document.getChar(nonWS) != '*') { 448 if (nonWS == JavaHeuristicScanner.NOT_FOUND) 449 return document.get(lineStart, lineLength); 450 return document.get(lineStart, nonWS - lineStart); 451 } 452 453 IRegion previousLine= document.getLineInformation(line - 1); 455 int previousLineStart= previousLine.getOffset(); 456 int previousLineLength= previousLine.getLength(); 457 int previousLineEnd= previousLineStart + previousLineLength; 458 459 StringBuffer buf= new StringBuffer (); 460 int previousLineNonWS= scanner.findNonWhitespaceForwardInAnyPartition(previousLineStart, previousLineEnd); 461 if (previousLineNonWS == JavaHeuristicScanner.NOT_FOUND || document.getChar(previousLineNonWS) != '*') { 462 previousLine= document.getLineInformationOfOffset(partition.getOffset()); 464 previousLineStart= previousLine.getOffset(); 465 previousLineLength= previousLine.getLength(); 466 previousLineEnd= previousLineStart + previousLineLength; 467 previousLineNonWS= scanner.findNonWhitespaceForwardInAnyPartition(previousLineStart, previousLineEnd); 468 if (previousLineNonWS == JavaHeuristicScanner.NOT_FOUND) 469 previousLineNonWS= previousLineEnd; 470 471 buf.append(' '); 474 } 475 476 String indentation= document.get(previousLineStart, previousLineNonWS - previousLineStart); 477 buf.insert(0, indentation); 478 return buf.toString(); 479 } 480 } 481 | Popular Tags |