Added INDENT_EACH_LINE reindentation strategy
diff --git a/org.eclipse.photran.core.vpg.tests/reindenter-test-code/05-eachline-test.f90 b/org.eclipse.photran.core.vpg.tests/reindenter-test-code/05-eachline-test.f90
new file mode 100644
index 0000000..6ba1b56
--- /dev/null
+++ b/org.eclipse.photran.core.vpg.tests/reindenter-test-code/05-eachline-test.f90
@@ -0,0 +1,12 @@
+ program program !<<<<<START
+ print *, "Hello"
+ end program program
+
+ subroutine s
+ integer :: i
+ do i = 3, 4
+ do j = 5, 6
+ print *, i + j
+ end do
+ end do
+end subroutine !<<<<<END
diff --git a/org.eclipse.photran.core.vpg.tests/reindenter-test-code/05-eachline-test.f90.result b/org.eclipse.photran.core.vpg.tests/reindenter-test-code/05-eachline-test.f90.result
new file mode 100644
index 0000000..e2f0ef3
--- /dev/null
+++ b/org.eclipse.photran.core.vpg.tests/reindenter-test-code/05-eachline-test.f90.result
@@ -0,0 +1,12 @@
+program program !<<<<<START
+ print *, "Hello"
+end program program
+
+subroutine s
+ integer :: i
+ do i = 3, 4
+ do j = 5, 6
+ print *, i + j
+ end do
+ end do
+end subroutine !<<<<<END
diff --git a/org.eclipse.photran.core.vpg.tests/src/org/eclipse/photran/refactoring/tests/infrastructure/ReindenterTestCase.java b/org.eclipse.photran.core.vpg.tests/src/org/eclipse/photran/refactoring/tests/infrastructure/ReindenterTestCase.java
index df9166e..e8c0366 100644
--- a/org.eclipse.photran.core.vpg.tests/src/org/eclipse/photran/refactoring/tests/infrastructure/ReindenterTestCase.java
+++ b/org.eclipse.photran.core.vpg.tests/src/org/eclipse/photran/refactoring/tests/infrastructure/ReindenterTestCase.java
@@ -21,6 +21,7 @@
import org.eclipse.photran.core.vpg.PhotranVPG;
import org.eclipse.photran.internal.core.refactoring.infrastructure.Reindenter;
import org.eclipse.photran.internal.core.refactoring.infrastructure.SourcePrinter;
+import org.eclipse.photran.internal.core.refactoring.infrastructure.Reindenter.Strategy;
import org.eclipse.photran.refactoring.tests.RefactoringTestCase;
public class ReindenterTestCase extends RefactoringTestCase
@@ -28,14 +29,16 @@
private static final String DIR = "reindenter-test-code";
protected String filename = null;
+ protected Strategy strategy;
private int fromLine;
private int thruLine;
public ReindenterTestCase() {;} // when JUnit invokes a subclass outside a test suite
- public ReindenterTestCase(String filename, Object ignored) // avoid JUnit constructor
+ public ReindenterTestCase(String filename, Strategy strategy)
{
this.filename = filename;
+ this.strategy = strategy;
this.setName("test");
}
@@ -48,7 +51,7 @@
IFortranAST ast = PhotranVPG.getInstance().acquireTransientAST(thisFile);
assertNotNull(ast);
- Reindenter.reindent(fromLine, thruLine, ast);
+ Reindenter.reindent(fromLine, thruLine, ast, strategy);
thisFile.setContents(
new ByteArrayInputStream(SourcePrinter.getSourceCodeFromAST(ast).getBytes()),
diff --git a/org.eclipse.photran.core.vpg.tests/src/org/eclipse/photran/refactoring/tests/infrastructure/ReindenterTestSuite.java b/org.eclipse.photran.core.vpg.tests/src/org/eclipse/photran/refactoring/tests/infrastructure/ReindenterTestSuite.java
index 5fb50e3..d88fc9d 100644
--- a/org.eclipse.photran.core.vpg.tests/src/org/eclipse/photran/refactoring/tests/infrastructure/ReindenterTestSuite.java
+++ b/org.eclipse.photran.core.vpg.tests/src/org/eclipse/photran/refactoring/tests/infrastructure/ReindenterTestSuite.java
@@ -13,23 +13,26 @@
import junit.framework.Test;
import junit.framework.TestSuite;
+import org.eclipse.photran.internal.core.refactoring.infrastructure.Reindenter.Strategy;
+
public class ReindenterTestSuite extends TestSuite
{
public static Test suite() throws Exception
{
TestSuite suite = new TestSuite();
- suite.addTest(getSuiteFor("01-simple.f90"));
- suite.addTest(getSuiteFor("02-end-after.f90"));
- suite.addTest(getSuiteFor("03-blank-after.f90"));
- suite.addTest(getSuiteFor("04-guess-indent.f90"));
- //suite.addTest(getSuiteFor("0.f90"));
+ suite.addTest(getSuiteFor("01-simple.f90", Strategy.SHIFT_ENTIRE_BLOCK));
+ suite.addTest(getSuiteFor("02-end-after.f90", Strategy.SHIFT_ENTIRE_BLOCK));
+ suite.addTest(getSuiteFor("03-blank-after.f90", Strategy.SHIFT_ENTIRE_BLOCK));
+ suite.addTest(getSuiteFor("04-guess-indent.f90", Strategy.SHIFT_ENTIRE_BLOCK));
+ suite.addTest(getSuiteFor("05-eachline-test.f90", Strategy.REINDENT_EACH_LINE));
+ //suite.addTest(getSuiteFor("0.f90", Strategy));
return suite;
}
- private static TestSuite getSuiteFor(String baseFilename)
+ private static TestSuite getSuiteFor(String baseFilename, Strategy strategy)
{
TestSuite subSuite = new TestSuite("Reindenting " + baseFilename);
- subSuite.addTest(new ReindenterTestCase(baseFilename, null));
+ subSuite.addTest(new ReindenterTestCase(baseFilename, strategy));
return subSuite;
}
}
diff --git a/org.eclipse.photran.core.vpg/src/org/eclipse/photran/internal/core/refactoring/infrastructure/Reindenter.java b/org.eclipse.photran.core.vpg/src/org/eclipse/photran/internal/core/refactoring/infrastructure/Reindenter.java
index c376b56..44d8f41 100644
--- a/org.eclipse.photran.core.vpg/src/org/eclipse/photran/internal/core/refactoring/infrastructure/Reindenter.java
+++ b/org.eclipse.photran.core.vpg/src/org/eclipse/photran/internal/core/refactoring/infrastructure/Reindenter.java
@@ -25,63 +25,115 @@
* The Reindenter is used to correct indentation when a node is inserted or
* moved in an AST so that the affected lines are correctly indented in their
* new context.
- * <p>
- * The algorithm is fairly straightforward:
- * <ul>
- * <li> Usually, the reindented region is adjusted to match the indentation of
- * the next non-empty line following the region.
- * <li> However, if the next non-empty line starts with "END" (or something),
- * the reindented region is adjusted to match the indentation of
- * the last non-empty line above the region.
- * <li> However, if the last non-empty line above starts with a token like
- * "PROGRAM" or "IF," then a guess is made, and the indentation is set
- * to match the following line, but four spaces are added.
- * </ul>
- * <p>
- * This code is very specific to the way Photran's parser uses the whiteBefore and
- * whiteAfter parts of a Token; it will not generalize to parsers that, say, include
- * newlines in the whitetext.
*
* @author Jeff Overbey
*/
+/*
+ * This code is very specific to the way Photran's parser uses the whiteBefore and
+ * whiteAfter parts of a Token; it will not generalize to parsers that, say, include
+ * newlines in the whitetext.
+ */
public class Reindenter
{
+ public static enum Strategy
+ {
+ /**
+ * Shifts the entire region left or right to match its new surroundings, keeping
+ * the relative indentation of each line the same.
+ * <p>
+ * The algorithm is as follows:
+ * <ul>
+ * <li> Usually, the reindented region is adjusted to match the indentation of
+ * the next non-empty line following the region.
+ * <li> However, if the next non-empty line starts with "END" (or something),
+ * the reindented region is adjusted to match the indentation of
+ * the last non-empty line above the region.
+ * <li> However, if the last non-empty line above starts with a token like
+ * "PROGRAM" or "IF," then a guess is made, and the indentation is set
+ * to match the following line, but four spaces are added.
+ * </ul>
+ */
+ SHIFT_ENTIRE_BLOCK
+ {
+ @Override protected ReindentingVisitor createVisitor(Reindenter reindenter, Token firstTokenInRegion, Token lastTokenInRegion)
+ {
+ return reindenter.new ShiftBlockReindentingVisitor(firstTokenInRegion, lastTokenInRegion);
+ }
+ },
+
+ /**
+ * Shifts each lines in the region left or right to match its new surroundings.
+ * <p>
+ * The algorithm is as follows:
+ * <ul>
+ * <li> Initially, the line is indented the same as the previous line.
+ * <li> If the preceding line starts with PROGRAM, IF, DO, etc., the line is
+ * indented an additional 4 spaces.
+ * <li> If the line starts with END, it is unindented 4 spaces.
+ * </ul>
+ */
+ REINDENT_EACH_LINE
+ {
+ @Override protected ReindentingVisitor createVisitor(Reindenter reindenter, Token firstTokenInRegion, Token lastTokenInRegion)
+ {
+ return reindenter.new ReindentEachLineReindentingVisitor(firstTokenInRegion, lastTokenInRegion);
+ }
+ };
+
+ protected abstract ReindentingVisitor createVisitor(Reindenter reindenter, Token firstTokenInRegion, Token lastTokenInRegion);
+ }
+
public static void reindent(IASTNode node, IFortranAST ast)
{
- new Reindenter(node, ast);
+ new Reindenter(node, ast, Strategy.SHIFT_ENTIRE_BLOCK);
}
public static void reindent(Token firstTokenInAffectedNode, Token lastTokenInAffectedNode, IFortranAST ast)
{
- new Reindenter(firstTokenInAffectedNode, lastTokenInAffectedNode, ast);
+ new Reindenter(firstTokenInAffectedNode, lastTokenInAffectedNode, ast, Strategy.SHIFT_ENTIRE_BLOCK);
}
public static void reindent(int fromLine, int thruLine, IFortranAST ast)
{
- new Reindenter(fromLine, thruLine, ast);
+ new Reindenter(fromLine, thruLine, ast, Strategy.SHIFT_ENTIRE_BLOCK);
+ }
+
+ public static void reindent(IASTNode node, IFortranAST ast, Strategy strategy)
+ {
+ new Reindenter(node, ast, strategy);
+ }
+
+ public static void reindent(Token firstTokenInAffectedNode, Token lastTokenInAffectedNode, IFortranAST ast, Strategy strategy)
+ {
+ new Reindenter(firstTokenInAffectedNode, lastTokenInAffectedNode, ast, strategy);
+ }
+
+ public static void reindent(int fromLine, int thruLine, IFortranAST ast, Strategy strategy)
+ {
+ new Reindenter(fromLine, thruLine, ast, strategy);
}
private final IFortranAST ast;
private final int lineNumOfLastTokenInAST;
- private Reindenter(IASTNode node, IFortranAST ast)
+ private Reindenter(IASTNode node, IFortranAST ast, Strategy strategy)
{
- this(node.findFirstToken(), node.findLastToken(), ast);
+ this(node.findFirstToken(), node.findLastToken(), ast, strategy);
}
- private Reindenter(int fromLine, int thruLine, IFortranAST ast)
+ private Reindenter(int fromLine, int thruLine, IFortranAST ast, Strategy strategy)
{
- this(ast.findFirstTokenOnLine(fromLine), ast.findLastTokenOnLine(thruLine), ast);
+ this(ast.findFirstTokenOnLine(fromLine), ast.findLastTokenOnLine(thruLine), ast, strategy);
}
- private Reindenter(Token firstTokenInRegion, Token lastTokenInRegion, IFortranAST ast)
+ private Reindenter(Token firstTokenInRegion, Token lastTokenInRegion, IFortranAST ast, Strategy strategy)
{
// Recompute TokenList so that line number-based searches will be correct
this.lineNumOfLastTokenInAST = recomputeLineColInfo(ast.getRoot());
this.ast = new FortranAST(ast.getFile(), ast.getRoot(), new TokenList(ast.getRoot()));
if (firstTokenInRegion != null && lastTokenInRegion != null)
- ast.accept(new ReindentingVisitor(firstTokenInRegion, lastTokenInRegion));
+ ast.accept(strategy.createVisitor(this, firstTokenInRegion, lastTokenInRegion));
}
private int recomputeLineColInfo(ASTExecutableProgramNode astRoot)
@@ -121,48 +173,25 @@
}
}
- private final class ReindentingVisitor extends ASTVisitorWithLoops
+ private abstract class ReindentingVisitor extends ASTVisitorWithLoops
{
- private final Token firstTokenInRegion;
- private final Token lastTokenInRegion;
- private String removeIndent, addIndent;
+ protected final Token firstTokenInRegion;
+ protected final Token lastTokenInRegion;
+ protected String removeIndent, addIndent;
- private boolean inFormatRegion = false;
- private Token previousToken = null;
+ protected boolean inFormatRegion = false;
+ protected Token previousToken = null;
/**
* @param firstTokenInRegion
* @param lastTokenInRegion
*/
- private ReindentingVisitor(Token firstTokenInRegion, Token lastTokenInRegion)
+ protected ReindentingVisitor(Token firstTokenInRegion, Token lastTokenInRegion)
{
this.lastTokenInRegion = lastTokenInRegion;
this.firstTokenInRegion = firstTokenInRegion;
//System.out.println("Indenting from " + firstTokenInRegion + " through " + lastTokenInRegion);
-
- int firstLineNumToReindent = firstTokenInRegion.getLine();
- Token firstTokenOnFirstLineToAdjust = ast.findFirstTokenOnLine(firstLineNumToReindent);
- if (firstTokenOnFirstLineToAdjust != firstTokenInRegion) firstLineNumToReindent++;
-
- Token firstTokenOnLineAfterReindentedRegion = findTokenStartingFirstNonemptyLineBelow(lastTokenInRegion.getLine());
- //if (firstTokenOnLineAfterReindentedRegion != null) System.out.println("Below: " + firstTokenOnLineAfterReindentedRegion.getTerminal() + " - " + firstTokenOnLineAfterReindentedRegion.getText() + " - " + firstTokenOnLineAfterReindentedRegion.getCol() + ";");
-
- this.removeIndent = getIndentation(firstTokenOnFirstLineToAdjust);
- this.addIndent = getIndentation(firstTokenOnLineAfterReindentedRegion);
-
- if (endsIndentedRegion(firstTokenOnLineAfterReindentedRegion))
- {
- Token firstTokenOnLineAboveReindentedRegion = findTokenStartingLastNonemptyLineAbove(firstTokenInRegion.getLine());
- //if (firstTokenOnLineAboveReindentedRegion != null) System.out.println("Above: " + firstTokenOnLineAboveReindentedRegion.getTerminal() + " - " + firstTokenOnLineAboveReindentedRegion.getText() + " - " + firstTokenOnLineAboveReindentedRegion.getCol() + ";");
-
- if (firstTokenOnLineAboveReindentedRegion != null && !startsIndentedRegion(firstTokenOnLineAboveReindentedRegion))
- this.addIndent = getIndentation(firstTokenOnLineAboveReindentedRegion);
- else
- this.addIndent = addIndent + " ";
- }
-
- //System.out.println("Removing [" + removeIndent + "] and adding [" + addIndent + "]");
}
@Override public void visitToken(Token token)
@@ -175,24 +204,14 @@
//System.out.println(token.getTerminal() + " - " + inFormatRegion + " - " + token.getLine());
if (inFormatRegion && (previousToken == null || token.getLine() > previousToken.getLine()))
- updateIndentation(token, previousToken);
+ updateIndentation(token);
previousToken = token;
}
- private void updateIndentation(Token firstTokenOnLine, Token previousToken)
- {
- String whiteBeforeFirstTok = firstTokenOnLine.getWhiteBefore();
- String currentIndentation = getIndentation(firstTokenOnLine);
- if (!whiteBeforeFirstTok.endsWith(currentIndentation)) return;
- String comments = whiteBeforeFirstTok.substring(0, whiteBeforeFirstTok.length()-currentIndentation.length());
- //System.out.println("Reindenting [" + firstTokenOnLine.getWhiteBefore() + firstTokenOnLine.getText() + "]");
-
- firstTokenOnLine.setWhiteBefore(reindentedComments(comments) + newIndentation(currentIndentation));
- //System.out.println(" to [" + firstTokenOnLine.getWhiteBefore() + firstTokenOnLine.getText() + "]");
- }
+ protected abstract void updateIndentation(Token firstTokenOnLine);
- private String reindentedComments(String comments)
+ protected String reindentedComments(String comments)
{
StringBuilder sb = new StringBuilder();
for (String line : splitLines(comments))
@@ -235,7 +254,7 @@
return newIndentation(indentation) + comment;
}
- private String newIndentation(String currentIndentation)
+ protected String newIndentation(String currentIndentation)
{
String newIndentation;
if (currentIndentation.startsWith(removeIndent))
@@ -248,6 +267,100 @@
}
}
+ private final class ShiftBlockReindentingVisitor extends ReindentingVisitor
+ {
+ /**
+ * @param firstTokenInRegion
+ * @param lastTokenInRegion
+ */
+ protected ShiftBlockReindentingVisitor(Token firstTokenInRegion, Token lastTokenInRegion)
+ {
+ super(firstTokenInRegion, lastTokenInRegion);
+
+ int firstLineNumToReindent = firstTokenInRegion.getLine();
+ Token firstTokenOnFirstLineToAdjust = ast.findFirstTokenOnLine(firstLineNumToReindent);
+ if (firstTokenOnFirstLineToAdjust != firstTokenInRegion) firstLineNumToReindent++;
+
+ Token firstTokenOnLineAfterReindentedRegion = findTokenStartingFirstNonemptyLineBelow(lastTokenInRegion.getLine());
+ //if (firstTokenOnLineAfterReindentedRegion != null) System.out.println("Below: " + firstTokenOnLineAfterReindentedRegion.getTerminal() + " - " + firstTokenOnLineAfterReindentedRegion.getText() + " - " + firstTokenOnLineAfterReindentedRegion.getCol() + ";");
+
+ this.removeIndent = getIndentation(firstTokenOnFirstLineToAdjust);
+ this.addIndent = getIndentation(firstTokenOnLineAfterReindentedRegion);
+
+ if (endsIndentedRegion(firstTokenOnLineAfterReindentedRegion))
+ {
+ Token firstTokenOnLineAboveReindentedRegion = findTokenStartingLastNonemptyLineAbove(firstTokenInRegion.getLine());
+ //if (firstTokenOnLineAboveReindentedRegion != null) System.out.println("Above: " + firstTokenOnLineAboveReindentedRegion.getTerminal() + " - " + firstTokenOnLineAboveReindentedRegion.getText() + " - " + firstTokenOnLineAboveReindentedRegion.getCol() + ";");
+
+ if (firstTokenOnLineAboveReindentedRegion != null && !startsIndentedRegion(firstTokenOnLineAboveReindentedRegion))
+ this.addIndent = getIndentation(firstTokenOnLineAboveReindentedRegion);
+ else
+ this.addIndent = addIndent + " ";
+ }
+
+ //System.out.println("Removing [" + removeIndent + "] and adding [" + addIndent + "]");
+ }
+
+ protected void updateIndentation(Token firstTokenOnLine)
+ {
+ String whiteBeforeFirstTok = firstTokenOnLine.getWhiteBefore();
+ String currentIndentation = getIndentation(firstTokenOnLine);
+ if (!whiteBeforeFirstTok.endsWith(currentIndentation)) return;
+ String comments = whiteBeforeFirstTok.substring(0, whiteBeforeFirstTok.length()-currentIndentation.length());
+ //System.out.println("Reindenting [" + firstTokenOnLine.getWhiteBefore() + firstTokenOnLine.getText() + "]");
+
+ firstTokenOnLine.setWhiteBefore(reindentedComments(comments) + newIndentation(currentIndentation));
+ //System.out.println(" to [" + firstTokenOnLine.getWhiteBefore() + firstTokenOnLine.getText() + "]");
+ }
+ }
+
+ private final class ReindentEachLineReindentingVisitor extends ReindentingVisitor
+ {
+ private Token firstTokenOnPreviousLine;
+ private String indentationOfPreviousLine;
+
+ /**
+ * @param firstTokenInRegion
+ * @param lastTokenInRegion
+ */
+ protected ReindentEachLineReindentingVisitor(Token firstTokenInRegion, Token lastTokenInRegion)
+ {
+ super(firstTokenInRegion, lastTokenInRegion);
+
+ int firstLineNumToReindent = firstTokenInRegion.getLine();
+ Token firstTokenOnFirstLineToAdjust = ast.findFirstTokenOnLine(firstLineNumToReindent);
+ if (firstTokenOnFirstLineToAdjust != firstTokenInRegion) firstLineNumToReindent++;
+
+ this.firstTokenOnPreviousLine = findTokenStartingLastNonemptyLineAbove(firstTokenInRegion.getLine());
+ this.indentationOfPreviousLine = getIndentation(firstTokenOnPreviousLine);
+
+ this.removeIndent = indentationOfPreviousLine;
+ this.addIndent = getIndentation(firstTokenOnFirstLineToAdjust);
+ }
+
+ protected void updateIndentation(Token firstTokenOnLine)
+ {
+ String whiteBeforeFirstTok = firstTokenOnLine.getWhiteBefore();
+ String currentIndentation = getIndentation(firstTokenOnLine);
+ if (!whiteBeforeFirstTok.endsWith(currentIndentation)) return;
+ String comments = whiteBeforeFirstTok.substring(0, whiteBeforeFirstTok.length()-currentIndentation.length());
+ //System.out.println("Reindenting [" + firstTokenOnLine.getWhiteBefore() + firstTokenOnLine.getText() + "]");
+
+ this.removeIndent = currentIndentation;
+ this.addIndent = indentationOfPreviousLine;
+ if (startsIndentedRegion(firstTokenOnPreviousLine))
+ this.addIndent += " ";
+ else if (endsIndentedRegion(firstTokenOnLine) && this.addIndent.endsWith(" "))
+ this.addIndent = this.addIndent.substring(0, this.addIndent.length()-4);
+
+ firstTokenOnLine.setWhiteBefore(reindentedComments(comments) + newIndentation(currentIndentation));
+ //System.out.println(" to [" + firstTokenOnLine.getWhiteBefore() + firstTokenOnLine.getText() + "]");
+
+ this.firstTokenOnPreviousLine = firstTokenOnLine;
+ this.indentationOfPreviousLine = this.addIndent;
+ }
+ }
+
private String getIndentation(Token token)
{
String indent = token == null ? "" : token.getWhiteBefore();