naotsugu / piecetable

Java implementation of PieceTable data structure.
Apache License 2.0
11 stars 1 forks source link

APIs to get global offset for (line, char) #2

Open VladimirMakaev opened 1 month ago

VladimirMakaev commented 1 month ago

HI, I've trying to use your library in a project related to LSP and one issue I came across is calculating the length of a string between 2 positions. I'm doing something like this

fun rangeToLength(range: Range): Int {
    if (range.start.line == range.end.line) {
      return range.end.character - range.start.character
    } else {
      var length =
          inner.getText(range.start.line).length - range.start.character + range.end.character
      for (i in range.start.line + 1..range.end.line - 1) {
        length += inner.getText(i).length
      }
      return length
    }
  }

Would it be possible to add an API under TextEdit something like:

// Gets a global offset from the beginning of the document to a given (line, col)
public int offset(line, col) {
//
}
naotsugu commented 1 month ago

Letter lengths have many meanings and are often confusing.

  1. Raw byte length of the file(In UTF-8, 1 to 4 bytes / one character)
  2. String length on java memory(UTF-16, 1 to 2 bytes / one character)
  3. Unicode code point length
  4. Apparent text on the screen(Ligature, Combining character)

In the case of (1), it is easy to provide, because it is managed by RowIndex. However, this is probably not what is required.

So we added a method to get the range string, not the length(v0.5.3).

    /**
     * Gets the text at the specified range.
     * @param startRow the start row
     * @param startCol the start col
     * @param endRow the end row
     * @param endCol the end col
     * @return the text of the specified range
     */
    default String getText(int startRow, int startCol, int endRow, int endCol) {
        return getText(new Pos(startRow, startCol), new Pos(endRow, endCol));
    }

    /**
     * Gets the text list at the specified range.
     * @param start the start pos
     * @param end the end pos
     * @return the text of the specified range
     */
    String getText(Pos start, Pos end);

Perhaps that is in line with your original intent!

getText(1, 0, 2, 5).length();
VladimirMakaev commented 1 month ago

@naotsugu Thanks for that, but what I really need is ability to apply TextEdit to a given document. So essentially:

replace(Pos start, Pos end, String newText)

or alternatively

delete(Pos start, Pos end)
insert(Pos start, String newText) // this already exists

Also having getRowLength(int row) would help in other cases

naotsugu commented 1 month ago

The following methods have been added for editing selections.

    default Pos replace(int startRow, int startCol, int endRow, int endCol, String text) {
        return replace(new Pos(startRow, startCol), new Pos(endRow, endCol), text);
    }

    default Pos replace(Pos start, Pos end, String text) {
        return replace(
            start.row(), start.col(),
            getText(start, end).length() * (int) Math.signum(end.compareTo(start)),
            text);
    }
VladimirMakaev commented 1 month ago

@naotsugu I did a few tests and I think there is still a bug in how rows() are calculate after "replace" is called. Please take a look at this commit

testReplace1 and testReplace2 are failing with incorrect value of rows() property after an edit is performed.

naotsugu commented 1 month ago

@VladimirMakaev Thanks for reporting the bug. Fixed in version 0.5.5.