Skip to content

Commit b6fb110

Browse files
authored
Merge pull request #3788 from 1c-syntax/develop
0.28.1
2 parents 277b242 + 71b5494 commit b6fb110

File tree

4 files changed

+89
-3
lines changed

4 files changed

+89
-3
lines changed

src/main/java/com/github/_1c_syntax/bsl/languageserver/BSLWorkspaceService.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,12 @@ public CompletableFuture<Either<List<? extends SymbolInformation>,List<? extends
8080

8181
@Override
8282
public void didChangeConfiguration(DidChangeConfigurationParams params) {
83+
var settings = params.getSettings();
84+
if (settings == null) {
85+
return;
86+
}
8387
try {
84-
PropertyUtils.copyProperties(configuration, params.getSettings());
88+
PropertyUtils.copyProperties(configuration, settings);
8589
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
8690
throw new RuntimeException(e);
8791
}

src/main/java/com/github/_1c_syntax/bsl/languageserver/providers/SemanticTokensProvider.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,10 @@ private static List<SemanticTokensEdit> computeEdits(int[] prev, int[] curr) {
391391
* кроме первого токена, у которого deltaLine смещён на lineOffset.
392392
* При вставке текста без перевода строки (lineOffset == 0), первый токен
393393
* может иметь смещённый deltaStart.
394+
* <p>
395+
* ВАЖНО: Граничный токен (с изменённым deltaLine при lineOffset != 0) НЕ включается
396+
* в suffix match, чтобы клиент получил обновлённое значение deltaLine через edit.
397+
* Это критично для случая, когда добавляются только пустые строки без нового кода.
394398
*/
395399
private static int findSuffixMatchWithOffset(int[] prev,
396400
int[] curr,
@@ -447,8 +451,10 @@ private static int findSuffixMatchWithOffset(int[] prev,
447451
}
448452
}
449453
} else if (!foundBoundary && currDeltaLine - prevDeltaLine == lineOffset) {
450-
// Граничный токен — deltaLine отличается ровно на lineOffset
451-
suffixMatch++;
454+
// Граничный токен при вставке/удалении строк.
455+
// НЕ включаем его в suffix match, чтобы он попал в edit и клиент получил
456+
// обновлённое значение deltaLine. Это критично для случая, когда добавляются
457+
// только пустые строки без нового кода.
452458
foundBoundary = true;
453459
} else {
454460
// Не совпадает

src/test/java/com/github/_1c_syntax/bsl/languageserver/BSLWorkspaceServiceTest.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import com.github._1c_syntax.bsl.languageserver.util.CleanupContextBeforeClassAndAfterEachTestMethod;
2626
import com.github._1c_syntax.utils.Absolute;
2727
import org.apache.commons.io.FileUtils;
28+
import org.eclipse.lsp4j.DidChangeConfigurationParams;
2829
import org.eclipse.lsp4j.DidChangeWatchedFilesParams;
2930
import org.eclipse.lsp4j.FileChangeType;
3031
import org.eclipse.lsp4j.FileEvent;
@@ -42,7 +43,10 @@
4243
import java.util.List;
4344

4445
import static org.assertj.core.api.Assertions.assertThat;
46+
import static org.assertj.core.api.Assertions.assertThatCode;
4547
import static org.awaitility.Awaitility.await;
48+
import static org.mockito.Mockito.mock;
49+
import static org.mockito.Mockito.when;
4650

4751
/**
4852
* Тесты для {@link BSLWorkspaceService}.
@@ -276,6 +280,21 @@ void testDidChangeWatchedFiles_MultipleEvents() throws IOException {
276280
assertThat(serverContext.getDocument(uri3)).isNull();
277281
}
278282

283+
@Test
284+
void testDidChangeConfiguration_WithNullSettings() {
285+
// given
286+
// Мокируем params с getSettings(), возвращающим null
287+
// Это соответствует реальному сценарию, когда некоторые LSP клиенты
288+
// отправляют workspace/didChangeConfiguration без настроек
289+
var params = mock(DidChangeConfigurationParams.class);
290+
when(params.getSettings()).thenReturn(null);
291+
292+
// when/then
293+
// Не должно быть исключений при вызове с null settings
294+
assertThatCode(() -> workspaceService.didChangeConfiguration(params))
295+
.doesNotThrowAnyException();
296+
}
297+
279298
/**
280299
* Создает временный тестовый файл с базовым содержимым.
281300
*/

src/test/java/com/github/_1c_syntax/bsl/languageserver/providers/SemanticTokensProviderTest.java

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1402,6 +1402,63 @@ void deltaWithTextInsertedOnSameLine_shouldReturnOptimalDelta() {
14021402
assertThat(editSize).isLessThan(tokens2.getData().size());
14031403
}
14041404

1405+
@Test
1406+
void deltaWithOnlyEmptyLinesInserted_shouldReturnEdits() {
1407+
// given - simulate inserting only empty lines (no new code)
1408+
// This reproduces the bug reported by user: when inserting/deleting empty lines,
1409+
// semantic highlighting "jumps" - tokens shift by 2+ lines
1410+
String bsl1 = """
1411+
Процедура Тест()
1412+
А = 1;
1413+
КонецПроцедуры
1414+
""";
1415+
1416+
String bsl2 = """
1417+
Процедура Тест()
1418+
А = 1;
1419+
1420+
1421+
КонецПроцедуры
1422+
""";
1423+
1424+
DocumentContext context1 = TestUtils.getDocumentContext(bsl1);
1425+
referenceIndexFiller.fill(context1);
1426+
TextDocumentIdentifier textDocId1 = TestUtils.getTextDocumentIdentifier(context1.getUri());
1427+
SemanticTokens tokens1 = provider.getSemanticTokensFull(context1, new SemanticTokensParams(textDocId1));
1428+
1429+
// Verify original tokens structure
1430+
var decoded1 = decode(tokens1.getData());
1431+
// КонецПроцедуры should be on line 2
1432+
var endProcToken1 = decoded1.stream()
1433+
.filter(t -> t.line == 2 && t.length == 14) // КонецПроцедуры has length 14
1434+
.findFirst();
1435+
assertThat(endProcToken1).isPresent();
1436+
1437+
DocumentContext context2 = TestUtils.getDocumentContext(context1.getUri(), bsl2);
1438+
referenceIndexFiller.fill(context2);
1439+
SemanticTokens tokens2 = provider.getSemanticTokensFull(context2, new SemanticTokensParams(textDocId1));
1440+
1441+
// Verify modified tokens structure
1442+
var decoded2 = decode(tokens2.getData());
1443+
// КонецПроцедуры should now be on line 4 (shifted by 2 empty lines)
1444+
var endProcToken2 = decoded2.stream()
1445+
.filter(t -> t.line == 4 && t.length == 14) // КонецПроцедуры has length 14
1446+
.findFirst();
1447+
assertThat(endProcToken2).isPresent();
1448+
1449+
// when
1450+
var deltaParams = new SemanticTokensDeltaParams(textDocId1, tokens1.getResultId());
1451+
var result = provider.getSemanticTokensFullDelta(context2, deltaParams);
1452+
1453+
// then - should return delta with edits, NOT empty edits
1454+
// The bug was that empty edits were returned, causing the client to use stale token positions
1455+
assertThat(result.isRight()).isTrue();
1456+
var delta = result.getRight();
1457+
assertThat(delta.getEdits())
1458+
.as("Delta should contain edits when empty lines are inserted, because token positions changed")
1459+
.isNotEmpty();
1460+
}
1461+
14051462
// endregion
14061463

14071464
// region Range tokens tests

0 commit comments

Comments
 (0)