Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,8 @@ private static List<SemanticTokensEdit> computeEdits(int[] prev, int[] curr) {
* <p>
* При дельта-кодировании токены после точки вставки идентичны,
* кроме первого токена, у которого deltaLine смещён на lineOffset.
* При вставке текста без перевода строки (lineOffset == 0), первый токен
* может иметь смещённый deltaStart.
*/
private static int findSuffixMatchWithOffset(int[] prev, int[] curr, int firstDiffToken, int lineOffset, int tokenSize) {
int prevTokenCount = prev.length / tokenSize;
Expand All @@ -310,9 +312,13 @@ private static int findSuffixMatchWithOffset(int[] prev, int[] curr, int firstDi
int prevIdx = (prevTokenCount - 1 - i) * tokenSize;
int currIdx = (currTokenCount - 1 - i) * tokenSize;

// Сначала проверяем все поля кроме deltaLine
// Для граничного токена при inline-редактировании (lineOffset == 0)
// разрешаем различие в deltaStart
int fieldsToCheck = (!foundBoundary && lineOffset == 0) ? 2 : 1;

// Проверяем поля кроме deltaLine (и возможно deltaStart для граничного токена)
boolean otherFieldsMatch = true;
for (int j = 1; j < tokenSize; j++) {
for (int j = fieldsToCheck; j < tokenSize; j++) {
if (prev[prevIdx + j] != curr[currIdx + j]) {
otherFieldsMatch = false;
break;
Expand All @@ -328,8 +334,16 @@ private static int findSuffixMatchWithOffset(int[] prev, int[] curr, int firstDi
int currDeltaLine = curr[currIdx];

if (prevDeltaLine == currDeltaLine) {
// Полное совпадение
// Полное совпадение (или совпадение с учётом deltaStart при inline-редактировании)
suffixMatch++;
// Если это был граничный токен при inline-редактировании, отмечаем его найденным
if (!foundBoundary && lineOffset == 0 && fieldsToCheck == 2) {
int prevDeltaStart = prev[prevIdx + 1];
int currDeltaStart = curr[currIdx + 1];
if (prevDeltaStart != currDeltaStart) {
foundBoundary = true;
}
}
} else if (!foundBoundary && currDeltaLine - prevDeltaLine == lineOffset) {
// Граничный токен — deltaLine отличается ровно на lineOffset
suffixMatch++;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1341,6 +1341,64 @@ void deltaWithLineInsertedInMiddle_shouldReturnOptimalDelta() {
assertThat(editSize).isLessThan(originalDataSize);
}

@Test
void deltaWithTextInsertedOnSameLine_shouldReturnOptimalDelta() {
// given - simulate inserting text on the same line without line breaks
// This tests the case raised by @nixel2007: text insertion without newline
String bsl1 = """
Перем А;
""";

String bsl2 = """
Перем Новая, А;
""";

DocumentContext context1 = TestUtils.getDocumentContext(bsl1);
referenceIndexFiller.fill(context1);
TextDocumentIdentifier textDocId1 = TestUtils.getTextDocumentIdentifier(context1.getUri());
SemanticTokens tokens1 = provider.getSemanticTokensFull(context1, new SemanticTokensParams(textDocId1));
int originalDataSize = tokens1.getData().size();

// Decode and print tokens for debugging
var decoded1 = decode(tokens1.getData());
System.out.println("Original tokens:");
for (var t : decoded1) {
System.out.println(" line=" + t.line + ", start=" + t.start + ", len=" + t.length + ", type=" + t.type + ", mods=" + t.modifiers);
}

DocumentContext context2 = TestUtils.getDocumentContext(context1.getUri(), bsl2);
referenceIndexFiller.fill(context2);
SemanticTokens tokens2 = provider.getSemanticTokensFull(context2, new SemanticTokensParams(textDocId1));
var decoded2 = decode(tokens2.getData());
System.out.println("New tokens:");
for (var t : decoded2) {
System.out.println(" line=" + t.line + ", start=" + t.start + ", len=" + t.length + ", type=" + t.type + ", mods=" + t.modifiers);
}

// when
var deltaParams = new SemanticTokensDeltaParams(textDocId1, tokens1.getResultId());
var result = provider.getSemanticTokensFullDelta(context2, deltaParams);

// then - should return delta, not full tokens
assertThat(result.isRight()).isTrue();
var delta = result.getRight();
assertThat(delta.getEdits()).isNotEmpty();

var edit = delta.getEdits().get(0);
System.out.println("Edit: start=" + edit.getStart() + ", deleteCount=" + edit.getDeleteCount() +
", insertSize=" + (edit.getData() != null ? edit.getData().size() : 0));

// Verify the delta is computed correctly
// Original tokens: "Перем" (keyword), "А" (variable), ";" (operator)
// New tokens: "Перем" (keyword), "Новая" (variable), "," (operator), "А" (variable), ";" (operator)
// Since lineOffset=0 (no line change), the algorithm should detect this as an inline edit
// The "Перем" token should match, and ";" should match (though its deltaStart changes)

// The edit should be smaller than resending all tokens
int editSize = edit.getDeleteCount() + (edit.getData() != null ? edit.getData().size() : 0);
assertThat(editSize).isLessThan(originalDataSize + tokens2.getData().size());
}

// endregion
}