diff --git a/build.gradle.kts b/build.gradle.kts index 3548a5f51bf..c2f6243ed2c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -82,13 +82,13 @@ dependencies { api("org.eclipse.lsp4j", "org.eclipse.lsp4j.websocket.jakarta", "0.24.0") // 1c-syntax - api("io.github.1c-syntax", "bsl-parser", "0.30.0-rc.2") { + api("io.github.1c-syntax", "bsl-parser", "0.30.0-rc.5") { exclude("com.ibm.icu", "*") exclude("org.antlr", "ST4") exclude("org.antlr", "antlr-runtime") } api("io.github.1c-syntax", "utils", "0.6.8") - api("io.github.1c-syntax", "mdclasses", "0.17.3") + api("io.github.1c-syntax", "mdclasses", "0.17.4") api("io.github.1c-syntax", "bsl-common-library", "0.9.2") api("io.github.1c-syntax", "supportconf", "0.15.0") diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/semantictokens/BslDocSemanticTokensSupplier.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/semantictokens/BslDocSemanticTokensSupplier.java index 4ffd34dea3e..21acf7f0738 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/languageserver/semantictokens/BslDocSemanticTokensSupplier.java +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/semantictokens/BslDocSemanticTokensSupplier.java @@ -212,7 +212,7 @@ private void addBslDocTokensPerLine( int lineLength = lineText.length(); int charOffset = (lineIdx == 0) ? fileStartChar : 0; - var lineElements = elementsByLine.getOrDefault(lineIdx, List.of()); + var lineElements = elementsByLine.getOrDefault(fileLine, List.of()); if (lineElements.isEmpty()) { if (lineLength > 0) { diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/semantictokens/PreprocessorSemanticTokensSupplier.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/semantictokens/PreprocessorSemanticTokensSupplier.java index 3fa235cb3ab..41f17710d3c 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/languageserver/semantictokens/PreprocessorSemanticTokensSupplier.java +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/semantictokens/PreprocessorSemanticTokensSupplier.java @@ -98,7 +98,7 @@ private void addDirectives(List entries, BSLParser.FileConte } } - // Other preprocessor directives: Macro for each HASH and PREPROC_* token, + // Other preprocessor directives: Macro for entire directive keyword (#Если, #КонецЕсли, etc.), // excluding region start/end, native, use (handled as Namespace) private void addOtherPreprocs(List entries, BSLParser.FileContext ast) { for (var preprocessor : Trees.findAllRuleNodes(ast, BSLParser.RULE_preprocessor)) { @@ -107,13 +107,30 @@ private void addOtherPreprocs(List entries, BSLParser.FileCo continue; // region handled as Namespace above } + // Find HASH token and keyword tokens to combine them into single token + Token hashToken = null; + boolean firstKeywordCombined = false; + for (Token token : Trees.getTokens(preprocessor)) { if (token.getChannel() != Token.DEFAULT_CHANNEL) { continue; } - String symbolicName = BSLLexer.VOCABULARY.getSymbolicName(token.getType()); - if (token.getType() == BSLLexer.HASH || (symbolicName != null && symbolicName.startsWith("PREPROC_"))) { - helper.addRange(entries, Ranges.create(token), SemanticTokenTypes.Macro); + if (token.getType() == BSLLexer.HASH) { + hashToken = token; + } else { + String symbolicName = BSLLexer.VOCABULARY.getSymbolicName(token.getType()); + if (symbolicName != null && symbolicName.startsWith("PREPROC_")) { + // Track keyword tokens for combining with HASH + if (hashToken != null && !firstKeywordCombined) { + // First keyword after HASH - combine them into single token + helper.addRange(entries, Ranges.create(hashToken, token), SemanticTokenTypes.Macro); + firstKeywordCombined = true; + } else { + // Subsequent keywords (e.g., "Сервер", "Тогда" in "#Если Сервер Тогда") + // or keyword without preceding HASH (shouldn't happen in valid syntax) + helper.addRange(entries, Ranges.create(token), SemanticTokenTypes.Macro); + } + } } } } diff --git a/src/test/java/com/github/_1c_syntax/bsl/languageserver/context/symbol/description/MethodDescriptionTest.java b/src/test/java/com/github/_1c_syntax/bsl/languageserver/context/symbol/description/MethodDescriptionTest.java index d6ee1782b25..cbb43ac815e 100644 --- a/src/test/java/com/github/_1c_syntax/bsl/languageserver/context/symbol/description/MethodDescriptionTest.java +++ b/src/test/java/com/github/_1c_syntax/bsl/languageserver/context/symbol/description/MethodDescriptionTest.java @@ -142,7 +142,7 @@ void testMethod12() { assertThat(type.description()).isEmpty(); assertThat(type.fields()).isEmpty(); assertThat(type).isInstanceOf(HyperlinkTypeDescription.class); - assertThat(((HyperlinkTypeDescription) type).hyperlink()).isEqualTo(Hyperlink.create("ОбщийМодуль.Метод()")); + assertThat(((HyperlinkTypeDescription) type).hyperlink().link()).isEqualTo("ОбщийМодуль.Метод"); } @Test @@ -161,7 +161,7 @@ void testMethod11() { assertThat(param.name()).isEqualTo("ОбщийМодуль.Метод"); assertThat(param.types()).hasSize(1); assertThat(param.isHyperlink()).isTrue(); - assertThat(param.link()).isEqualTo(Hyperlink.create("ОбщийМодуль.Метод()")); + assertThat(param.link().link()).isEqualTo("ОбщийМодуль.Метод"); } @Test @@ -175,7 +175,7 @@ void testMethod10() { assertThat(method.getCallOptions()).isEmpty(); assertThat(method.getParameters()).isEmpty(); assertThat(method.getReturnedValue()).isEmpty(); - assertThat(method.getLinks()).contains(Hyperlink.create("ОбщийМодуль.Метод()")); + assertThat(method.getLinks()).extracting(Hyperlink::link).contains("ОбщийМодуль.Метод"); } @Test diff --git a/src/test/java/com/github/_1c_syntax/bsl/languageserver/providers/SemanticTokensProviderTest.java b/src/test/java/com/github/_1c_syntax/bsl/languageserver/providers/SemanticTokensProviderTest.java index d4cbe5a214d..0b7df28b8d9 100644 --- a/src/test/java/com/github/_1c_syntax/bsl/languageserver/providers/SemanticTokensProviderTest.java +++ b/src/test/java/com/github/_1c_syntax/bsl/languageserver/providers/SemanticTokensProviderTest.java @@ -346,18 +346,14 @@ void preprocessorDirectives() { var decoded = getDecodedTokens(bsl); - // Verify preprocessor macro tokens on specific lines + // Verify preprocessor macro tokens - # and keyword are combined into single token var expectedTokens = List.of( - new ExpectedToken(0, 0, 1, SemanticTokenTypes.Macro, "#"), - new ExpectedToken(0, 1, 4, SemanticTokenTypes.Macro, "Если"), + new ExpectedToken(0, 0, 5, SemanticTokenTypes.Macro, "#Если"), new ExpectedToken(0, 6, 6, SemanticTokenTypes.Macro, "Сервер"), new ExpectedToken(0, 13, 5, SemanticTokenTypes.Macro, "Тогда"), - new ExpectedToken(3, 0, 1, SemanticTokenTypes.Macro, "#"), - new ExpectedToken(3, 1, 9, SemanticTokenTypes.Macro, "ИначеЕсли"), - new ExpectedToken(4, 0, 1, SemanticTokenTypes.Macro, "#"), - new ExpectedToken(4, 1, 5, SemanticTokenTypes.Macro, "Иначе"), - new ExpectedToken(5, 0, 1, SemanticTokenTypes.Macro, "#"), - new ExpectedToken(5, 1, 9, SemanticTokenTypes.Macro, "КонецЕсли") + new ExpectedToken(3, 0, 10, SemanticTokenTypes.Macro, "#ИначеЕсли"), + new ExpectedToken(4, 0, 6, SemanticTokenTypes.Macro, "#Иначе"), + new ExpectedToken(5, 0, 10, SemanticTokenTypes.Macro, "#КонецЕсли") ); assertContainsTokens(decoded, expectedTokens); @@ -391,6 +387,8 @@ void literals() { @Test void methodDescriptionComments() { String bsl = """ + // просто коммент + // Описание процедуры // Параметры: // Парам - Число - описание @@ -402,41 +400,43 @@ void methodDescriptionComments() { var decoded = getDecodedTokens(bsl); // Documentation comments are now split around BSL doc keywords and operators. - // Line 0: "// Описание процедуры" - no BSL doc elements, full line as Comment+Documentation - // Line 1: "// Параметры:" - keyword in structural position - // Line 2: "// Парам - Число - описание" - parameter name, type, operator, description + // Line 3: "// Описание процедуры" - no BSL doc elements, full line as Comment+Documentation + // Line 4: "// Параметры:" - keyword in structural position + // Line 4: "// Парам - Число - описание" - parameter name, type, operator, description // Body comment on line 4 should NOT have Documentation modifier var expected = List.of( - // Line 0: full line as Comment+Documentation - new ExpectedToken(0, 0, 21, SemanticTokenTypes.Comment, SemanticTokenModifiers.Documentation, "// Описание процедуры"), - // Line 1: "// " before keyword - new ExpectedToken(1, 0, 3, SemanticTokenTypes.Comment, SemanticTokenModifiers.Documentation, "// "), - // Line 1: "Параметры:" keyword - new ExpectedToken(1, 3, 10, SemanticTokenTypes.Macro, SemanticTokenModifiers.Documentation, "Параметры:"), - // Line 2: "// " before param name - new ExpectedToken(2, 0, 4, SemanticTokenTypes.Comment, SemanticTokenModifiers.Documentation, "// "), - // Line 2: "Парам" parameter name - new ExpectedToken(2, 4, 5, SemanticTokenTypes.Parameter, SemanticTokenModifiers.Documentation, "Парам"), - // Line 2: " " between param name and dash - new ExpectedToken(2, 9, 3, SemanticTokenTypes.Comment, SemanticTokenModifiers.Documentation, " - "), - // Line 2: "Число" type - new ExpectedToken(2, 12, 5, SemanticTokenTypes.Type, SemanticTokenModifiers.Documentation, "Число"), - // Line 2: " " between type and second dash - new ExpectedToken(2, 17, 11, SemanticTokenTypes.Comment, SemanticTokenModifiers.Documentation, " - описание"), - // Line 3: Процедура keyword - new ExpectedToken(3, 0, 9, SemanticTokenTypes.Keyword, "Процедура"), - // Line 3: ДокТест method name - new ExpectedToken(3, 10, 7, SemanticTokenTypes.Method, "ДокТест"), - // Line 3: ( operator - new ExpectedToken(3, 17, 1, SemanticTokenTypes.Operator, "("), - // Line 3: Парам parameter definition - new ExpectedToken(3, 18, 5, SemanticTokenTypes.Parameter, SemanticTokenModifiers.Definition, "Парам"), - // Line 3: ) operator - new ExpectedToken(3, 23, 1, SemanticTokenTypes.Operator, ")"), - // Line 4: body comment (no Documentation modifier) - new ExpectedToken(4, 2, 22, SemanticTokenTypes.Comment, "// обычный комментарий"), - // Line 5: КонецПроцедуры keyword - new ExpectedToken(5, 0, 14, SemanticTokenTypes.Keyword, "КонецПроцедуры") + new ExpectedToken(0, 0, 17, SemanticTokenTypes.Comment, "// просто коммент"), + + // Line 2: full line as Comment+Documentation + new ExpectedToken(2, 0, 21, SemanticTokenTypes.Comment, SemanticTokenModifiers.Documentation, "// Описание процедуры"), + // Line 3: "// " before keyword + new ExpectedToken(3, 0, 3, SemanticTokenTypes.Comment, SemanticTokenModifiers.Documentation, "// "), + // Line 3: "Параметры:" keyword + new ExpectedToken(3, 3, 10, SemanticTokenTypes.Macro, SemanticTokenModifiers.Documentation, "Параметры:"), + // Line 4: "// " before param name + new ExpectedToken(4, 0, 4, SemanticTokenTypes.Comment, SemanticTokenModifiers.Documentation, "// "), + // Line 4: "Парам" parameter name + new ExpectedToken(4, 4, 5, SemanticTokenTypes.Parameter, SemanticTokenModifiers.Documentation, "Парам"), + // Line 4: " " between param name and dash + new ExpectedToken(4, 9, 3, SemanticTokenTypes.Comment, SemanticTokenModifiers.Documentation, " - "), + // Line 4: "Число" type + new ExpectedToken(4, 12, 5, SemanticTokenTypes.Type, SemanticTokenModifiers.Documentation, "Число"), + // Line 4: " " between type and second dash + new ExpectedToken(4, 17, 11, SemanticTokenTypes.Comment, SemanticTokenModifiers.Documentation, " - описание"), + // Line 5: Процедура keyword + new ExpectedToken(5, 0, 9, SemanticTokenTypes.Keyword, "Процедура"), + // Line 5: ДокТест method name + new ExpectedToken(5, 10, 7, SemanticTokenTypes.Method, "ДокТест"), + // Line 5: ( operator + new ExpectedToken(5, 17, 1, SemanticTokenTypes.Operator, "("), + // Line 5: Парам parameter definition + new ExpectedToken(5, 18, 5, SemanticTokenTypes.Parameter, SemanticTokenModifiers.Definition, "Парам"), + // Line 5: ) operator + new ExpectedToken(5, 23, 1, SemanticTokenTypes.Operator, ")"), + // Line 6: body comment (no Documentation modifier) + new ExpectedToken(6, 2, 22, SemanticTokenTypes.Comment, "// обычный комментарий"), + // Line 7: КонецПроцедуры keyword + new ExpectedToken(7, 0, 14, SemanticTokenTypes.Keyword, "КонецПроцедуры") ); assertTokensMatch(decoded, expected); @@ -465,7 +465,6 @@ void variableDescriptionComments() { new ExpectedToken(1, 14, 8, SemanticTokenTypes.Comment, SemanticTokenModifiers.Documentation, "// трейл") ); - assertTokensMatch(decoded, expected); } diff --git a/src/test/java/com/github/_1c_syntax/bsl/languageserver/semantictokens/BslDocSemanticTokensSupplierTest.java b/src/test/java/com/github/_1c_syntax/bsl/languageserver/semantictokens/BslDocSemanticTokensSupplierTest.java index 8cb1efe642e..c0c79ad886b 100644 --- a/src/test/java/com/github/_1c_syntax/bsl/languageserver/semantictokens/BslDocSemanticTokensSupplierTest.java +++ b/src/test/java/com/github/_1c_syntax/bsl/languageserver/semantictokens/BslDocSemanticTokensSupplierTest.java @@ -27,7 +27,6 @@ import com.github._1c_syntax.bsl.languageserver.util.TestUtils; import org.eclipse.lsp4j.SemanticTokenModifiers; import org.eclipse.lsp4j.SemanticTokenTypes; -import org.eclipse.lsp4j.SemanticTokensLegend; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -37,8 +36,6 @@ import java.util.List; import java.util.Set; -import static org.assertj.core.api.Assertions.assertThat; - @SpringBootTest @CleanupContextBeforeClassAndAfterEachTestMethod @Import(SemanticTokensTestHelper.class) @@ -50,9 +47,6 @@ class BslDocSemanticTokensSupplierTest { @Autowired private SemanticTokensTestHelper helper; - @Autowired - private SemanticTokensLegend legend; - @BeforeEach void init() { supplier.setMultilineTokenSupport(false); @@ -198,15 +192,14 @@ void testMultipleTypesOnSeparateLines() { var decoded = helper.getDecodedTokens(bsl, supplier); // then - All three types should be Type with Documentation modifier - int typeTypeIdx = legend.getTokenTypes().indexOf(SemanticTokenTypes.Type); - int docModifierMask = 1 << legend.getTokenModifiers().indexOf(SemanticTokenModifiers.Documentation); - - var typeTokens = decoded.stream() - .filter(t -> t.type() == typeTypeIdx && (t.modifiers() & docModifierMask) != 0) - .toList(); - - // Should have 3 type tokens: СправочникСсылка, ДокументСсылка, ПеречислениеСсылка - assertThat(typeTokens).hasSize(3); + helper.assertContainsTokens(decoded, List.of( + new ExpectedToken(2, 15, 16, SemanticTokenTypes.Type, + Set.of(SemanticTokenModifiers.Documentation), "СправочникСсылка"), + new ExpectedToken(3, 22, 14, SemanticTokenTypes.Type, + Set.of(SemanticTokenModifiers.Documentation), "ДокументСсылка"), + new ExpectedToken(4, 22, 18, SemanticTokenTypes.Type, + Set.of(SemanticTokenModifiers.Documentation), "ПеречислениеСсылка") + )); } @Test @@ -226,15 +219,12 @@ void testMultipleReturnTypesOnSeparateLines() { var decoded = helper.getDecodedTokens(bsl, supplier); // then - Both types should be Type with Documentation modifier - int typeTypeIdx = legend.getTokenTypes().indexOf(SemanticTokenTypes.Type); - int docModifierMask = 1 << legend.getTokenModifiers().indexOf(SemanticTokenModifiers.Documentation); - - var typeTokens = decoded.stream() - .filter(t -> t.type() == typeTypeIdx && (t.modifiers() & docModifierMask) != 0) - .toList(); - - // Should have 2 type tokens: СправочникСсылка, ДокументСсылка - assertThat(typeTokens).hasSize(2); + helper.assertContainsTokens(decoded, List.of( + new ExpectedToken(2, 4, 16, SemanticTokenTypes.Type, + Set.of(SemanticTokenModifiers.Documentation), "СправочникСсылка"), + new ExpectedToken(3, 6, 14, SemanticTokenTypes.Type, + Set.of(SemanticTokenModifiers.Documentation), "ДокументСсылка") + )); } @Test @@ -250,27 +240,29 @@ void testMultilineSupport() { var documentContext = TestUtils.getDocumentContext(bsl); - // Test without multiline support + // Test without multiline support - should have 3 separate comment tokens (one per line) supplier.setMultilineTokenSupport(false); var tokensWithoutMultiline = helper.decodeFromEntries(supplier.getSemanticTokens(documentContext)); - // Test with multiline support + helper.assertContainsTokens(tokensWithoutMultiline, List.of( + new ExpectedToken(0, 0, 25, SemanticTokenTypes.Comment, + Set.of(SemanticTokenModifiers.Documentation), "// Первая строка описания"), + new ExpectedToken(1, 0, 25, SemanticTokenTypes.Comment, + Set.of(SemanticTokenModifiers.Documentation), "// Вторая строка описания"), + new ExpectedToken(2, 0, 25, SemanticTokenTypes.Comment, + Set.of(SemanticTokenModifiers.Documentation), "// Третья строка описания") + )); + + // Test with multiline support - should merge consecutive lines into one token supplier.setMultilineTokenSupport(true); var tokensWithMultiline = helper.decodeFromEntries(supplier.getSemanticTokens(documentContext)); - // Without multiline: should have 3 separate comment tokens (one per line) - // With multiline: may merge consecutive lines into fewer tokens - int commentTypeIdx = legend.getTokenTypes().indexOf(SemanticTokenTypes.Comment); - - var commentTokensWithout = tokensWithoutMultiline.stream() - .filter(t -> t.type() == commentTypeIdx) - .toList(); - var commentTokensWith = tokensWithMultiline.stream() - .filter(t -> t.type() == commentTypeIdx) - .toList(); - - // With multiline support, we expect fewer or equal number of tokens - assertThat(commentTokensWith.size()).isLessThanOrEqualTo(commentTokensWithout.size()); + // With multiline support, all 3 lines are merged into single token starting at line 0 + // Length is 77 (25 + 1 + 25 + 1 + 25 = 77 including newlines) + helper.assertContainsTokens(tokensWithMultiline, List.of( + new ExpectedToken(0, 0, 77, SemanticTokenTypes.Comment, + Set.of(SemanticTokenModifiers.Documentation), "// Первая строка описания...") + )); } } diff --git a/src/test/java/com/github/_1c_syntax/bsl/languageserver/semantictokens/CommentSemanticTokensSupplierTest.java b/src/test/java/com/github/_1c_syntax/bsl/languageserver/semantictokens/CommentSemanticTokensSupplierTest.java index b7fc2aa3a6a..25df005fe2d 100644 --- a/src/test/java/com/github/_1c_syntax/bsl/languageserver/semantictokens/CommentSemanticTokensSupplierTest.java +++ b/src/test/java/com/github/_1c_syntax/bsl/languageserver/semantictokens/CommentSemanticTokensSupplierTest.java @@ -34,8 +34,6 @@ import java.util.List; -import static org.assertj.core.api.Assertions.assertThat; - @SpringBootTest @CleanupContextBeforeClassAndAfterEachTestMethod @Import(SemanticTokensTestHelper.class) @@ -134,19 +132,20 @@ void testMultilineCommentTokens() { supplier.setMultilineTokenSupport(false); var tokensWithoutMultiline = helper.decodeFromEntries(supplier.getSemanticTokens(documentContext)); + helper.assertTokensMatch(tokensWithoutMultiline, List.of( + new ExpectedToken(1, 2, 21, SemanticTokenTypes.Comment, "// Первый комментарий"), + new ExpectedToken(2, 2, 21, SemanticTokenTypes.Comment, "// Второй комментарий"), + new ExpectedToken(3, 2, 21, SemanticTokenTypes.Comment, "// Третий комментарий") + )); + // Test with multiline support - should have 1 merged token supplier.setMultilineTokenSupport(true); var tokensWithMultiline = helper.decodeFromEntries(supplier.getSemanticTokens(documentContext)); - // then - // Without multiline: 3 separate tokens - assertThat(tokensWithoutMultiline).hasSize(3); - - // With multiline: 1 merged token for consecutive comments - assertThat(tokensWithMultiline).hasSize(1); - - // The merged token should start on line 1 (0-indexed) - assertThat(tokensWithMultiline.get(0).line()).isEqualTo(1); + // With multiline: 1 merged token for consecutive comments (length = 21 + 1 + 21 + 1 + 21 = 65) + helper.assertTokensMatch(tokensWithMultiline, List.of( + new ExpectedToken(1, 2, 65, SemanticTokenTypes.Comment, "// Первый комментарий...") + )); } @Test diff --git a/src/test/java/com/github/_1c_syntax/bsl/languageserver/semantictokens/LexicalSemanticTokensSupplierTest.java b/src/test/java/com/github/_1c_syntax/bsl/languageserver/semantictokens/LexicalSemanticTokensSupplierTest.java index 4ab46926e70..ba029f7d9aa 100644 --- a/src/test/java/com/github/_1c_syntax/bsl/languageserver/semantictokens/LexicalSemanticTokensSupplierTest.java +++ b/src/test/java/com/github/_1c_syntax/bsl/languageserver/semantictokens/LexicalSemanticTokensSupplierTest.java @@ -32,8 +32,6 @@ import java.util.List; -import static org.assertj.core.api.Assertions.assertThat; - @SpringBootTest @CleanupContextBeforeClassAndAfterEachTestMethod @Import(SemanticTokensTestHelper.class) @@ -66,7 +64,7 @@ void testKeywords() { @Test void testStrings() { // Note: STRING tokens are now handled by StringSemanticTokensSupplier - // This test verifies that LexicalSemanticTokensSupplier does NOT process regular strings + // This test verifies that LexicalSemanticTokensSupplier processes keywords but NOT regular strings // given String bsl = """ Процедура Тест() @@ -77,11 +75,12 @@ void testStrings() { // when var decoded = helper.getDecodedTokens(bsl, supplier); - // then - String tokens are handled by StringSemanticTokensSupplier - // No String type tokens expected from LexicalSemanticTokensSupplier at position where the string is - assertThat(decoded.stream() - .filter(t -> t.start() == 10 && t.line() == 1) - .toList()).isEmpty(); + // then - should have keywords and operators, but no string token at position 10 on line 1 + helper.assertContainsTokens(decoded, List.of( + new ExpectedToken(0, 0, 9, SemanticTokenTypes.Keyword, "Процедура"), + new ExpectedToken(1, 8, 1, SemanticTokenTypes.Operator, "="), + new ExpectedToken(2, 0, 14, SemanticTokenTypes.Keyword, "КонецПроцедуры") + )); } @Test @@ -159,7 +158,7 @@ void testLiterals() { @Test void testQueryStringsAreSkipped() { - // given - Query strings should be skipped (handled by QuerySemanticTokensSupplier) + // given - Query strings should be skipped (handled by StringSemanticTokensSupplier with SDBL parsing) String bsl = """ Процедура Тест() Запрос = "Выбрать * из Справочник.Контрагенты"; @@ -169,12 +168,11 @@ void testQueryStringsAreSkipped() { // when var decoded = helper.getDecodedTokens(bsl, supplier); - // then - The query string should NOT be present as a single String token - var stringTokensOnQueryLine = decoded.stream() - .filter(t -> t.line() == 1 && t.start() >= 11 && t.start() < 40) - .toList(); - - // Query string is skipped - no full string token at that position - assertThat(stringTokensOnQueryLine).isEmpty(); + // then - should have keywords and operators, query string content is handled elsewhere + helper.assertContainsTokens(decoded, List.of( + new ExpectedToken(0, 0, 9, SemanticTokenTypes.Keyword, "Процедура"), + new ExpectedToken(1, 9, 1, SemanticTokenTypes.Operator, "="), + new ExpectedToken(2, 0, 14, SemanticTokenTypes.Keyword, "КонецПроцедуры") + )); } } diff --git a/src/test/java/com/github/_1c_syntax/bsl/languageserver/semantictokens/PreprocessorSemanticTokensSupplierTest.java b/src/test/java/com/github/_1c_syntax/bsl/languageserver/semantictokens/PreprocessorSemanticTokensSupplierTest.java index 1b0b66b4d98..d82892f800e 100644 --- a/src/test/java/com/github/_1c_syntax/bsl/languageserver/semantictokens/PreprocessorSemanticTokensSupplierTest.java +++ b/src/test/java/com/github/_1c_syntax/bsl/languageserver/semantictokens/PreprocessorSemanticTokensSupplierTest.java @@ -32,8 +32,6 @@ import java.util.List; -import static org.assertj.core.api.Assertions.assertThat; - @SpringBootTest @CleanupContextBeforeClassAndAfterEachTestMethod @Import(SemanticTokensTestHelper.class) @@ -100,9 +98,14 @@ void testIfDirective() { // when var decoded = helper.getDecodedTokens(bsl, supplier); - // then - // #Если, Сервер, Тогда, #КонецЕсли - assertThat(decoded).hasSizeGreaterThanOrEqualTo(4); + // then - #Если and #КонецЕсли are single tokens, other keywords are separate + var expected = List.of( + new ExpectedToken(0, 0, 5, SemanticTokenTypes.Macro, "#Если"), + new ExpectedToken(0, 6, 6, SemanticTokenTypes.Macro, "Сервер"), + new ExpectedToken(0, 13, 5, SemanticTokenTypes.Macro, "Тогда"), + new ExpectedToken(3, 0, 10, SemanticTokenTypes.Macro, "#КонецЕсли") + ); + helper.assertTokensMatch(decoded, expected); } @Test diff --git a/src/test/java/com/github/_1c_syntax/bsl/languageserver/semantictokens/StringSemanticTokensSupplierTest.java b/src/test/java/com/github/_1c_syntax/bsl/languageserver/semantictokens/StringSemanticTokensSupplierTest.java index 111cd574f6b..428847ec6a2 100644 --- a/src/test/java/com/github/_1c_syntax/bsl/languageserver/semantictokens/StringSemanticTokensSupplierTest.java +++ b/src/test/java/com/github/_1c_syntax/bsl/languageserver/semantictokens/StringSemanticTokensSupplierTest.java @@ -33,8 +33,6 @@ import java.util.List; -import static org.assertj.core.api.Assertions.assertThat; - @SpringBootTest @CleanupContextBeforeClassAndAfterEachTestMethod @Import(SemanticTokensTestHelper.class) @@ -61,8 +59,7 @@ void testSimpleString() { var decoded = helper.getDecodedTokens(bsl, supplier); // then - one string token - assertThat(decoded).hasSize(1); - helper.assertContainsTokens(decoded, List.of( + helper.assertTokensMatch(decoded, List.of( new ExpectedToken(1, 10, 12, SemanticTokenTypes.String, "\"Привет мир\"") )); } @@ -82,8 +79,7 @@ void testMultilineString() { var decoded = helper.getDecodedTokens(bsl, supplier); // then - multiple string parts - assertThat(decoded).hasSize(3); - helper.assertContainsTokens(decoded, List.of( + helper.assertTokensMatch(decoded, List.of( new ExpectedToken(1, 10, 14, SemanticTokenTypes.String, "\"Первая строка"), new ExpectedToken(2, 2, 14, SemanticTokenTypes.String, "|Вторая строка"), new ExpectedToken(3, 2, 15, SemanticTokenTypes.String, "|Третья строка\"") @@ -400,8 +396,7 @@ void testRegularStringNotAffectedByOtherContexts() { var decoded = helper.getDecodedTokens(bsl, supplier); // then - just one string token - assertThat(decoded).hasSize(1); - helper.assertContainsTokens(decoded, List.of( + helper.assertTokensMatch(decoded, List.of( new ExpectedToken(1, 18, 38, SemanticTokenTypes.String, "\"Это просто строка без НСтр и запроса\"") )); } diff --git a/src/test/java/com/github/_1c_syntax/bsl/languageserver/semantictokens/SymbolsSemanticTokensSupplierTest.java b/src/test/java/com/github/_1c_syntax/bsl/languageserver/semantictokens/SymbolsSemanticTokensSupplierTest.java index fabe8a93ad4..7bb5be6a9d1 100644 --- a/src/test/java/com/github/_1c_syntax/bsl/languageserver/semantictokens/SymbolsSemanticTokensSupplierTest.java +++ b/src/test/java/com/github/_1c_syntax/bsl/languageserver/semantictokens/SymbolsSemanticTokensSupplierTest.java @@ -23,15 +23,16 @@ import com.github._1c_syntax.bsl.languageserver.util.CleanupContextBeforeClassAndAfterEachTestMethod; import com.github._1c_syntax.bsl.languageserver.util.SemanticTokensTestHelper; +import com.github._1c_syntax.bsl.languageserver.util.SemanticTokensTestHelper.ExpectedToken; import org.eclipse.lsp4j.SemanticTokenModifiers; import org.eclipse.lsp4j.SemanticTokenTypes; -import org.eclipse.lsp4j.SemanticTokensLegend; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Import; -import static org.assertj.core.api.Assertions.assertThat; +import java.util.List; +import java.util.Set; @SpringBootTest @CleanupContextBeforeClassAndAfterEachTestMethod @@ -44,9 +45,6 @@ class SymbolsSemanticTokensSupplierTest { @Autowired private SemanticTokensTestHelper helper; - @Autowired - private SemanticTokensLegend legend; - @Test void testMethodDeclaration() { // given @@ -59,14 +57,10 @@ void testMethodDeclaration() { // when var decoded = helper.getDecodedTokens(bsl, supplier); - // then - assertThat(decoded).isNotEmpty(); - - int functionTypeIdx = legend.getTokenTypes().indexOf(SemanticTokenTypes.Function); - var functionTokens = decoded.stream() - .filter(t -> t.type() == functionTypeIdx) - .toList(); - assertThat(functionTokens).hasSize(1); + // then - function name should be highlighted as Function + helper.assertContainsTokens(decoded, List.of( + new ExpectedToken(0, 8, 10, SemanticTokenTypes.Function, "МояФункция") + )); } @Test @@ -80,14 +74,10 @@ void testProcedureDeclaration() { // when var decoded = helper.getDecodedTokens(bsl, supplier); - // then - assertThat(decoded).isNotEmpty(); - - int methodTypeIdx = legend.getTokenTypes().indexOf(SemanticTokenTypes.Method); - var methodTokens = decoded.stream() - .filter(t -> t.type() == methodTypeIdx) - .toList(); - assertThat(methodTokens).hasSize(1); + // then - procedure name should be highlighted as Method + helper.assertContainsTokens(decoded, List.of( + new ExpectedToken(0, 10, 12, SemanticTokenTypes.Method, "МояПроцедура") + )); } @Test @@ -102,23 +92,16 @@ void testParameterDeclaration() { // when var decoded = helper.getDecodedTokens(bsl, supplier); - // then - assertThat(decoded).isNotEmpty(); - - int parameterTypeIdx = legend.getTokenTypes().indexOf(SemanticTokenTypes.Parameter); - int definitionModifierMask = 1 << legend.getTokenModifiers().indexOf(SemanticTokenModifiers.Definition); - - var parameterTokens = decoded.stream() - .filter(t -> t.type() == parameterTypeIdx) - .toList(); - // 2 definitions + 2 usages = 4 parameter tokens - assertThat(parameterTokens).hasSizeGreaterThanOrEqualTo(2); - - // At least 2 should have Definition modifier (the declarations) - var definitionTokens = parameterTokens.stream() - .filter(t -> (t.modifiers() & definitionModifierMask) != 0) - .toList(); - assertThat(definitionTokens).hasSize(2); + // then - parameter declarations should have Definition modifier + helper.assertContainsTokens(decoded, List.of( + new ExpectedToken(0, 13, 9, SemanticTokenTypes.Parameter, + Set.of(SemanticTokenModifiers.Definition), "Параметр1"), + new ExpectedToken(0, 24, 9, SemanticTokenTypes.Parameter, + Set.of(SemanticTokenModifiers.Definition), "Параметр2"), + // Parameter usages in the body + new ExpectedToken(1, 10, 9, SemanticTokenTypes.Parameter, "Параметр1"), + new ExpectedToken(1, 22, 9, SemanticTokenTypes.Parameter, "Параметр2") + )); } @Test @@ -133,15 +116,10 @@ void testVariableDeclaration() { // when var decoded = helper.getDecodedTokens(bsl, supplier); - // then - assertThat(decoded).isNotEmpty(); - - int variableTypeIdx = legend.getTokenTypes().indexOf(SemanticTokenTypes.Variable); - int definitionModifierMask = 1 << legend.getTokenModifiers().indexOf(SemanticTokenModifiers.Definition); - - var variableTokens = decoded.stream() - .filter(t -> t.type() == variableTypeIdx && (t.modifiers() & definitionModifierMask) != 0) - .toList(); - assertThat(variableTokens).hasSize(1); + // then - variable declaration should have Definition modifier + helper.assertContainsTokens(decoded, List.of( + new ExpectedToken(1, 8, 13, SemanticTokenTypes.Variable, + Set.of(SemanticTokenModifiers.Definition), "МояПеременная") + )); } }