-
-
Notifications
You must be signed in to change notification settings - Fork 354
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
review: feat: Make sniper printer infer indentation style of compilation unit #3717
Merged
nharrand
merged 9 commits into
INRIA:master
from
slarse:issue/3715-sniper-printer-infer-indentation
Dec 4, 2020
Merged
Changes from 8 commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
90a6836
Add indentation test case
slarse d440fca
Implement indentation detection
slarse e69e861
Refactor indentation detection
slarse 62d8e11
Move indentation detection to a separate class
slarse 9495041
Define default to be 1 tabs
slarse 34d3ca3
Fix checkstyle errors
slarse 1a7ab79
Document setters
slarse 5fb7531
Add a couple of blank lines for readability
slarse 1d3d592
Clarify indentation detection strategy
slarse File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
94 changes: 94 additions & 0 deletions
94
src/main/java/spoon/support/sniper/internal/IndentationDetector.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
/** | ||
* SPDX-License-Identifier: (MIT OR CECILL-C) | ||
* | ||
* Copyright (C) 2006-2019 INRIA and contributors | ||
* | ||
* Spoon is available either under the terms of the MIT License (see LICENSE-MIT.txt) of the Cecill-C License (see LICENSE-CECILL-C.txt). You as the user are entitled to choose the terms under which to adopt Spoon. | ||
*/ | ||
package spoon.support.sniper.internal; | ||
|
||
import org.apache.commons.lang3.tuple.Pair; | ||
import spoon.reflect.declaration.CtCompilationUnit; | ||
import spoon.reflect.path.CtRole; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
|
||
/** | ||
* Utility class for detecting the indentation style used in a compilation unit. | ||
*/ | ||
public class IndentationDetector { | ||
|
||
private IndentationDetector() { | ||
} | ||
|
||
/** | ||
* Detect the indentation style of the given compilation unit as 1, 2 or 4 spaces or tabs. | ||
* | ||
nharrand marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* @param cu A compilation unit. | ||
* @return A pair on the form (indentationSize, isTabs) | ||
*/ | ||
public static Pair<Integer, Boolean> detectIndentation(CtCompilationUnit cu) { | ||
List<ElementSourceFragment> typeFragments = cu.getOriginalSourceFragment() | ||
.getGroupedChildrenFragments().stream() | ||
.filter(fragment -> fragment instanceof CollectionSourceFragment) | ||
.flatMap(fragment -> extractTypeFragments((CollectionSourceFragment) fragment).stream()) | ||
.collect(Collectors.toList()); | ||
return detectIndentation(typeFragments); | ||
} | ||
|
||
private static Pair<Integer, Boolean> detectIndentation(List<ElementSourceFragment> topLevelTypeFragments) { | ||
List<String> wsPrecedingTypeMembers = new ArrayList<>(); | ||
|
||
for (ElementSourceFragment typeSource : topLevelTypeFragments) { | ||
assert typeSource.getRoleInParent() == CtRole.DECLARED_TYPE; | ||
|
||
List<SourceFragment> children = typeSource.getChildrenFragments(); | ||
for (int i = 0; i < children.size() - 1; i++) { | ||
if (children.get(i) instanceof TokenSourceFragment | ||
&& children.get(i + 1) instanceof ElementSourceFragment) { | ||
|
||
TokenSourceFragment cur = (TokenSourceFragment) children.get(i); | ||
ElementSourceFragment next = (ElementSourceFragment) children.get(i + 1); | ||
if (cur.getType() == TokenType.SPACE && next.getRoleInParent() == CtRole.TYPE_MEMBER) { | ||
wsPrecedingTypeMembers.add(cur.getSourceCode().replace("\n", "")); | ||
} | ||
} | ||
} | ||
} | ||
|
||
return guessIndentationStyle(wsPrecedingTypeMembers); | ||
} | ||
|
||
private static Pair<Integer, Boolean> guessIndentationStyle(List<String> wsPrecedingTypeMembers) { | ||
double avgIndent = wsPrecedingTypeMembers.stream() | ||
.map(String::length) | ||
.map(Double::valueOf) | ||
.reduce((acc, next) -> (acc + next) / 2).orElse(1d); | ||
|
||
double diff1 = Math.abs(1d - avgIndent); | ||
double diff2 = Math.abs(2d - avgIndent); | ||
double diff4 = Math.abs(4d - avgIndent); | ||
|
||
int indentationSize; | ||
if (diff1 > diff2) { | ||
indentationSize = diff2 > diff4 ? 4 : 2; | ||
} else { | ||
indentationSize = 1; | ||
} | ||
|
||
boolean usesTabs = wsPrecedingTypeMembers.stream() | ||
.filter(s -> s.contains("\t")) | ||
.count() >= wsPrecedingTypeMembers.size() / 2; | ||
return Pair.of(indentationSize, usesTabs); | ||
} | ||
|
||
private static List<ElementSourceFragment> extractTypeFragments(CollectionSourceFragment collection) { | ||
return collection.getItems().stream() | ||
.filter(fragment -> fragment instanceof ElementSourceFragment) | ||
.map(fragment -> (ElementSourceFragment) fragment) | ||
.filter(fragment -> fragment.getRoleInParent() == CtRole.DECLARED_TYPE) | ||
.collect(Collectors.toList()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,8 +22,38 @@ public class MutableTokenWriter implements TokenWriter { | |
private final TokenWriter delegate; | ||
private boolean muted = false; | ||
|
||
// indentation style to use for new elements | ||
private boolean originSourceUsesTabulations; | ||
private int originSourceTabulationSize; | ||
|
||
public MutableTokenWriter(Environment env) { | ||
this.delegate = new DefaultTokenWriter(new PrinterHelper(env)); | ||
this.delegate = new DefaultTokenWriter(new SniperPrinterHelper(env)); | ||
originSourceUsesTabulations = true; | ||
originSourceTabulationSize = 1; | ||
} | ||
|
||
private class SniperPrinterHelper extends PrinterHelper { | ||
private final Environment env; | ||
|
||
SniperPrinterHelper(Environment env) { | ||
super(env); | ||
this.env = env; | ||
} | ||
|
||
/** | ||
* We override this method to use the correct style of indentation for new elements. | ||
*/ | ||
@Override | ||
protected void autoWriteTabs() { | ||
int setTabulationSize = env.getTabulationSize(); | ||
env.useTabulations(originSourceUsesTabulations); | ||
env.setTabulationSize(originSourceTabulationSize); | ||
|
||
super.autoWriteTabs(); | ||
|
||
env.setTabulationSize(setTabulationSize); | ||
env.useTabulations(true); | ||
} | ||
Comment on lines
+35
to
+56
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the actual thing that overrides the printing of tabs with whatever indentation style is present in the source file. I'm not completely satisfied with this solution as it seems a bit hacky, but I couldn't figure out a better way to do it. Suggestions are welcome. |
||
} | ||
|
||
/** | ||
|
@@ -40,6 +70,20 @@ public void setMuted(boolean muted) { | |
this.muted = muted; | ||
} | ||
|
||
/** | ||
* @param originSourceUsesTabulations whether or not the origin source uses tabs for indentation. | ||
*/ | ||
public void setOriginSourceUsesTabulations(boolean originSourceUsesTabulations) { | ||
this.originSourceUsesTabulations = originSourceUsesTabulations; | ||
} | ||
|
||
/** | ||
* @param originSourceTabulationSize the amount of indentation used in the origin source. | ||
*/ | ||
public void setOriginSourceTabulationSize(int originSourceTabulationSize) { | ||
this.originSourceTabulationSize = originSourceTabulationSize; | ||
} | ||
|
||
@Override | ||
public TokenWriter writeSeparator(String token) { | ||
if (isMuted()) { | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package indentation; | ||
|
||
public class FourSpaces { | ||
private int x = 1; | ||
private int y = 2; | ||
|
||
public int sum() { | ||
return x + y; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
package indentation; | ||
|
||
public class NoTypeMembers { | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package indentation; | ||
|
||
public class Tabs { | ||
private int x = 1; | ||
private int y = 2; | ||
|
||
public int sum() { | ||
return x + y; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package indentation; | ||
|
||
public class TwoSpaces { | ||
private int x = 1; | ||
private int y = 2; | ||
|
||
public int sum() { | ||
return x + y; | ||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The token writer is updated with indentation style for each CU to be printed.