This commit is contained in:
Clément Fournier
2019-08-29 21:39:47 +02:00
parent 1d7f964126
commit a733da4dcf
7 changed files with 59 additions and 60 deletions

View File

@ -57,6 +57,10 @@ public interface Document {
CharSequence subSequence(TextRegion region);
/** Returns a mutable document that uses the given replace handler. */
MutableDocument newMutableDoc(ReplaceHandler out);
static Document forFile(final Path file, final Charset charset) throws IOException {
byte[] bytes = Files.readAllBytes(requireNonNull(file));
String text = new String(bytes, requireNonNull(charset));
@ -65,7 +69,7 @@ public interface Document {
static Document forCode(final String source) {
return new DocumentImpl(source, ReplaceFunction.NOOP);
return new DocumentImpl(source, ReplaceHandler.NOOP);
}
}

View File

@ -17,17 +17,22 @@ import net.sourceforge.pmd.lang.ast.SourceCodePositioner;
class DocumentImpl implements MutableDocument {
private ReplaceFunction out;
private ReplaceHandler out;
/** The positioner has the original source file. */
private SourceCodePositioner positioner;
private SortedMap<Integer, Integer> accumulatedOffsets = new TreeMap<>();
public DocumentImpl(final String source, final ReplaceFunction writer) {
DocumentImpl(final String source, final ReplaceHandler writer) {
this.out = writer;
positioner = new SourceCodePositioner(source);
}
@Override
public MutableDocument newMutableDoc(ReplaceHandler out) {
return new DocumentImpl(getText().toString(), out);
}
@Override
public void insert(int beginLine, int beginColumn, final String textToInsert) {
insert(positioner.offsetFromLineColumn(beginLine, beginColumn), textToInsert);
@ -55,14 +60,20 @@ class DocumentImpl implements MutableDocument {
private RegionByOffset shiftOffset(RegionByOffset origCoords, int lenDiff) {
ArrayList<Integer> keys = new ArrayList<>(accumulatedOffsets.keySet());
int idx = Collections.binarySearch(keys, origCoords.getOffset());
int idx = Collections.binarySearch(keys, origCoords.getStartOffset());
if (idx < 0) {
// there is no entry exactly for this offset, so that binarySearch
// returns the correct insertion index (but inverted)
idx = -(idx + 1);
} else {
// there is an exact entry
// since the loop below stops at idx, increment it to take that last entry into account
idx++;
}
// compute the shift accumulated by the mutations that have occurred
// left of the start index
int shift = 0;
for (int i = 0; i < idx; i++) {
shift += accumulatedOffsets.get(keys.get(i));
@ -70,10 +81,10 @@ class DocumentImpl implements MutableDocument {
RegionByOffset realPos = shift == 0
? origCoords
// don't check it
: new RegionByOffsetImp(origCoords.getOffset() + shift, origCoords.getLength());
// don't check the bounds
: new RegionByOffsetImpl(origCoords.getStartOffset() + shift, origCoords.getLength());
accumulatedOffsets.compute(origCoords.getOffset(), (k, v) -> {
accumulatedOffsets.compute(origCoords.getStartOffset(), (k, v) -> {
int s = v == null ? lenDiff : v + lenDiff;
return s == 0 ? null : s; // delete mapping if shift is 0
});
@ -83,8 +94,8 @@ class DocumentImpl implements MutableDocument {
@Override
public RegionByLine mapToLine(RegionByOffset region) {
int bline = positioner.lineNumberFromOffset(region.getOffset());
int bcol = positioner.columnFromOffset(bline, region.getOffset());
int bline = positioner.lineNumberFromOffset(region.getStartOffset());
int bcol = positioner.columnFromOffset(bline, region.getStartOffset());
int eline = positioner.lineNumberFromOffset(region.getOffsetAfterEnding());
int ecol = positioner.columnFromOffset(eline, region.getOffsetAfterEnding());
@ -103,7 +114,7 @@ class DocumentImpl implements MutableDocument {
@Override
public RegionByLine createRegion(int beginLine, int beginColumn, int endLine, int endColumn) {
// TODO checks, positioner should return -1
return TextRegion.newRegionByLine(beginLine, beginColumn, endLine, endColumn);
return new RegionByLineImpl(beginLine, beginColumn, endLine, endColumn);
}
@Override
@ -114,7 +125,7 @@ class DocumentImpl implements MutableDocument {
}
return new RegionByOffsetImp(offset, length);
return new RegionByOffsetImpl(offset, length);
}
@Override
@ -125,9 +136,10 @@ class DocumentImpl implements MutableDocument {
@Override
public CharSequence subSequence(TextRegion region) {
RegionByOffset byOffset = region.toOffset(this);
return getText().subSequence(byOffset.getOffset(), byOffset.getOffsetAfterEnding());
return getText().subSequence(byOffset.getStartOffset(), byOffset.getOffsetAfterEnding());
}
@Override
public CharSequence getUncommittedText() {
return out.getCurrentText(this);

View File

@ -5,12 +5,9 @@
package net.sourceforge.pmd.document;
import static java.util.Objects.requireNonNull;
import java.io.Closeable;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
/**
@ -62,24 +59,13 @@ public interface MutableDocument extends Document, Closeable {
CharSequence getText();
/** Returns the uncommitted text. */
/** Returns the uncommitted text, that will be committed by {@link #close()}. */
CharSequence getUncommittedText();
static MutableDocument forFile(final Path file, final Charset charset) throws IOException {
byte[] bytes = Files.readAllBytes(requireNonNull(file));
String text = new String(bytes, requireNonNull(charset));
return forCode(text, ReplaceFunction.bufferedFile(text, file, charset));
}
static MutableDocument forFile(String code, final Path file, final Charset charset) {
return forCode(code, ReplaceFunction.bufferedFile(code, file, charset));
}
static MutableDocument forCode(final String source, final ReplaceFunction writer) {
return new DocumentImpl(source, writer);
Document doc = Document.forFile(file, charset);
return doc.newMutableDoc(ReplaceHandler.bufferedFile(doc.getText(), file, charset));
}
}

View File

@ -7,14 +7,14 @@ package net.sourceforge.pmd.document;
/**
* Immutable implementation of the {@link TextRegion.RegionByLine} interface.
*/
class RegionByLineImp implements TextRegion.RegionByLine {
class RegionByLineImpl implements TextRegion.RegionByLine {
private final int beginLine;
private final int endLine;
private final int beginColumn;
private final int endColumn;
RegionByLineImp(final int beginLine, final int beginColumn, final int endLine, final int endColumn) {
RegionByLineImpl(final int beginLine, final int beginColumn, final int endLine, final int endColumn) {
this.beginLine = requireOver1(beginLine);
this.endLine = requireOver1(endLine);
this.beginColumn = requireOver1(beginColumn);
@ -58,11 +58,6 @@ class RegionByLineImp implements TextRegion.RegionByLine {
@Override
public String toString() {
return "RegionByLineImp{"
+ "beginLine=" + beginLine
+ ", endLine=" + endLine
+ ", beginColumn=" + beginColumn
+ ", endColumn=" + endColumn
+ '}';
return "Region(bl=" + beginLine + ", el=" + endLine + ", bc=" + beginColumn + ", ec=" + endColumn + ')';
}
}

View File

@ -7,11 +7,11 @@ package net.sourceforge.pmd.document;
/**
* Immutable implementation of the {@link TextRegion.RegionByOffset} interface.
*/
class RegionByOffsetImp implements TextRegion.RegionByOffset {
class RegionByOffsetImpl implements TextRegion.RegionByOffset {
private final int offset;
private final int length;
RegionByOffsetImp(final int offset, final int length) {
RegionByOffsetImpl(final int offset, final int length) {
this.offset = requireNonNegative(offset);
this.length = requireNonNegative(length);
}
@ -25,7 +25,7 @@ class RegionByOffsetImp implements TextRegion.RegionByOffset {
}
@Override
public int getOffset() {
public int getStartOffset() {
return offset;
}
@ -34,4 +34,8 @@ class RegionByOffsetImp implements TextRegion.RegionByOffset {
return length;
}
@Override
public String toString() {
return "Region(start=" + offset + ", len=" + length + ")";
}
}

View File

@ -12,10 +12,11 @@ import java.nio.file.Path;
import net.sourceforge.pmd.document.TextRegion.RegionByOffset;
public interface ReplaceFunction {
/** Handles text updates for a {@link MutableDocument}. */
public interface ReplaceHandler {
ReplaceFunction NOOP = new ReplaceFunction() {
/** Does nothing. */
ReplaceHandler NOOP = new ReplaceHandler() {
@Override
public CharSequence getCurrentText(MutableDocument doc) {
return doc.getText();
@ -27,7 +28,7 @@ public interface ReplaceFunction {
}
@Override
public ReplaceFunction commit() {
public ReplaceHandler commit() {
return NOOP;
}
};
@ -39,24 +40,26 @@ public interface ReplaceFunction {
void replace(RegionByOffset region, String text);
/** Gets the latest text. */
CharSequence getCurrentText(MutableDocument doc);
/**
* Commit the document (eg writing it to disk), and returns a new
* document corresponding to the new document.
*
* @return An updated replace function
*/
ReplaceFunction commit() throws IOException;
ReplaceHandler commit() throws IOException;
/**
* Write updates into an in-memory buffer, commit writes to disk.
* This doesn't use any IO resources outside of the commit method.
*/
static ReplaceFunction bufferedFile(String originalBuffer, Path path, Charset charSet) {
static ReplaceHandler bufferedFile(CharSequence originalBuffer, Path path, Charset charSet) {
return new ReplaceFunction() {
return new ReplaceHandler() {
private StringBuilder builder = new StringBuilder(originalBuffer);
@ -67,11 +70,11 @@ public interface ReplaceFunction {
@Override
public void replace(RegionByOffset region, String text) {
builder.replace(region.getOffset(), region.getOffsetAfterEnding(), text);
builder.replace(region.getStartOffset(), region.getOffsetAfterEnding(), text);
}
@Override
public ReplaceFunction commit() throws IOException {
public ReplaceHandler commit() throws IOException {
String done = builder.toString();
byte[] bytes = done.getBytes(charSet);
Files.write(path, bytes);

View File

@ -22,12 +22,7 @@ public interface TextRegion {
*
* @throws IndexOutOfBoundsException If the argument does not identify a valid region in the document
*/
RegionByLine toLine(Document document);
static RegionByLine newRegionByLine(final int beginLine, final int beginColumn, final int endLine, final int endColumn) {
return new RegionByLineImp(beginLine, beginColumn, endLine, endColumn);
}
RegionByLine toLineColumn(Document document);
/**
@ -66,7 +61,7 @@ public interface TextRegion {
@Override
default RegionByLine toLine(Document document) {
default RegionByLine toLineColumn(Document document) {
return this;
}
@ -82,12 +77,12 @@ public interface TextRegion {
*/
interface RegionByOffset extends TextRegion, Comparable<RegionByOffset> {
Comparator<RegionByOffset> COMPARATOR = Comparator.comparingInt(RegionByOffset::getOffset)
Comparator<RegionByOffset> COMPARATOR = Comparator.comparingInt(RegionByOffset::getStartOffset)
.thenComparingInt(RegionByOffset::getLength);
/** 0-based, inclusive index. */
int getOffset();
int getStartOffset();
/** Length of the region. */
@ -96,12 +91,12 @@ public interface TextRegion {
/** 0-based, exclusive index. */
default int getOffsetAfterEnding() {
return getOffset() + getLength();
return getStartOffset() + getLength();
}
@Override
default RegionByLine toLine(Document document) {
default RegionByLine toLineColumn(Document document) {
return document.mapToLine(this);
}