Skip to content

Commit a92fe5c

Browse files
authored
Merge pull request #3697 from 1c-syntax/copilot/resolve-coloring-nstr-strshablon
Add semantic highlighting for NStr and StrTemplate functions
2 parents 50382d6 + 4e47072 commit a92fe5c

19 files changed

+2047
-857
lines changed

src/main/java/com/github/_1c_syntax/bsl/languageserver/context/symbol/SymbolTree.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@
2727
import lombok.Getter;
2828
import lombok.Value;
2929
import org.antlr.v4.runtime.ParserRuleContext;
30+
import org.eclipse.lsp4j.Position;
3031
import org.eclipse.lsp4j.Range;
32+
import org.eclipse.lsp4j.SymbolKind;
3133

3234
import java.util.ArrayList;
3335
import java.util.Collections;
@@ -198,6 +200,35 @@ public Optional<VariableSymbol> getVariableSymbol(String variableName, SourceDef
198200
);
199201
}
200202

203+
/**
204+
* Поиск самого вложенного символа, содержащего указанную позицию.
205+
* <p>
206+
* Использует иерархический спуск по дереву символов вместо линейного поиска.
207+
*
208+
* @param position Позиция в документе.
209+
* @return Символ, содержащий позицию, или символ модуля, если позиция вне всех символов.
210+
*/
211+
public SourceDefinedSymbol getSymbolAtPosition(Position position) {
212+
return findSymbolAtPosition(module, position);
213+
}
214+
215+
private SourceDefinedSymbol findSymbolAtPosition(SourceDefinedSymbol parent, Position position) {
216+
// Ищем среди детей символ, содержащий позицию
217+
for (var child : parent.getChildren()) {
218+
if (Ranges.containsPosition(child.getRange(), position)) {
219+
// Рекурсивно ищем более вложенный символ
220+
var found = findSymbolAtPosition(child, position);
221+
// Пропускаем Namespace (Region) - ищем дальше или возвращаем parent
222+
if (found.getSymbolKind() == SymbolKind.Namespace) {
223+
continue;
224+
}
225+
return found;
226+
}
227+
}
228+
// Если среди детей не нашли — возвращаем текущий символ
229+
return parent;
230+
}
231+
201232
private List<SourceDefinedSymbol> createChildrenFlat() {
202233
List<SourceDefinedSymbol> symbols = new ArrayList<>();
203234
getChildren().forEach(child -> flatten(child, symbols));

src/main/java/com/github/_1c_syntax/bsl/languageserver/references/ReferenceIndex.java

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@
4343
import org.springframework.stereotype.Component;
4444

4545
import java.net.URI;
46-
import java.util.Collection;
4746
import java.util.List;
4847
import java.util.Locale;
4948
import java.util.Optional;
@@ -276,7 +275,7 @@ private Optional<Reference> buildReference(
276275

277276
return getSourceDefinedSymbol(symbolOccurrence.symbol())
278277
.map((SourceDefinedSymbol symbol) -> {
279-
SourceDefinedSymbol from = getFromSymbol(symbolOccurrence);
278+
var from = getFromSymbol(symbolOccurrence);
280279
return new Reference(from, symbol, uri, range, occurrenceType);
281280
})
282281
.filter(ReferenceIndex::isReferenceAccessible);
@@ -307,20 +306,12 @@ private Optional<SourceDefinedSymbol> getSourceDefinedSymbol(Symbol symbolEntity
307306
}
308307

309308
private SourceDefinedSymbol getFromSymbol(SymbolOccurrence symbolOccurrence) {
310-
311309
var uri = symbolOccurrence.location().getUri();
312310
var position = symbolOccurrence.location().getRange().getStart();
313311

314-
var symbolTree = Optional.ofNullable(serverContext.getDocument(uri))
315-
.map(DocumentContext::getSymbolTree);
316-
return symbolTree
317-
.map(SymbolTree::getChildrenFlat)
318-
.stream()
319-
.flatMap(Collection::stream)
320-
.filter(sourceDefinedSymbol -> sourceDefinedSymbol.getSymbolKind() != SymbolKind.Namespace)
321-
.filter(symbol -> Ranges.containsPosition(symbol.getRange(), position))
322-
.findFirst()
323-
.or(() -> symbolTree.map(SymbolTree::getModule))
312+
return Optional.ofNullable(serverContext.getDocument(uri))
313+
.map(DocumentContext::getSymbolTree)
314+
.map(symbolTree -> symbolTree.getSymbolAtPosition(position))
324315
.orElseThrow();
325316
}
326317

src/main/java/com/github/_1c_syntax/bsl/languageserver/semantictokens/LexicalSemanticTokensSupplier.java

Lines changed: 7 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,14 @@
3030
import org.springframework.stereotype.Component;
3131

3232
import java.util.ArrayList;
33-
import java.util.HashSet;
3433
import java.util.List;
3534
import java.util.Objects;
3635
import java.util.Set;
3736

3837
/**
39-
* Сапплаер семантических токенов для лексических элементов: строк, чисел, операторов и ключевых слов.
38+
* Сапплаер семантических токенов для лексических элементов: чисел, операторов и ключевых слов.
4039
* <p>
41-
* Исключает строки, которые содержат запросы SDBL (они обрабатываются в {@link QuerySemanticTokensSupplier}).
40+
* Строки обрабатываются в {@link StringSemanticTokensSupplier}.
4241
*/
4342
@Component
4443
@RequiredArgsConstructor
@@ -53,8 +52,7 @@ public class LexicalSemanticTokensSupplier implements SemanticTokensSupplier {
5352
BSLLexer.STRING,
5453
BSLLexer.STRINGPART,
5554
BSLLexer.STRINGSTART,
56-
BSLLexer.STRINGTAIL,
57-
BSLLexer.PREPROC_STRING
55+
BSLLexer.STRINGTAIL
5856
);
5957

6058
private static final Set<Integer> OPERATOR_TYPES = Set.of(
@@ -107,14 +105,13 @@ public class LexicalSemanticTokensSupplier implements SemanticTokensSupplier {
107105
public List<SemanticTokenEntry> getSemanticTokens(DocumentContext documentContext) {
108106
List<SemanticTokenEntry> entries = new ArrayList<>();
109107
var tokensFromDefaultChannel = documentContext.getTokensFromDefaultChannel();
110-
var stringsWithQueries = collectStringsWithQueries(documentContext);
111108

112109
for (Token token : tokensFromDefaultChannel) {
113110
var tokenType = token.getType();
114111
var tokenText = Objects.toString(token.getText(), "");
115112
if (!tokenText.isEmpty()) {
116-
// Skip string tokens that contain SDBL tokens - they'll be handled by QuerySemanticTokensSupplier
117-
if (STRING_TYPES.contains(tokenType) && stringsWithQueries.contains(token)) {
113+
// Skip STRING tokens - they are handled by StringSemanticTokensSupplier
114+
if (STRING_TYPES.contains(tokenType)) {
118115
continue;
119116
}
120117
selectAndAddSemanticToken(entries, token, tokenType);
@@ -124,46 +121,15 @@ public List<SemanticTokenEntry> getSemanticTokens(DocumentContext documentContex
124121
return entries;
125122
}
126123

127-
private Set<Token> collectStringsWithQueries(DocumentContext documentContext) {
128-
var queries = documentContext.getQueries();
129-
if (queries.isEmpty()) {
130-
return Set.of();
131-
}
132-
133-
var stringsToSkip = new HashSet<Token>();
134-
for (var query : queries) {
135-
for (Token queryToken : query.getTokens()) {
136-
if (queryToken.getChannel() != Token.DEFAULT_CHANNEL) {
137-
continue;
138-
}
139-
int queryLine = queryToken.getLine();
140-
for (var bslToken : documentContext.getTokensFromDefaultChannel()) {
141-
if (!STRING_TYPES.contains(bslToken.getType())) {
142-
continue;
143-
}
144-
if (bslToken.getLine() == queryLine) {
145-
var bslRange = Ranges.create(bslToken);
146-
int queryStart = queryToken.getCharPositionInLine();
147-
int queryEnd = queryStart + queryToken.getText().length();
148-
if (queryStart >= bslRange.getStart().getCharacter() && queryEnd <= bslRange.getEnd().getCharacter()) {
149-
stringsToSkip.add(bslToken);
150-
}
151-
}
152-
}
153-
}
154-
}
155-
return stringsToSkip;
156-
}
157-
158124
private void selectAndAddSemanticToken(List<SemanticTokenEntry> entries, Token token, int tokenType) {
159125
// Skip '&' and all ANNOTATION_* symbol tokens here to avoid duplicate Decorator emission (handled via AST)
160126
if (tokenType == BSLLexer.AMPERSAND || ANNOTATION_TOKENS.contains(tokenType)) {
161127
return;
162128
}
163129

164-
if (STRING_TYPES.contains(tokenType)) {
130+
if (tokenType == BSLLexer.DATETIME) {
165131
helper.addRange(entries, Ranges.create(token), SemanticTokenTypes.String);
166-
} else if (tokenType == BSLLexer.DATETIME) {
132+
} else if (tokenType == BSLLexer.PREPROC_STRING) {
167133
helper.addRange(entries, Ranges.create(token), SemanticTokenTypes.String);
168134
} else if (NUMBER_TYPES.contains(tokenType)) {
169135
helper.addRange(entries, Ranges.create(token), SemanticTokenTypes.Number);

0 commit comments

Comments
 (0)