PageRenderTime 48ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/groovy-eclipse/org.codehaus.groovy.eclipse.ui/src/org/codehaus/groovy/eclipse/editor/GroovyIndenter.java

http://groovy-eclipse.googlecode.com/
Java | 1668 lines | 1001 code | 163 blank | 504 comment | 209 complexity | 77a5be2218c502cfae978510c5f6ddb2 MD5 | raw file
Possible License(s): Apache-2.0

Large files files are truncated, but you can click here to view the full file

  1. package org.codehaus.groovy.eclipse.editor;
  2. import org.eclipse.jdt.core.IJavaProject;
  3. import org.eclipse.jdt.core.JavaCore;
  4. import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
  5. import org.eclipse.jdt.internal.corext.util.CodeFormatterUtil;
  6. import org.eclipse.jdt.internal.ui.text.DocumentCharacterIterator;
  7. import org.eclipse.jdt.internal.ui.text.JavaHeuristicScanner;
  8. import org.eclipse.jdt.internal.ui.text.Symbols;
  9. import org.eclipse.jface.text.BadLocationException;
  10. import org.eclipse.jface.text.IDocument;
  11. import org.eclipse.jface.text.IRegion;
  12. /**
  13. * This is a copy of the JavaIndenter with the only change so far
  14. * being
  15. * case Symbols.TokenRBRACE:
  16. * added to findReferencePosition()
  17. *
  18. * Uses the {@link org.eclipse.jdt.internal.ui.text.JavaHeuristicScanner} to
  19. * get the indentation level for a certain position in a document.
  20. *
  21. * <p>
  22. * An instance holds some internal position in the document and is therefore
  23. * not threadsafe.
  24. * </p>
  25. *
  26. * @since 3.0
  27. */
  28. public final class GroovyIndenter {
  29. /**
  30. * The JDT Core preferences.
  31. * @since 3.2
  32. */
  33. private final class CorePrefs {
  34. final boolean prefUseTabs;
  35. final int prefTabSize;
  36. final int prefIndentationSize;
  37. final boolean prefArrayDimensionsDeepIndent;
  38. final int prefArrayIndent;
  39. final boolean prefArrayDeepIndent;
  40. final boolean prefTernaryDeepAlign;
  41. final int prefTernaryIndent;
  42. final int prefCaseIndent;
  43. final int prefAssignmentIndent;
  44. final int prefCaseBlockIndent;
  45. final int prefSimpleIndent;
  46. final int prefBracketIndent;
  47. final boolean prefMethodDeclDeepIndent;
  48. final int prefMethodDeclIndent;
  49. final boolean prefMethodCallDeepIndent;
  50. final int prefMethodCallIndent;
  51. final boolean prefParenthesisDeepIndent;
  52. final int prefParenthesisIndent;
  53. final int prefBlockIndent;
  54. final int prefMethodBodyIndent;
  55. final int prefTypeIndent;
  56. final boolean prefIndentBracesForBlocks;
  57. final boolean prefIndentBracesForArrays;
  58. final boolean prefIndentBracesForMethods;
  59. final boolean prefIndentBracesForTypes;
  60. final int prefContinuationIndent;
  61. final boolean prefHasGenerics;
  62. final String prefTabChar;
  63. private final IJavaProject fProject;
  64. /**
  65. * Returns <code>true</code> if the class is used outside the workbench,
  66. * <code>false</code> in normal mode
  67. *
  68. * @return <code>true</code> if the plug-ins are not available
  69. */
  70. private boolean isStandalone() {
  71. return JavaCore.getPlugin() == null;
  72. }
  73. /**
  74. * Returns the possibly project-specific core preference defined under <code>key</code>.
  75. *
  76. * @param key the key of the preference
  77. * @return the value of the preference
  78. * @since 3.1
  79. */
  80. private String getCoreFormatterOption(String key) {
  81. if (fProject == null)
  82. return JavaCore.getOption(key);
  83. return fProject.getOption(key, true);
  84. }
  85. CorePrefs(IJavaProject project) {
  86. fProject= project;
  87. if (isStandalone()) {
  88. prefUseTabs= true;
  89. prefTabSize= 4;
  90. prefIndentationSize= 4;
  91. prefArrayDimensionsDeepIndent= true;
  92. prefContinuationIndent= 2;
  93. prefBlockIndent= 1;
  94. prefArrayIndent= prefContinuationIndent;
  95. prefArrayDeepIndent= true;
  96. prefTernaryDeepAlign= false;
  97. prefTernaryIndent= prefContinuationIndent;
  98. prefCaseIndent= 0;
  99. prefAssignmentIndent= prefBlockIndent;
  100. prefCaseBlockIndent= prefBlockIndent;
  101. prefIndentBracesForBlocks= false;
  102. prefSimpleIndent= (prefIndentBracesForBlocks && prefBlockIndent == 0) ? 1 : prefBlockIndent;
  103. prefBracketIndent= prefBlockIndent;
  104. prefMethodDeclDeepIndent= true;
  105. prefMethodDeclIndent= 1;
  106. prefMethodCallDeepIndent= false;
  107. prefMethodCallIndent= 1;
  108. prefParenthesisDeepIndent= false;
  109. prefParenthesisIndent= prefContinuationIndent;
  110. prefMethodBodyIndent= 1;
  111. prefTypeIndent= 1;
  112. prefIndentBracesForArrays= false;
  113. prefIndentBracesForMethods= false;
  114. prefIndentBracesForTypes= false;
  115. prefHasGenerics= false;
  116. prefTabChar= JavaCore.TAB;
  117. } else {
  118. prefUseTabs= prefUseTabs();
  119. prefTabSize= prefTabSize();
  120. prefIndentationSize= prefIndentationSize();
  121. prefArrayDimensionsDeepIndent= prefArrayDimensionsDeepIndent();
  122. prefContinuationIndent= prefContinuationIndent();
  123. prefBlockIndent= prefBlockIndent();
  124. prefArrayIndent= prefArrayIndent();
  125. prefArrayDeepIndent= prefArrayDeepIndent();
  126. prefTernaryDeepAlign= prefTernaryDeepAlign();
  127. prefTernaryIndent= prefTernaryIndent();
  128. prefCaseIndent= prefCaseIndent();
  129. prefAssignmentIndent= prefAssignmentIndent();
  130. prefCaseBlockIndent= prefCaseBlockIndent();
  131. prefIndentBracesForBlocks= prefIndentBracesForBlocks();
  132. prefSimpleIndent= prefSimpleIndent();
  133. prefBracketIndent= prefBracketIndent();
  134. prefMethodDeclDeepIndent= prefMethodDeclDeepIndent();
  135. prefMethodDeclIndent= prefMethodDeclIndent();
  136. prefMethodCallDeepIndent= prefMethodCallDeepIndent();
  137. prefMethodCallIndent= prefMethodCallIndent();
  138. prefParenthesisDeepIndent= prefParenthesisDeepIndent();
  139. prefParenthesisIndent= prefParenthesisIndent();
  140. prefMethodBodyIndent= prefMethodBodyIndent();
  141. prefTypeIndent= prefTypeIndent();
  142. prefIndentBracesForArrays= prefIndentBracesForArrays();
  143. prefIndentBracesForMethods= prefIndentBracesForMethods();
  144. prefIndentBracesForTypes= prefIndentBracesForTypes();
  145. prefHasGenerics= hasGenerics();
  146. prefTabChar= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR);
  147. }
  148. }
  149. private boolean prefUseTabs() {
  150. return !JavaCore.SPACE.equals(getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR));
  151. }
  152. private int prefTabSize() {
  153. return CodeFormatterUtil.getTabWidth(fProject);
  154. }
  155. private int prefIndentationSize() {
  156. return CodeFormatterUtil.getIndentWidth(fProject);
  157. }
  158. private boolean prefArrayDimensionsDeepIndent() {
  159. return true; // sensible default, no formatter setting
  160. }
  161. private int prefArrayIndent() {
  162. String option= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_EXPRESSIONS_IN_ARRAY_INITIALIZER);
  163. try {
  164. if (DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_BY_ONE)
  165. return 1;
  166. } catch (IllegalArgumentException e) {
  167. // ignore and return default
  168. }
  169. return prefContinuationIndent(); // default
  170. }
  171. private boolean prefArrayDeepIndent() {
  172. String option= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_EXPRESSIONS_IN_ARRAY_INITIALIZER);
  173. try {
  174. return DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_ON_COLUMN;
  175. } catch (IllegalArgumentException e) {
  176. // ignore and return default
  177. }
  178. return true;
  179. }
  180. private boolean prefTernaryDeepAlign() {
  181. String option= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_CONDITIONAL_EXPRESSION);
  182. try {
  183. return DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_ON_COLUMN;
  184. } catch (IllegalArgumentException e) {
  185. // ignore and return default
  186. }
  187. return false;
  188. }
  189. private int prefTernaryIndent() {
  190. String option= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_CONDITIONAL_EXPRESSION);
  191. try {
  192. if (DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_BY_ONE)
  193. return 1;
  194. else
  195. return prefContinuationIndent();
  196. } catch (IllegalArgumentException e) {
  197. // ignore and return default
  198. }
  199. return prefContinuationIndent();
  200. }
  201. private int prefCaseIndent() {
  202. if (DefaultCodeFormatterConstants.TRUE.equals(getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_INDENT_SWITCHSTATEMENTS_COMPARE_TO_SWITCH)))
  203. return prefBlockIndent();
  204. else
  205. return 0;
  206. }
  207. private int prefAssignmentIndent() {
  208. return prefBlockIndent();
  209. }
  210. private int prefCaseBlockIndent() {
  211. if (true)
  212. return prefBlockIndent();
  213. if (DefaultCodeFormatterConstants.TRUE.equals(getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_INDENT_SWITCHSTATEMENTS_COMPARE_TO_CASES)))
  214. return prefBlockIndent();
  215. else
  216. return 0;
  217. }
  218. private int prefSimpleIndent() {
  219. if (prefIndentBracesForBlocks() && prefBlockIndent() == 0)
  220. return 1;
  221. else return prefBlockIndent();
  222. }
  223. private int prefBracketIndent() {
  224. return prefBlockIndent();
  225. }
  226. private boolean prefMethodDeclDeepIndent() {
  227. String option= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_PARAMETERS_IN_METHOD_DECLARATION);
  228. try {
  229. return DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_ON_COLUMN;
  230. } catch (IllegalArgumentException e) {
  231. // ignore and return default
  232. }
  233. return true;
  234. }
  235. private int prefMethodDeclIndent() {
  236. String option= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_PARAMETERS_IN_METHOD_DECLARATION);
  237. try {
  238. if (DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_BY_ONE)
  239. return 1;
  240. else
  241. return prefContinuationIndent();
  242. } catch (IllegalArgumentException e) {
  243. // ignore and return default
  244. }
  245. return 1;
  246. }
  247. private boolean prefMethodCallDeepIndent() {
  248. String option= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_ARGUMENTS_IN_METHOD_INVOCATION);
  249. try {
  250. return DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_ON_COLUMN;
  251. } catch (IllegalArgumentException e) {
  252. // ignore and return default
  253. }
  254. return false; // sensible default
  255. }
  256. private int prefMethodCallIndent() {
  257. String option= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_ARGUMENTS_IN_METHOD_INVOCATION);
  258. try {
  259. if (DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_BY_ONE)
  260. return 1;
  261. else
  262. return prefContinuationIndent();
  263. } catch (IllegalArgumentException e) {
  264. // ignore and return default
  265. }
  266. return 1; // sensible default
  267. }
  268. private boolean prefParenthesisDeepIndent() {
  269. if (true) // don't do parenthesis deep indentation
  270. return false;
  271. String option= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_CONTINUATION_INDENTATION);
  272. try {
  273. return DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_ON_COLUMN;
  274. } catch (IllegalArgumentException e) {
  275. // ignore and return default
  276. }
  277. return false; // sensible default
  278. }
  279. private int prefParenthesisIndent() {
  280. return prefContinuationIndent();
  281. }
  282. private int prefBlockIndent() {
  283. String option= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_INDENT_STATEMENTS_COMPARE_TO_BLOCK);
  284. if (DefaultCodeFormatterConstants.FALSE.equals(option))
  285. return 0;
  286. return 1; // sensible default
  287. }
  288. private int prefMethodBodyIndent() {
  289. if (DefaultCodeFormatterConstants.FALSE.equals(getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_INDENT_STATEMENTS_COMPARE_TO_BODY)))
  290. return 0;
  291. return 1; // sensible default
  292. }
  293. private int prefTypeIndent() {
  294. String option= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_INDENT_BODY_DECLARATIONS_COMPARE_TO_TYPE_HEADER);
  295. if (DefaultCodeFormatterConstants.FALSE.equals(option))
  296. return 0;
  297. return 1; // sensible default
  298. }
  299. private boolean prefIndentBracesForBlocks() {
  300. return DefaultCodeFormatterConstants.NEXT_LINE_SHIFTED.equals(getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_BRACE_POSITION_FOR_BLOCK));
  301. }
  302. private boolean prefIndentBracesForArrays() {
  303. return DefaultCodeFormatterConstants.NEXT_LINE_SHIFTED.equals(getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_BRACE_POSITION_FOR_ARRAY_INITIALIZER));
  304. }
  305. private boolean prefIndentBracesForMethods() {
  306. return DefaultCodeFormatterConstants.NEXT_LINE_SHIFTED.equals(getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_BRACE_POSITION_FOR_METHOD_DECLARATION));
  307. }
  308. private boolean prefIndentBracesForTypes() {
  309. return DefaultCodeFormatterConstants.NEXT_LINE_SHIFTED.equals(getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_BRACE_POSITION_FOR_TYPE_DECLARATION));
  310. }
  311. private int prefContinuationIndent() {
  312. try {
  313. return Integer.parseInt(getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_CONTINUATION_INDENTATION));
  314. } catch (NumberFormatException e) {
  315. // ignore and return default
  316. }
  317. return 2; // sensible default
  318. }
  319. private boolean hasGenerics() {
  320. return JavaCore.VERSION_1_5.compareTo(getCoreFormatterOption(JavaCore.COMPILER_SOURCE)) <= 0;
  321. }
  322. }
  323. /** The document being scanned. */
  324. private final IDocument fDocument;
  325. /** The indentation accumulated by <code>findReferencePosition</code>. */
  326. private int fIndent;
  327. /**
  328. * The absolute (character-counted) indentation offset for special cases
  329. * (method defs, array initializers)
  330. */
  331. private int fAlign;
  332. /** The stateful scanposition for the indentation methods. */
  333. private int fPosition;
  334. /** The previous position. */
  335. private int fPreviousPos;
  336. /** The most recent token. */
  337. private int fToken;
  338. /** The line of <code>fPosition</code>. */
  339. private int fLine;
  340. /**
  341. * The scanner we will use to scan the document. It has to be installed
  342. * on the same document as the one we get.
  343. */
  344. private final JavaHeuristicScanner fScanner;
  345. /**
  346. * The JDT Core preferences.
  347. * @since 3.2
  348. */
  349. private final CorePrefs fPrefs;
  350. /**
  351. * Creates a new instance.
  352. *
  353. * @param document the document to scan
  354. * @param scanner the {@link JavaHeuristicScanner} to be used for scanning
  355. * the document. It must be installed on the same <code>IDocument</code>.
  356. */
  357. public GroovyIndenter(IDocument document, JavaHeuristicScanner scanner) {
  358. this(document, scanner, null);
  359. }
  360. /**
  361. * Creates a new instance.
  362. *
  363. * @param document the document to scan
  364. * @param scanner the {@link JavaHeuristicScanner}to be used for scanning
  365. * the document. It must be installed on the same
  366. * <code>IDocument</code>.
  367. * @param project the java project to get the formatter preferences from, or
  368. * <code>null</code> to use the workspace settings
  369. * @since 3.1
  370. */
  371. public GroovyIndenter(IDocument document, JavaHeuristicScanner scanner, IJavaProject project) {
  372. assert document != null;
  373. assert scanner != null;
  374. fDocument= document;
  375. fScanner= scanner;
  376. fPrefs= new CorePrefs(project);
  377. }
  378. /**
  379. * Computes the indentation at the reference point of <code>position</code>.
  380. *
  381. * @param offset the offset in the document
  382. * @return a String which reflects the indentation at the line in which the
  383. * reference position to <code>offset</code> resides, or <code>null</code>
  384. * if it cannot be determined
  385. */
  386. public StringBuffer getReferenceIndentation(int offset) {
  387. return getReferenceIndentation(offset, false);
  388. }
  389. /**
  390. * Computes the indentation at the reference point of <code>position</code>.
  391. *
  392. * @param offset the offset in the document
  393. * @param assumeOpeningBrace <code>true</code> if an opening brace should be assumed
  394. * @return a String which reflects the indentation at the line in which the
  395. * reference position to <code>offset</code> resides, or <code>null</code>
  396. * if it cannot be determined
  397. */
  398. private StringBuffer getReferenceIndentation(int offset, boolean assumeOpeningBrace) {
  399. int unit;
  400. if (assumeOpeningBrace)
  401. unit= findReferencePosition(offset, Symbols.TokenLBRACE);
  402. else
  403. unit= findReferencePosition(offset, peekChar(offset));
  404. // if we were unable to find anything, return null
  405. if (unit == JavaHeuristicScanner.NOT_FOUND)
  406. return null;
  407. return getLeadingWhitespace(unit);
  408. }
  409. /**
  410. * Computes the indentation at <code>offset</code>.
  411. *
  412. * @param offset the offset in the document
  413. * @return a String which reflects the correct indentation for the line in
  414. * which offset resides, or <code>null</code> if it cannot be
  415. * determined
  416. */
  417. public StringBuffer computeIndentation(int offset) {
  418. return computeIndentation(offset, false);
  419. }
  420. /**
  421. * Computes the indentation at <code>offset</code>.
  422. *
  423. * @param offset the offset in the document
  424. * @param assumeOpeningBrace <code>true</code> if an opening brace should be assumed
  425. * @return a String which reflects the correct indentation for the line in
  426. * which offset resides, or <code>null</code> if it cannot be
  427. * determined
  428. */
  429. public StringBuffer computeIndentation(int offset, boolean assumeOpeningBrace) {
  430. StringBuffer reference= getReferenceIndentation(offset, assumeOpeningBrace);
  431. // handle special alignment
  432. if (fAlign != JavaHeuristicScanner.NOT_FOUND) {
  433. try {
  434. // a special case has been detected.
  435. IRegion line= fDocument.getLineInformationOfOffset(fAlign);
  436. int lineOffset= line.getOffset();
  437. return createIndent(lineOffset, fAlign, false);
  438. } catch (BadLocationException e) {
  439. return null;
  440. }
  441. }
  442. if (reference == null)
  443. return null;
  444. // add additional indent
  445. return createReusingIndent(reference, fIndent);
  446. }
  447. /**
  448. * Computes the length of a <code>CharacterSequence</code>, counting
  449. * a tab character as the size until the next tab stop and every other
  450. * character as one.
  451. *
  452. * @param indent the string to measure
  453. * @return the visual length in characters
  454. */
  455. private int computeVisualLength(CharSequence indent) {
  456. final int tabSize= fPrefs.prefTabSize;
  457. int length= 0;
  458. for (int i= 0; i < indent.length(); i++) {
  459. char ch= indent.charAt(i);
  460. switch (ch) {
  461. case '\t':
  462. if (tabSize > 0) {
  463. int reminder= length % tabSize;
  464. length += tabSize - reminder;
  465. }
  466. break;
  467. case ' ':
  468. length++;
  469. break;
  470. }
  471. }
  472. return length;
  473. }
  474. /**
  475. * Strips any characters off the end of <code>reference</code> that exceed
  476. * <code>indentLength</code>.
  477. *
  478. * @param reference the string to measure
  479. * @param indentLength the maximum visual indentation length
  480. * @return the stripped <code>reference</code>
  481. */
  482. private StringBuffer stripExceedingChars(StringBuffer reference, int indentLength) {
  483. final int tabSize= fPrefs.prefTabSize;
  484. int measured= 0;
  485. int chars= reference.length();
  486. int i= 0;
  487. for (; measured < indentLength && i < chars; i++) {
  488. char ch= reference.charAt(i);
  489. switch (ch) {
  490. case '\t':
  491. if (tabSize > 0) {
  492. int reminder= measured % tabSize;
  493. measured += tabSize - reminder;
  494. }
  495. break;
  496. case ' ':
  497. measured++;
  498. break;
  499. }
  500. }
  501. int deleteFrom= measured > indentLength ? i - 1 : i;
  502. return reference.delete(deleteFrom, chars);
  503. }
  504. /**
  505. * Returns the indentation of the line at <code>offset</code> as a
  506. * <code>StringBuffer</code>. If the offset is not valid, the empty string
  507. * is returned.
  508. *
  509. * @param offset the offset in the document
  510. * @return the indentation (leading whitespace) of the line in which
  511. * <code>offset</code> is located
  512. */
  513. private StringBuffer getLeadingWhitespace(int offset) {
  514. StringBuffer indent= new StringBuffer();
  515. try {
  516. IRegion line= fDocument.getLineInformationOfOffset(offset);
  517. int lineOffset= line.getOffset();
  518. int nonWS= fScanner.findNonWhitespaceForwardInAnyPartition(lineOffset, lineOffset + line.getLength());
  519. indent.append(fDocument.get(lineOffset, nonWS - lineOffset));
  520. return indent;
  521. } catch (BadLocationException e) {
  522. return indent;
  523. }
  524. }
  525. /**
  526. * Creates an indentation string of the length indent - start, consisting of
  527. * the content in <code>fDocument</code> in the range [start, indent),
  528. * with every character replaced by a space except for tabs, which are kept
  529. * as such.
  530. * <p>
  531. * If <code>convertSpaceRunsToTabs</code> is <code>true</code>, every
  532. * run of the number of spaces that make up a tab are replaced by a tab
  533. * character. If it is not set, no conversion takes place, but tabs in the
  534. * original range are still copied verbatim.
  535. * </p>
  536. *
  537. * @param start the start of the document region to copy the indent from
  538. * @param indent the exclusive end of the document region to copy the indent
  539. * from
  540. * @param convertSpaceRunsToTabs whether to convert consecutive runs of
  541. * spaces to tabs
  542. * @return the indentation corresponding to the document content specified
  543. * by <code>start</code> and <code>indent</code>
  544. */
  545. private StringBuffer createIndent(int start, final int indent, final boolean convertSpaceRunsToTabs) {
  546. final boolean convertTabs= fPrefs.prefUseTabs && convertSpaceRunsToTabs;
  547. final int tabLen= fPrefs.prefTabSize;
  548. final StringBuffer ret= new StringBuffer();
  549. try {
  550. int spaces= 0;
  551. while (start < indent) {
  552. char ch= fDocument.getChar(start);
  553. if (ch == '\t') {
  554. ret.append('\t');
  555. spaces= 0;
  556. } else if (convertTabs) {
  557. spaces++;
  558. if (spaces == tabLen) {
  559. ret.append('\t');
  560. spaces= 0;
  561. }
  562. } else {
  563. ret.append(' ');
  564. }
  565. start++;
  566. }
  567. // remainder
  568. while (spaces-- > 0)
  569. ret.append(' ');
  570. } catch (BadLocationException e) {
  571. }
  572. return ret;
  573. }
  574. /**
  575. * Creates a string with a visual length of the given
  576. * <code>indentationSize</code>.
  577. *
  578. * @param buffer the original indent to reuse if possible
  579. * @param additional the additional indentation units to add or subtract to
  580. * reference
  581. * @return the modified <code>buffer</code> reflecting the indentation
  582. * adapted to <code>additional</code>
  583. */
  584. private StringBuffer createReusingIndent(StringBuffer buffer, int additional) {
  585. int refLength= computeVisualLength(buffer);
  586. int addLength= fPrefs.prefIndentationSize * additional; // may be < 0
  587. int totalLength= Math.max(0, refLength + addLength);
  588. // copy the reference indentation for the indent up to the last tab
  589. // stop within the maxCopy area
  590. int minLength= Math.min(totalLength, refLength);
  591. int tabSize= fPrefs.prefTabSize;
  592. int maxCopyLength= tabSize > 0 ? minLength - minLength % tabSize : minLength; // maximum indent to copy
  593. stripExceedingChars(buffer, maxCopyLength);
  594. // add additional indent
  595. int missing= totalLength - maxCopyLength;
  596. final int tabs, spaces;
  597. if (JavaCore.SPACE.equals(fPrefs.prefTabChar)) {
  598. tabs= 0;
  599. spaces= missing;
  600. } else if (JavaCore.TAB.equals(fPrefs.prefTabChar)) {
  601. tabs= tabSize > 0 ? missing / tabSize : 0;
  602. spaces= tabSize > 0 ? missing % tabSize : missing;
  603. } else if (DefaultCodeFormatterConstants.MIXED.equals(fPrefs.prefTabChar)) {
  604. tabs= tabSize > 0 ? missing / tabSize : 0;
  605. spaces= tabSize > 0 ? missing % tabSize : missing;
  606. } else {
  607. assert false;
  608. return null;
  609. }
  610. for(int i= 0; i < tabs; i++)
  611. buffer.append('\t');
  612. for(int i= 0; i < spaces; i++)
  613. buffer.append(' ');
  614. return buffer;
  615. }
  616. /**
  617. * Returns the reference position regarding to indentation for <code>offset</code>,
  618. * or <code>NOT_FOUND</code>. This method calls
  619. * {@link #findReferencePosition(int, int) findReferencePosition(offset, nextChar)} where
  620. * <code>nextChar</code> is the next character after <code>offset</code>.
  621. *
  622. * @param offset the offset for which the reference is computed
  623. * @return the reference statement relative to which <code>offset</code>
  624. * should be indented, or {@link JavaHeuristicScanner#NOT_FOUND}
  625. */
  626. public int findReferencePosition(int offset) {
  627. return findReferencePosition(offset, peekChar(offset));
  628. }
  629. /**
  630. * Peeks the next char in the document that comes after <code>offset</code>
  631. * on the same line as <code>offset</code>.
  632. *
  633. * @param offset the offset into document
  634. * @return the token symbol of the next element, or TokenEOF if there is none
  635. */
  636. private int peekChar(int offset) {
  637. if (offset < fDocument.getLength()) {
  638. try {
  639. IRegion line= fDocument.getLineInformationOfOffset(offset);
  640. int lineOffset= line.getOffset();
  641. int next= fScanner.nextToken(offset, lineOffset + line.getLength());
  642. return next;
  643. } catch (BadLocationException e) {
  644. }
  645. }
  646. return Symbols.TokenEOF;
  647. }
  648. /**
  649. * Returns the reference position regarding to indentation for <code>position</code>,
  650. * or <code>NOT_FOUND</code>.
  651. *
  652. * <p>If <code>peekNextChar</code> is <code>true</code>, the next token after
  653. * <code>offset</code> is read and taken into account when computing the
  654. * indentation. Currently, if the next token is the first token on the line
  655. * (i.e. only preceded by whitespace), the following tokens are specially
  656. * handled:
  657. * <ul>
  658. * <li><code>switch</code> labels are indented relative to the switch block</li>
  659. * <li>opening curly braces are aligned correctly with the introducing code</li>
  660. * <li>closing curly braces are aligned properly with the introducing code of
  661. * the matching opening brace</li>
  662. * <li>closing parenthesis' are aligned with their opening peer</li>
  663. * <li>the <code>else</code> keyword is aligned with its <code>if</code>, anything
  664. * else is aligned normally (i.e. with the base of any introducing statements).</li>
  665. * <li>if there is no token on the same line after <code>offset</code>, the indentation
  666. * is the same as for an <code>else</code> keyword</li>
  667. * </ul>
  668. *
  669. * @param offset the offset for which the reference is computed
  670. * @param nextToken the next token to assume in the document
  671. * @return the reference statement relative to which <code>offset</code>
  672. * should be indented, or {@link JavaHeuristicScanner#NOT_FOUND}
  673. */
  674. public int findReferencePosition(int offset, int nextToken) {
  675. boolean danglingElse= false;
  676. boolean unindent= false;
  677. boolean indent= false;
  678. boolean matchBrace= false;
  679. boolean matchParen= false;
  680. boolean matchCase= false;
  681. // account for un-indentation characters already typed in, but after position
  682. // if they are on a line by themselves, the indentation gets adjusted
  683. // accordingly
  684. //
  685. // also account for a dangling else
  686. if (offset < fDocument.getLength()) {
  687. try {
  688. IRegion line= fDocument.getLineInformationOfOffset(offset);
  689. int lineOffset= line.getOffset();
  690. int prevPos= Math.max(offset - 1, 0);
  691. boolean isFirstTokenOnLine= fDocument.get(lineOffset, prevPos + 1 - lineOffset).trim().length() == 0;
  692. int prevToken= fScanner.previousToken(prevPos, JavaHeuristicScanner.UNBOUND);
  693. boolean bracelessBlockStart= fScanner.isBracelessBlockStart(prevPos, JavaHeuristicScanner.UNBOUND);
  694. switch (nextToken) {
  695. case Symbols.TokenELSE:
  696. danglingElse= true;
  697. break;
  698. case Symbols.TokenCASE:
  699. case Symbols.TokenDEFAULT:
  700. if (isFirstTokenOnLine)
  701. matchCase= true;
  702. break;
  703. case Symbols.TokenLBRACE: // for opening-brace-on-new-line style
  704. if (bracelessBlockStart && !fPrefs.prefIndentBracesForBlocks)
  705. unindent= true;
  706. else if ((prevToken == Symbols.TokenCOLON || prevToken == Symbols.TokenEQUAL || prevToken == Symbols.TokenRBRACKET) && !fPrefs.prefIndentBracesForArrays)
  707. unindent= true;
  708. else if (!bracelessBlockStart && fPrefs.prefIndentBracesForMethods)
  709. indent= true;
  710. break;
  711. case Symbols.TokenRBRACE: // closing braces get unindented
  712. if (isFirstTokenOnLine)
  713. matchBrace= true;
  714. break;
  715. case Symbols.TokenRPAREN:
  716. if (isFirstTokenOnLine)
  717. matchParen= true;
  718. break;
  719. }
  720. } catch (BadLocationException e) {
  721. }
  722. } else {
  723. // don't assume an else could come if we are at the end of file
  724. danglingElse= false;
  725. }
  726. int ref= findReferencePosition(offset, danglingElse, matchBrace, matchParen, matchCase);
  727. if (unindent)
  728. fIndent--;
  729. if (indent)
  730. fIndent++;
  731. return ref;
  732. }
  733. /**
  734. * Returns the reference position regarding to indentation for <code>position</code>,
  735. * or <code>NOT_FOUND</code>.<code>fIndent</code> will contain the
  736. * relative indentation (in indentation units, not characters) after the
  737. * call. If there is a special alignment (e.g. for a method declaration
  738. * where parameters should be aligned), <code>fAlign</code> will contain
  739. * the absolute position of the alignment reference in <code>fDocument</code>,
  740. * otherwise <code>fAlign</code> is set to <code>JavaHeuristicScanner.NOT_FOUND</code>.
  741. *
  742. * @param offset the offset for which the reference is computed
  743. * @param danglingElse whether a dangling else should be assumed at <code>position</code>
  744. * @param matchBrace whether the position of the matching brace should be
  745. * returned instead of doing code analysis
  746. * @param matchParen whether the position of the matching parenthesis
  747. * should be returned instead of doing code analysis
  748. * @param matchCase whether the position of a switch statement reference
  749. * should be returned (either an earlier case statement or the
  750. * switch block brace)
  751. * @return the reference statement relative to which <code>position</code>
  752. * should be indented, or {@link JavaHeuristicScanner#NOT_FOUND}
  753. */
  754. public int findReferencePosition(int offset, boolean danglingElse, boolean matchBrace, boolean matchParen, boolean matchCase) {
  755. fIndent= 0; // the indentation modification
  756. fAlign= JavaHeuristicScanner.NOT_FOUND;
  757. fPosition= offset;
  758. // forward cases
  759. // an unindentation happens sometimes if the next token is special, namely on braces, parens and case labels
  760. // align braces, but handle the case where we align with the method declaration start instead of
  761. // the opening brace.
  762. if (matchBrace) {
  763. if (skipScope(Symbols.TokenLBRACE, Symbols.TokenRBRACE)) {
  764. try {
  765. // align with the opening brace that is on a line by its own
  766. int lineOffset= fDocument.getLineOffset(fLine);
  767. if (lineOffset <= fPosition && fDocument.get(lineOffset, fPosition - lineOffset).trim().length() == 0)
  768. return fPosition;
  769. } catch (BadLocationException e) {
  770. // concurrent modification - walk default path
  771. }
  772. // if the opening brace is not on the start of the line, skip to the start
  773. int pos= skipToStatementStart(true, true);
  774. fIndent= 0; // indent is aligned with reference position
  775. return pos;
  776. }
  777. // if we can't find the matching brace, the heuristic is to unindent
  778. // by one against the normal position
  779. int pos= findReferencePosition(offset, danglingElse, false, matchParen, matchCase);
  780. fIndent--;
  781. return pos;
  782. }
  783. // align parenthesis'
  784. if (matchParen) {
  785. if (skipScope(Symbols.TokenLPAREN, Symbols.TokenRPAREN)){
  786. return fPosition;
  787. }
  788. // if we can't find the matching paren, the heuristic is to unindent
  789. // by one against the normal position
  790. int pos= findReferencePosition(offset, danglingElse, matchBrace, false, matchCase);
  791. fIndent--;
  792. return pos;
  793. }
  794. // the only reliable way to get case labels aligned (due to many different styles of using braces in a block)
  795. // is to go for another case statement, or the scope opening brace
  796. if (matchCase) {
  797. return matchCaseAlignment();
  798. }
  799. nextToken();
  800. switch (fToken) {
  801. case Symbols.TokenGREATERTHAN:
  802. case Symbols.TokenRBRACE:
  803. case Symbols.TokenRBRACKET:
  804. // skip the block and fall through
  805. // if we can't complete the scope, reset the scan position
  806. int pos= fPosition;
  807. if (!skipScope())
  808. fPosition= pos;
  809. case Symbols.TokenSEMICOLON:
  810. // this is the 90% case: after a statement block
  811. // the end of the previous statement / block previous.end
  812. // search to the end of the statement / block before the previous; the token just after that is previous.start
  813. return skipToStatementStart(danglingElse, false);
  814. // scope introduction: special treat who special is
  815. case Symbols.TokenLPAREN:
  816. case Symbols.TokenLBRACE:
  817. case Symbols.TokenLBRACKET:
  818. return handleScopeIntroduction(offset + 1);
  819. case Symbols.TokenEOF:
  820. // trap when hitting start of document
  821. return JavaHeuristicScanner.NOT_FOUND;
  822. case Symbols.TokenEQUAL:
  823. // indent assignments
  824. fIndent= fPrefs.prefAssignmentIndent;
  825. return fPosition;
  826. case Symbols.TokenCOLON:
  827. // TODO handle ternary deep indentation
  828. fIndent= fPrefs.prefCaseBlockIndent;
  829. return fPosition;
  830. case Symbols.TokenQUESTIONMARK:
  831. if (fPrefs.prefTernaryDeepAlign) {
  832. setFirstElementAlignment(fPosition, offset + 1);
  833. return fPosition;
  834. }
  835. fIndent= fPrefs.prefTernaryIndent;
  836. return fPosition;
  837. // indentation for blockless introducers:
  838. case Symbols.TokenDO:
  839. case Symbols.TokenWHILE:
  840. case Symbols.TokenELSE:
  841. fIndent= fPrefs.prefSimpleIndent;
  842. return fPosition;
  843. case Symbols.TokenTRY:
  844. return skipToStatementStart(danglingElse, false);
  845. case Symbols.TokenRPAREN:
  846. int line= fLine;
  847. if (skipScope(Symbols.TokenLPAREN, Symbols.TokenRPAREN)) {
  848. int scope= fPosition;
  849. nextToken();
  850. if (fToken == Symbols.TokenIF || fToken == Symbols.TokenWHILE || fToken == Symbols.TokenFOR) {
  851. fIndent= fPrefs.prefSimpleIndent;
  852. return fPosition;
  853. }
  854. fPosition= scope;
  855. if (looksLikeMethodDecl()) {
  856. return skipToStatementStart(danglingElse, false);
  857. }
  858. if (fToken == Symbols.TokenCATCH) {
  859. return skipToStatementStart(danglingElse, false);
  860. }
  861. fPosition= scope;
  862. if (looksLikeAnonymousTypeDecl()) {
  863. return skipToStatementStart(danglingElse, false);
  864. }
  865. }
  866. // restore
  867. fPosition= offset;
  868. fLine= line;
  869. // else: fall through to default
  870. case Symbols.TokenCOMMA:
  871. // inside a list of some type
  872. // easy if there is already a list item before with its own indentation - we just align
  873. // if not: take the start of the list ( LPAREN, LBRACE, LBRACKET ) and either align or
  874. // indent by list-indent
  875. default:
  876. // inside whatever we don't know about: similar to the list case:
  877. // if we are inside a continued expression, then either align with a previous line that has indentation
  878. // or indent from the expression start line (either a scope introducer or the start of the expr).
  879. return skipToPreviousListItemOrListStart();
  880. }
  881. }
  882. /**
  883. * Skips to the start of a statement that ends at the current position.
  884. *
  885. * @param danglingElse whether to indent aligned with the last <code>if</code>
  886. * @param isInBlock whether the current position is inside a block, which limits the search scope to the next scope introducer
  887. * @return the reference offset of the start of the statement
  888. */
  889. private int skipToStatementStart(boolean danglingElse, boolean isInBlock) {
  890. final int NOTHING= 0;
  891. final int READ_PARENS= 1;
  892. final int READ_IDENT= 2;
  893. int mayBeMethodBody= NOTHING;
  894. boolean isTypeBody= false;
  895. while (true) {
  896. nextToken();
  897. if (isInBlock) {
  898. switch (fToken) {
  899. // exit on all block introducers
  900. case Symbols.TokenIF:
  901. case Symbols.TokenELSE:
  902. case Symbols.TokenCATCH:
  903. case Symbols.TokenDO:
  904. case Symbols.TokenWHILE:
  905. case Symbols.TokenFINALLY:
  906. case Symbols.TokenFOR:
  907. case Symbols.TokenTRY:
  908. return fPosition;
  909. case Symbols.TokenSTATIC:
  910. mayBeMethodBody= READ_IDENT; // treat static blocks like methods
  911. break;
  912. case Symbols.TokenSYNCHRONIZED:
  913. // if inside a method declaration, use body indentation
  914. // else use block indentation.
  915. if (mayBeMethodBody != READ_IDENT)
  916. return fPosition;
  917. break;
  918. case Symbols.TokenCLASS:
  919. case Symbols.TokenINTERFACE:
  920. case Symbols.TokenENUM:
  921. isTypeBody= true;
  922. break;
  923. case Symbols.TokenSWITCH:
  924. fIndent= fPrefs.prefCaseIndent;
  925. return fPosition;
  926. }
  927. }
  928. switch (fToken) {
  929. // scope introduction through: LPAREN, LBRACE, LBRACKET
  930. // search stop on SEMICOLON, RBRACE, COLON, EOF
  931. // -> the next token is the start of the statement (i.e. previousPos when backward scanning)
  932. case Symbols.TokenLPAREN:
  933. case Symbols.TokenLBRACE:
  934. case Symbols.TokenLBRACKET:
  935. case Symbols.TokenSEMICOLON:
  936. case Symbols.TokenEOF:
  937. if (isInBlock)
  938. fIndent= getBlockIndent(mayBeMethodBody == READ_IDENT, isTypeBody);
  939. // else: fIndent set by previous calls
  940. return fPreviousPos;
  941. case Symbols.TokenCOLON:
  942. int pos= fPreviousPos;
  943. if (!isConditional())
  944. return pos;
  945. break;
  946. case Symbols.TokenRBRACE:
  947. // RBRACE is a little tricky: it can be the end of an array definition, but
  948. // usually it is the end of a previous block
  949. pos= fPreviousPos; // store state
  950. if (skipScope() && looksLikeArrayInitializerIntro()) {
  951. continue; // it's an array
  952. } else {
  953. if (isInBlock)
  954. fIndent= getBlockIndent(mayBeMethodBody == READ_IDENT, isTypeBody);
  955. return pos; // it's not - do as with all the above
  956. }
  957. // scopes: skip them
  958. case Symbols.TokenRPAREN:
  959. if (isInBlock)
  960. mayBeMethodBody= READ_PARENS;
  961. case Symbols.TokenRBRACKET:
  962. case Symbols.TokenGREATERTHAN:
  963. pos= fPreviousPos;
  964. if (skipScope())
  965. break;
  966. else
  967. return pos;
  968. // IF / ELSE: align the position after the conditional block with the if
  969. // so we are ready for an else, except if danglingElse is false
  970. // in order for this to work, we must skip an else to its if
  971. case Symbols.TokenIF:
  972. if (danglingElse)
  973. return fPosition;
  974. else
  975. break;
  976. case Symbols.TokenELSE:
  977. // skip behind the next if, as we have that one covered
  978. pos= fPosition;
  979. if (skipNextIF())
  980. break;
  981. else
  982. return pos;
  983. case Symbols.TokenDO:
  984. // align the WHILE position with its do
  985. return fPosition;
  986. case Symbols.TokenWHILE:
  987. // this one is tricky: while can be the start of a while loop
  988. // or the end of a do - while
  989. pos= fPosition;
  990. if (hasMatchingDo()) {
  991. // continue searching from the DO on
  992. break;
  993. } else {
  994. // continue searching from the WHILE on
  995. fPosition= pos;
  996. break;
  997. }
  998. case Symbols.TokenIDENT:
  999. if (mayBeMethodBody == READ_PARENS)
  1000. mayBeMethodBody= READ_IDENT;
  1001. break;
  1002. default:
  1003. // keep searching
  1004. }
  1005. }
  1006. }
  1007. private int getBlockIndent(boolean isMethodBody, boolean isTypeBody) {
  1008. if (isTypeBody)
  1009. return fPrefs.prefTypeIndent + (fPrefs.prefIndentBracesForTypes ? 1 : 0);
  1010. else if (isMethodBody)
  1011. return fPrefs.prefMethodBodyIndent + (fPrefs.prefIndentBracesForMethods ? 1 : 0);
  1012. else
  1013. return fIndent;
  1014. }
  1015. /**
  1016. * Returns true if the colon at the current position is part of a conditional
  1017. * (ternary) expression, false otherwise.
  1018. *
  1019. * @return true if the colon at the current position is part of a conditional
  1020. */
  1021. private boolean isConditional() {
  1022. while (true) {
  1023. nextToken();
  1024. switch (fToken) {
  1025. // search for case labels, which consist of (possibly qualified) identifiers or numbers
  1026. case Symbols.TokenIDENT:
  1027. case Symbols.TokenOTHER: // dots for qualified constants
  1028. continue;
  1029. case Symbols.TokenCASE:
  1030. return false;
  1031. default:
  1032. return true;
  1033. }
  1034. }
  1035. }
  1036. /**
  1037. * Returns as a reference any previous <code>switch</code> labels (<code>case</code>
  1038. * or <code>default</code>) or the offset of the brace that scopes the switch
  1039. * statement. Sets <code>fIndent</code> to <code>prefCaseIndent</code> upon
  1040. * a match.
  1041. *
  1042. * @return the reference offset for a <code>switch</code> label
  1043. */
  1044. private int matchCaseAlignment() {
  1045. while (true) {
  1046. nextToken();
  1047. switch (fToken) {
  1048. // invalid cases: another case label or an LBRACE must come before a case
  1049. // -> bail out with the current position
  1050. case Symbols.TokenLPAREN:
  1051. case Symbols.TokenLBRACKET:
  1052. case Symbols.TokenEOF:
  1053. return fPosition;
  1054. case Symbols.TokenLBRACE:
  1055. // opening brace of switch statement
  1056. fIndent= fPrefs.prefCaseIndent;
  1057. return fPosition;
  1058. case Symbols.TokenCASE:
  1059. case Symbols.TokenDEFAULT:
  1060. // align with previous label
  1061. fIndent= 0;
  1062. return fPosition;
  1063. // scopes: skip them
  1064. case Symbols.TokenRPAREN:
  1065. case Symbols.TokenRBRACKET:
  1066. case Symbols.TokenRBRACE:
  1067. case Symbols.TokenGREATERTHAN:
  1068. skipScope();
  1069. break;
  1070. default:
  1071. // keep searching
  1072. continue;
  1073. }
  1074. }
  1075. }
  1076. /**
  1077. * Returns the reference position for a list element. The algorithm
  1078. * tries to match any previous indentation on the same list. If there is none,
  1079. * the reference position returned is determined depending on the type of list:
  1080. * The indentation will either match the list scope introducer (e.g. for
  1081. * method declarations), so called deep indents, or simply increase the
  1082. * indentation by a number of standard indents. See also {@link #handleScopeIntroduction(int)}.
  1083. *
  1084. * @return the reference position for a list item: either a previous list item
  1085. * that has its own indentation, or the list introduction start.
  1086. */
  1087. private int skipToPreviousListItemOrListStart() {
  1088. int startLine= fLine;
  1089. int startPosition= fPosition;
  1090. while (true) {
  1091. nextToken();
  1092. // if any line item comes with its own indentation, adapt to it
  1093. if (fLine < startLine) {
  1094. try {
  1095. int lineOffset= fDocument.getLineOffset(startLine);
  1096. int bound= Math.min(fDocument.getLength(), startPosition + 1);
  1097. fAlign= fScanner.findNonWhitespaceForwardInAnyPartition(lineOffset, bound);
  1098. } catch (BadLocationException e) {
  1099. // ignore and return just the position
  1100. }
  1101. return startPosition;
  1102. }
  1103. switch (fToken) {
  1104. // scopes: skip them
  1105. case Symbols.TokenRPAREN:
  1106. case Symbols.TokenRBRACKET:
  1107. case Symbols.TokenRBRACE:
  1108. case Symbols.TokenGREATERTHAN:
  1109. skipScope();
  1110. break;
  1111. // scope introduction: special treat who special is
  1112. case Symbols.TokenLPAREN:
  1113. case Symbols.TokenLBRACE:
  1114. case Symbols.TokenLBRACKET:
  1115. return handleScopeIntroduction(startPosition + 1);
  1116. case Symbols.TokenSEMICOLON:
  1117. return fPosition;
  1118. case Symbols.TokenQUESTIONMARK:
  1119. if (fPrefs.prefTernaryDeepAlign) {
  1120. setFirstElementAlignment(fPosition - 1, fPosition + 1);
  1121. return fPosition;
  1122. } else {
  1123. fIndent= fPrefs.prefTernaryIndent;
  1124. return fPosition;
  1125. }
  1126. case Symbols.TokenEOF:
  1127. return 0;
  1128. }
  1129. }
  1130. }
  1131. /**
  1132. * Skips a scope and positions the cursor (<code>fPosition</code>) on the
  1133. * token that opens the scope. Returns <code>true</code> if a matching peer
  1134. * could be found, <code>false</code> otherwise. The current token when calling
  1135. * must be one out of <code>Symbols.TokenRPAREN</code>, <code>Symbols.TokenRBRACE</code>,
  1136. * and <code>Symbols.TokenRBRACKET</code>.
  1137. *
  1138. * @return <code>true</code> if a matching peer was found, <code>false</code> otherwise
  1139. */
  1140. private boolean skipScope() {
  1141. switch (fToken) {
  1142. case Symbols.TokenRPAREN:
  1143. return skipScope(Symbols.TokenLPAREN, Symbols.TokenRPAREN);
  1144. case Symbols.TokenRBRACKET:
  1145. return skipScope(Symbols.TokenLBRACKET, Symbols.TokenRBRACKET);
  1146. case Symbols.TokenRBRACE:
  1147. return skipScope(Symbols.TokenLBRACE, Symbols.TokenRBRACE);
  1148. case Symbols.TokenGREATERTHAN:
  1149. if (!fPrefs.prefHasGenerics)
  1150. return false;
  1151. int storedPosition= fPosition;
  1152. int storedToken= fToken;
  1153. nextToken();
  1154. switch (fToken) {
  1155. case Symbols.TokenIDENT:
  1156. if (!isGenericStarter(getTokenContent()))
  1157. break;
  1158. case Symbols.TokenQUESTIONMARK:
  1159. case Symbols.TokenGREATERTHAN:
  1160. if (skipScope(Symbols.TokenLESSTHAN, Symbols.TokenGREATERTHAN))
  1161. return true;
  1162. }
  1163. // <> are harder to detect - restore the position if we fail
  1164. fPosition= storedPosition;
  1165. fToken= storedToken;
  1166. return false;
  1167. default:
  1168. assert false;
  1169. return false;
  1170. }
  1171. }
  1172. /**
  1173. * Returns the contents of the current token.
  1174. *
  1175. * @return the contents of the current token
  1176. * @since 3.1
  1177. */
  1178. private CharSequence getTokenContent() {
  1179. return new DocumentCharacterIterator(fDocument, fPosition, fPreviousPos);
  1180. }
  1181. /**
  1182. * Returns <code>true</code> if <code>identifier</code> is probably a
  1183. * type variable or type name, <code>false</code> if it is rather not.
  1184. * This is a heuristic.
  1185. *
  1186. * @param identifier the identifier to check
  1187. * @return <code>true</code> if <code>identifier</code> is probably a
  1188. * type variable or type name, <code>false</code> if not
  1189. * @since 3.1
  1190. */
  1191. private boolean isGenericStarter(CharSequence identifier) {
  1192. /* This heuristic allows any identifiers if they start with an upper
  1193. * case. This will fail when a comparison is made with constants:
  1194. *
  1195. * if (MAX > foo)
  1196. *
  1197. * will try to find the matching '<' which will never come
  1198. *
  1199. * Also, it will fail on lower case types and type variables
  1200. */
  1201. int length= identifier.length();
  1202. if (length > 0 && Character.isUpperCase(identifier.charAt(0))) {
  1203. for (int i= 0; i < length; i++) {
  1204. if (identifier.charAt(i) == '_')
  1205. return false;
  1206. }
  1207. return true;
  1208. }
  1209. return false;
  1210. }
  1211. /**
  1212. * Handles the introduction of a new scope. The current token must be one out
  1213. * of <code>Symbols.TokenLPAREN</code>, <code>Symbols.TokenLBRACE</code>,
  1214. * and <code>Symbols.TokenLBRACKET</code>. Returns as the reference position
  1215. * either the token introducing the scope or - if available - the first
  1216. * java token after that.
  1217. *
  1218. * <p>Depending on the type of scope introduction, the indentation will align
  1219. * (deep indenting) with the reference position (<code>fAlign</code> will be
  1220. * set to the reference position) or <code>fIndent</code> will be set to
  1221. * the number of indentation units.
  1222. * </p>
  1223. *
  1224. * @param bound the bound for the search for the first token after the scope
  1225. * introduction.
  1226. * @return the indent
  1227. */
  1228. private int handleScopeIntroduction(int bound) {
  1229. switch (fToken) {
  1230. // scope introduction: special treat who special is
  1231. case Symbols.TokenLPAREN:
  1232. int pos= fPosition; // store
  1233. // special: method declaration deep indentation
  1234. if (looksLikeMethodDecl()) {
  1235. if (fPrefs.prefMethodDeclDeepIndent)
  1236. return setFirstElementAlignment(pos, bound);
  1237. else {
  1238. fIndent= fPrefs.prefMethodDeclIndent;
  1239. return pos;
  1240. }
  1241. } else {
  1242. fPosition= pos;
  1243. if (looksLikeMethodCall()) {
  1244. if (fPrefs.prefMethodCallDeepIndent)
  1245. return setFirstElementAlignment(pos, bound);
  1246. else {
  1247. fIndent= fPrefs.prefMethodCallIndent;
  1248. return pos;
  1249. }
  1250. } else if (fPrefs.prefParenthesisDeepIndent)
  1251. return setFirstElementAlignment(pos, bound);
  1252. }
  1253. // normal: return the parenthesis as reference
  1254. fIndent= fPrefs.prefParenthesisIndent;
  1255. return pos;
  1256. case Symbols.TokenLBRACE:
  1257. pos= fPosition; // store
  1258. // special: array initializer
  1259. if (looksLikeArrayInitializerIntro())
  1260. if (fPrefs.prefArrayDeepIndent)
  1261. return setFirstElementAlignment(pos, bound);
  1262. else
  1263. fIndent= fPrefs.prefArrayIndent;
  1264. else
  1265. fIndent= fPrefs.prefBlockIndent;
  1266. // normal: skip to the statement start before the scope introducer
  1267. // opening braces are often on differently ending indents than e.g. a method definition
  1268. if (looksLikeArrayInitializerIntro() && !fPrefs.prefIndentBracesForArrays
  1269. || !fPrefs.prefIndentBracesForBlocks) {
  1270. fPosition= pos; // restore
  1271. return skipToStatementStart(true, true); // set to true to match the first if
  1272. } else {
  1273. return pos;
  1274. }
  1275. case Symbols.TokenLBRACKET:
  1276. pos= fPosition; // store
  1277. // special: method declaration deep indentation
  1278. if (fPrefs.prefArrayDimensionsDeepIndent) {
  1279. return setFirstElementAlignment(pos, bound);
  1280. }
  1281. // normal: return the bracket as reference
  1282. fIndent= fPrefs.prefBracketIndent;
  1283. return pos; // restore
  1284. default:
  1285. assert false;
  1286. return -1; // dummy
  1287. }
  1288. }
  1289. /**
  1290. * Sets the deep indent offset (<code>fAlign</code>) to either the offset
  1291. * right after <code>scopeIntroducerOffset</code> or - if available - the
  1292. * first Java token after <code>scopeIntroducerOffset</code>, but before
  1293. * <code>bound</code>.
  1294. *
  1295. * @param scopeIntroducerOffset the offset of the scope introducer
  1296. * @param bound the bound for the search for another element
  1297. * @return the reference position
  1298. */
  1299. private int setFirstElementAlignment(int scopeIntroducerOffset, int bound) {
  1300. int firstPossible= scopeIntroducerOffset + 1; // align with the first position after the scope intro
  1301. fAlign= fScanner.findNonWhitespaceForwardInAnyPartition(firstPossible, bound);
  1302. if (fAlign == JavaHeuristicScanner.NOT_FOUND)
  1303. fAlign= firstPossible;
  1304. return fAlign;
  1305. }
  1306. /**
  1307. * Returns <code>true</code> if the next token received after calling
  1308. * <code>nextToken</code> …

Large files files are truncated, but you can click here to view the full file