Merge, wip
This commit is contained in:
11 files changed
+117
-44
No files matched your search
@@ -130,12 +130,8 @@ public interface Node extends Reportable {
|
||||
* to create a region.
|
||||
*/
|
||||
default TextRegion getTextRegion() {
|
||||
@SuppressWarnings("PMD.CloseResource")
|
||||
TextDocument document = getAstInfo().getTextDocument();
|
||||
FileLocation loc = getReportLocation();
|
||||
int startOffset = document.offsetAtLineColumn(loc.getStartPos());
|
||||
int endOffset = document.offsetAtLineColumn(loc.getStartPos());
|
||||
return TextRegion.fromBothOffsets(startOffset, endOffset);
|
||||
return getAstInfo().getTextDocument().toRegion(loc.toRange2d());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -59,6 +59,21 @@ abstract class BaseMappedDocument implements TextDocument {
|
||||
return base.createLineRange(startLineInclusive, endLineInclusive);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int offsetAtLineColumn(int line, int column) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextPos2d lineColumnAtOffset(int offset, boolean inclusive) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInRange(TextPos2d textPos2d) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int inputOffset(int outOffset, boolean inclusive) {
|
||||
if (outOffset < 0 || outOffset > getLength()) {
|
||||
@@ -67,8 +82,13 @@ abstract class BaseMappedDocument implements TextDocument {
|
||||
return base.inputOffset(localOffsetTransform(outOffset, inclusive), inclusive);
|
||||
}
|
||||
|
||||
/**
|
||||
* Output offset to input offset.
|
||||
*/
|
||||
protected abstract int localOffsetTransform(int outOffset, boolean inclusive);
|
||||
|
||||
protected abstract int inverseLocalOffsetTransform(int inOffset, boolean inclusive);
|
||||
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
|
||||
+41
-12
@@ -4,6 +4,8 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.document;
|
||||
|
||||
import java.util.function.ToIntFunction;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import net.sourceforge.pmd.lang.LanguageVersion;
|
||||
@@ -48,44 +50,71 @@ final class FragmentedTextDocument extends BaseMappedDocument implements TextDoc
|
||||
|
||||
@Override
|
||||
protected int localOffsetTransform(int outOffset, boolean inclusive) {
|
||||
return inputOffsetAt(outOffset, inclusive);
|
||||
return offsetTransform(outOffset, inclusive,
|
||||
Fragment::outToIn,
|
||||
Fragment::outEnd,
|
||||
Fragment::outStart
|
||||
);
|
||||
}
|
||||
|
||||
private int inputOffsetAt(int outputOffset, boolean inclusive) {
|
||||
@Override
|
||||
protected int inverseLocalOffsetTransform(int inOffset, boolean inclusive) {
|
||||
return offsetTransform(inOffset, inclusive,
|
||||
Fragment::inToOut,
|
||||
Fragment::inEnd,
|
||||
Fragment::inStart
|
||||
);
|
||||
}
|
||||
|
||||
interface OffsetMapper {
|
||||
|
||||
int mapOffset(Fragment fragment, int offset);
|
||||
}
|
||||
|
||||
private int offsetTransform(int offset,
|
||||
boolean inclusive,
|
||||
OffsetMapper mapOffsetWhenContains,
|
||||
ToIntFunction<Fragment> end,
|
||||
ToIntFunction<Fragment> start) {
|
||||
// caching the last accessed fragment instead of doing
|
||||
// a linear search is critical for performance.
|
||||
Fragment f = this.lastAccessedFragment;
|
||||
if (f == null) {
|
||||
return outputOffset;
|
||||
return offset;
|
||||
}
|
||||
|
||||
if (!f.contains(outputOffset)) {
|
||||
// Whether the fragment contains the offset we're looking for.
|
||||
// Will be true most of the time.
|
||||
boolean containsOffset =
|
||||
start.applyAsInt(f) >= offset && offset < end.applyAsInt(f);
|
||||
|
||||
if (!containsOffset) {
|
||||
// Slow path, we must search for the fragment
|
||||
// This optimisation is important, otherwise we have
|
||||
// to search for very long times in some files
|
||||
|
||||
if (f.outEnd() < outputOffset) { // search forward
|
||||
while (f.next != null && f.outEnd() < outputOffset) {
|
||||
if (end.applyAsInt(f) < offset) { // search forward
|
||||
while (f.next != null && end.applyAsInt(f) < offset) {
|
||||
f = f.next;
|
||||
}
|
||||
} else { // search backwards
|
||||
while (f.prev != null && outputOffset <= f.outStart()) {
|
||||
while (f.prev != null && offset <= start.applyAsInt(f)) {
|
||||
f = f.prev;
|
||||
}
|
||||
}
|
||||
lastAccessedFragment = f;
|
||||
}
|
||||
|
||||
if (!inclusive && f.outEnd() == outputOffset) {
|
||||
if (!inclusive && end.applyAsInt(f) == offset) {
|
||||
if (f.next != null) {
|
||||
f = f.next;
|
||||
lastAccessedFragment = f;
|
||||
// fallthrough
|
||||
} else {
|
||||
return f.outToIn(outputOffset) + 1;
|
||||
return mapOffsetWhenContains.mapOffset(f, offset) + 1;
|
||||
}
|
||||
}
|
||||
return f.outToIn(outputOffset);
|
||||
return mapOffsetWhenContains.mapOffset(f, offset);
|
||||
}
|
||||
|
||||
|
||||
@@ -150,8 +179,8 @@ final class FragmentedTextDocument extends BaseMappedDocument implements TextDoc
|
||||
return inStart() + outOffset - outStart();
|
||||
}
|
||||
|
||||
boolean contains(int outOffset) {
|
||||
return outStart() <= outOffset && outEnd() > outOffset;
|
||||
int inToOut(int inOffset) {
|
||||
return inOffset - inStart() + outStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -148,7 +148,7 @@ public interface TextDocument extends Closeable {
|
||||
int inputOffset(int outOffset, boolean inclusive);
|
||||
|
||||
/**
|
||||
* Translate a region given in the the coordinate system of this
|
||||
* Translate a region given in the coordinate system of this
|
||||
* document, to the coordinate system of the original document.
|
||||
* This works as if creating a new region with both start and end
|
||||
* offsets translated through {@link #inputOffset(int, boolean)}. The
|
||||
@@ -160,6 +160,21 @@ public interface TextDocument extends Closeable {
|
||||
*/
|
||||
TextRegion inputRegion(TextRegion outputRegion);
|
||||
|
||||
/**
|
||||
* Translate a 2D range given in the coordinate system of this
|
||||
* document, to the coordinate system of the original document.
|
||||
* This works as if creating a new region with both start and end
|
||||
* offsets translated through {@link #inputOffset(int, boolean)}. The
|
||||
* returned region may have a different length.
|
||||
*
|
||||
* @param outputRange Output region
|
||||
*
|
||||
* @return Input region
|
||||
*/
|
||||
default TextRange2d inputRange(TextRange2d outputRange) {
|
||||
return toRange2d(inputRegion(toRegion(outputRange)));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a reader over the text of this document.
|
||||
@@ -176,16 +191,20 @@ public interface TextDocument extends Closeable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a text region that corresponds to the entire document.
|
||||
* Returns a text region that corresponds to the entire document,
|
||||
* in the coordinate system of this document.
|
||||
*/
|
||||
default TextRegion getEntireRegion() {
|
||||
return TextRegion.fromOffsetLength(0, getLength());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a 2D text range that corresponds to the entire document.
|
||||
* Returns a 2D text range that corresponds to the entire document,
|
||||
* in the coordinate system of this document.
|
||||
*/
|
||||
TextRange2d getEntireRegion2d();
|
||||
default TextRange2d getEntireRegion2d() {
|
||||
return toRange2d(getEntireRegion());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a region that spans the text of all the given lines.
|
||||
@@ -210,7 +229,9 @@ public interface TextDocument extends Closeable {
|
||||
* the line/column information for both start and end offset of
|
||||
* the region.
|
||||
*
|
||||
* @return A new file position
|
||||
* @param region A region, in the coordinate system of this document
|
||||
*
|
||||
* @return A new file position, with absolute coordinates
|
||||
*
|
||||
* @throws IndexOutOfBoundsException If the argument is not a valid region in this document
|
||||
*/
|
||||
@@ -231,7 +252,7 @@ public interface TextDocument extends Closeable {
|
||||
}
|
||||
TextRegion region = TextRegion.caretAt(startOffset);
|
||||
checkInRange(region, this.getLength());
|
||||
return FileLocation.location(getDisplayName(), range);
|
||||
return FileLocation.range(getDisplayName(), range);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -289,6 +310,8 @@ public interface TextDocument extends Closeable {
|
||||
|
||||
/**
|
||||
* Returns the line and column at the given offset (inclusive).
|
||||
* Both the input offset and the output range are in the coordinates
|
||||
* of this document.
|
||||
*
|
||||
* @param offset A source offset (0-based), can range in {@code [0, length]}.
|
||||
* @param inclusive If the offset falls right after a line terminator,
|
||||
@@ -296,6 +319,8 @@ public interface TextDocument extends Closeable {
|
||||
* choose the position at the start of the next line.
|
||||
* Otherwise choose the offset at the end of the line.
|
||||
*
|
||||
* @return A position, in the coordinate system of this document
|
||||
*
|
||||
* @throws IndexOutOfBoundsException if the offset is out of bounds
|
||||
*/
|
||||
TextPos2d lineColumnAtOffset(int offset, boolean inclusive);
|
||||
@@ -315,6 +340,17 @@ public interface TextDocument extends Closeable {
|
||||
@Override
|
||||
void close() throws IOException;
|
||||
|
||||
/**
|
||||
* Create a new text document for the given text file. The document's
|
||||
* coordinate system is the same as the original text file.
|
||||
*
|
||||
* @param textFile A text file
|
||||
*
|
||||
* @return A new text document
|
||||
*
|
||||
* @throws IOException If the file cannot be read ({@link TextFile#readContents()})
|
||||
* @throws NullPointerException If the parameter is null
|
||||
*/
|
||||
static TextDocument create(TextFile textFile) throws IOException {
|
||||
return new RootTextDocument(textFile);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,11 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
*/
|
||||
public final class TextPos2d implements Comparable<TextPos2d> {
|
||||
|
||||
/**
|
||||
* The position at the start of the document (line 1, column 1).
|
||||
*/
|
||||
public static TextPos2d DOCUMENT_START = new TextPos2d(1, 1);
|
||||
|
||||
private final int line;
|
||||
private final int column;
|
||||
|
||||
|
||||
@@ -122,8 +122,8 @@ public final class TextRegion implements Comparable<TextRegion> {
|
||||
* @throws AssertionError If the parameter cannot produce a valid region
|
||||
*/
|
||||
public TextRegion growLeft(int delta) {
|
||||
assert (delta + length) >= 0 : "Left delta " + delta + " would produce a negative length region" + parThis();
|
||||
assert (startOffset - delta) >= 0 : "Left delta " + delta + " would produce a region that starts before zero" + parThis();
|
||||
assert delta + length >= 0 : "Left delta " + delta + " would produce a negative length region" + parThis();
|
||||
assert startOffset - delta >= 0 : "Left delta " + delta + " would produce a region that starts before zero" + parThis();
|
||||
return new TextRegion(startOffset - delta, delta + length);
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ public final class TextRegion implements Comparable<TextRegion> {
|
||||
* @throws AssertionError If the delta is negative and less than the length of this region
|
||||
*/
|
||||
public TextRegion growRight(int delta) {
|
||||
assert (delta + length) >= 0 : "Right delta " + delta + " would produce a negative length region" + parThis();
|
||||
assert delta + length >= 0 : "Right delta " + delta + " would produce a negative length region" + parThis();
|
||||
return new TextRegion(startOffset, delta + length);
|
||||
}
|
||||
|
||||
@@ -181,15 +181,6 @@ public final class TextRegion implements Comparable<TextRegion> {
|
||||
return new TextRegion(startOffset, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a new region from offset and length.
|
||||
*
|
||||
* @throws AssertionError If either parameter is negative
|
||||
*/
|
||||
public static TextRegion caretAt(int offset) {
|
||||
return fromOffsetLength(offset, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a new region from start and end offset.
|
||||
*
|
||||
|
||||
@@ -75,10 +75,7 @@ public class ReportTest {
|
||||
}
|
||||
|
||||
private static FileLocation getNode(int line, int column, String filename) {
|
||||
return FileLocation.location(
|
||||
filename,
|
||||
TextRange2d.range2d(line, column, line, column)
|
||||
);
|
||||
return FileLocation.range(filename, TextRange2d.range2d(line, column, line, column));
|
||||
}
|
||||
|
||||
public static String render(Renderer renderer, Consumer<? super FileAnalysisListener> listenerEffects) throws IOException {
|
||||
|
||||
@@ -70,7 +70,7 @@ public class RuleViolationComparatorTest {
|
||||
|
||||
private RuleViolation createJavaRuleViolation(Rule rule, String fileName, int beginLine, String description,
|
||||
int beginColumn, int endLine, int endColumn) {
|
||||
FileLocation loc = FileLocation.location(fileName, TextRange2d.range2d(beginLine, beginColumn, endLine, endColumn));
|
||||
FileLocation loc = FileLocation.range(fileName, TextRange2d.range2d(beginLine, beginColumn, endLine, endColumn));
|
||||
return new ParametricRuleViolation(rule, loc, description);
|
||||
}
|
||||
}
|
||||
@@ -92,7 +92,7 @@ public abstract class AbstractRendererTest {
|
||||
|
||||
protected FileLocation createLocation(int beginLine, int beginColumn, int endLine, int endColumn) {
|
||||
TextRange2d range2d = TextRange2d.range2d(beginLine, beginColumn, endLine, endColumn);
|
||||
return FileLocation.location(getSourceCodeFilename(), range2d);
|
||||
return FileLocation.range(getSourceCodeFilename(), range2d);
|
||||
}
|
||||
|
||||
protected RuleViolation newRuleViolation(int beginLine, int beginColumn, int endLine, int endColumn, Rule rule) {
|
||||
|
||||
@@ -91,8 +91,7 @@ public class XMLRendererTest extends AbstractRendererTest {
|
||||
}
|
||||
|
||||
private RuleViolation createRuleViolation(String description) {
|
||||
FileLocation loc = FileLocation.location(getSourceCodeFilename(),
|
||||
TextRange2d.range2d(1, 1, 1, 1));
|
||||
FileLocation loc = FileLocation.range(getSourceCodeFilename(), TextRange2d.range2d(1, 1, 1, 1));
|
||||
return new ParametricRuleViolation(new FooRule(), loc, description);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ public class XSLTRendererTest {
|
||||
@Test
|
||||
public void testDefaultStylesheet() throws Exception {
|
||||
XSLTRenderer renderer = new XSLTRenderer();
|
||||
FileLocation loc = FileLocation.location("file", TextRange2d.range2d(1, 1, 1, 2));
|
||||
FileLocation loc = FileLocation.range("file", TextRange2d.range2d(1, 1, 1, 2));
|
||||
RuleViolation rv = new ParametricRuleViolation(new FooRule(), loc, "violation message");
|
||||
String result = ReportTest.render(renderer, it -> it.onRuleViolation(rv));
|
||||
Assert.assertTrue(result.contains("violation message"));
|
||||
|
||||
Reference in new issue
Block a user