Skip to content

Commit 5d1f75d

Browse files
committed
Refactor Location class to use record type and improve range handling methods
1 parent 18c093f commit 5d1f75d

File tree

8 files changed

+214
-62
lines changed

8 files changed

+214
-62
lines changed

src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/MissingCommonModuleMethodDiagnostic.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,14 @@ private Optional<CallData> getReferenceToMethodCall(SymbolOccurrence symbolOccur
8585
// т.к. через refIndex.getReferences нельзя получить приватные методы, приходится обходить символы модуля
8686
final var methodSymbol = document.get()
8787
.getSymbolTree().getMethodSymbol(symbol.symbolName());
88+
final var location = symbolOccurrence.location();
89+
final var locationRange = location.getRange();
8890
if (methodSymbol.isEmpty()) {
89-
final var location = symbolOccurrence.location();
9091
// Нельзя использовать symbol.getSymbolName(), т.к. имя в нижнем регистре
9192
return Optional.of(
9293
new CallData(mdObject.get().getName(),
93-
getMethodNameByLocation(documentContext.getAst(), location.getRange()),
94-
location.getRange(), false, false));
94+
getMethodNameByLocation(documentContext.getAst(), locationRange),
95+
locationRange, false, false));
9596
}
9697
// вызовы приватных методов внутри самого модуля пропускаем
9798
if (document.get().getUri().equals(documentContext.getUri())) {
@@ -101,7 +102,7 @@ private Optional<CallData> getReferenceToMethodCall(SymbolOccurrence symbolOccur
101102
.filter(methodSymbol2 -> !methodSymbol2.isExport())
102103
.map(methodSymbol1 -> new CallData(mdObject.get().getName(),
103104
methodSymbol1.getName(),
104-
symbolOccurrence.location().getRange(), true, true));
105+
locationRange, true, true));
105106
}
106107

107108
private void fireIssue(CallData callData) {

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,12 @@ public List<Reference> getReferencesTo(SourceDefinedSymbol symbol) {
107107
*/
108108
public Optional<Reference> getReference(URI uri, Position position) {
109109
return locationRepository.getSymbolOccurrencesByLocationUri(uri)
110-
.filter(symbolOccurrence -> Ranges.containsPosition(symbolOccurrence.location().getRange(), position))
110+
.filter((SymbolOccurrence symbolOccurrence) -> {
111+
var location = symbolOccurrence.location();
112+
return Ranges.containsPosition(
113+
location.startLine(), location.startCharacter(), location.endLine(), location.endCharacter(),
114+
position);
115+
})
111116
.findAny()
112117
.flatMap(this::buildReference);
113118
}
@@ -269,7 +274,7 @@ private Optional<Reference> buildReference(
269274
SymbolOccurrence symbolOccurrence
270275
) {
271276

272-
var uri = symbolOccurrence.location().getUri();
277+
var uri = symbolOccurrence.location().uri();
273278
var range = symbolOccurrence.location().getRange();
274279
var occurrenceType = symbolOccurrence.occurrenceType();
275280

@@ -306,8 +311,8 @@ private Optional<SourceDefinedSymbol> getSourceDefinedSymbol(Symbol symbolEntity
306311
}
307312

308313
private SourceDefinedSymbol getFromSymbol(SymbolOccurrence symbolOccurrence) {
309-
var uri = symbolOccurrence.location().getUri();
310-
var position = symbolOccurrence.location().getRange().getStart();
314+
var uri = symbolOccurrence.location().uri();
315+
var position = symbolOccurrence.location().getStart();
311316

312317
return Optional.ofNullable(serverContext.getDocument(uri))
313318
.map(DocumentContext::getSymbolTree)

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@ private VariableSymbolReferenceIndexFinder(DocumentContext documentContext) {
330330
public ParserRuleContext visitModuleVarDeclaration(BSLParser.ModuleVarDeclarationContext ctx) {
331331
findVariableSymbol(ctx.var_name().getText()).ifPresent(s -> {
332332
if (notVariableInitialization(ctx, s)) {
333+
333334
addVariableUsage(
334335
s.getRootParent(SymbolKind.Method),
335336
ctx.var_name().getText(),

src/main/java/com/github/_1c_syntax/bsl/languageserver/references/model/Location.java

Lines changed: 14 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -22,57 +22,31 @@
2222
package com.github._1c_syntax.bsl.languageserver.references.model;
2323

2424
import com.github._1c_syntax.bsl.languageserver.utils.Ranges;
25-
import lombok.AllArgsConstructor;
26-
import lombok.Builder;
27-
import lombok.Value;
25+
import org.eclipse.lsp4j.Position;
2826
import org.eclipse.lsp4j.Range;
2927

3028
import java.net.URI;
3129

3230
/**
3331
* Месторасположение появления символа.
32+
*
33+
* @param uri URI файла, в котором расположен символ.
34+
* @param startLine Строка, в которой начинается символ.
35+
* @param startCharacter Столбец, в котором начинается символ.
36+
* @param endLine Строка, в которой заканчивается символ.
37+
* @param endCharacter Столбец, в котором заканчивается символ.
3438
*/
35-
@Value
36-
@AllArgsConstructor
37-
@Builder
38-
public class Location {
39-
40-
/**
41-
* URI файла, в котором расположен символ.
42-
*/
43-
URI uri;
44-
45-
/**
46-
* Строка, в которой начинается символ.
47-
*/
48-
int startLine;
49-
50-
/**
51-
* Столбец, в котором начинается символ.
52-
*/
53-
int startCharacter;
54-
55-
/**
56-
* Строка, в которой заканчивается символ.
57-
*/
58-
int endLine;
59-
60-
/**
61-
* Столбец, в котором заканчивается символ.
62-
*/
63-
int endCharacter;
39+
public record Location(URI uri, int startLine, int startCharacter, int endLine, int endCharacter) {
6440

6541
public Location(URI uri, Range range) {
66-
this.uri = uri;
67-
var start = range.getStart();
68-
var end = range.getEnd();
69-
startLine = start.getLine();
70-
startCharacter = start.getCharacter();
71-
endLine = end.getLine();
72-
endCharacter = end.getCharacter();
42+
this(uri, range.getStart().getLine(), range.getStart().getCharacter(), range.getEnd().getLine(), range.getEnd().getCharacter());
7343
}
74-
44+
7545
public Range getRange() {
7646
return Ranges.create(startLine, startCharacter, endLine, endCharacter);
7747
}
48+
49+
public Position getStart() {
50+
return new Position(startLine, startCharacter);
51+
}
7852
}

src/main/java/com/github/_1c_syntax/bsl/languageserver/references/model/LocationRepository.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public Stream<SymbolOccurrence> getSymbolOccurrencesByLocationUri(URI uri) {
5656
* @param symbolOccurrence Обращение к символу.
5757
*/
5858
public void updateLocation(SymbolOccurrence symbolOccurrence) {
59-
locations.computeIfAbsent(symbolOccurrence.location().getUri(), uri -> ConcurrentHashMap.newKeySet())
59+
locations.computeIfAbsent(symbolOccurrence.location().uri(), uri -> ConcurrentHashMap.newKeySet())
6060
.add(symbolOccurrence);
6161
}
6262

src/main/java/com/github/_1c_syntax/bsl/languageserver/references/model/SymbolOccurrence.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
import lombok.Builder;
2626
import org.jspecify.annotations.Nullable;
2727

28+
import static java.util.Comparator.comparing;
29+
2830
/**
2931
* Обращение к символу в файле.
3032
*
@@ -45,9 +47,10 @@ public int compareTo(@Nullable SymbolOccurrence other) {
4547
return 1;
4648
}
4749

48-
return java.util.Comparator
49-
.comparing(SymbolOccurrence::location, java.util.Comparator.comparing(Location::getUri)
50-
.thenComparing((l1, l2) -> Ranges.compare(l1.getRange(), l2.getRange())))
50+
return comparing(SymbolOccurrence::location, comparing(Location::uri)
51+
.thenComparing((l1, l2) -> Ranges.compare(
52+
l1.startLine(), l1.startCharacter(), l1.endLine(), l1.endCharacter(),
53+
l2.startLine(), l2.startCharacter(), l2.endLine(), l2.endCharacter())))
5154
.thenComparing(SymbolOccurrence::occurrenceType)
5255
.thenComparing(SymbolOccurrence::symbol)
5356
.compare(this, other);

src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/Ranges.java

Lines changed: 90 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,69 @@ public boolean containsPosition(Range range, Position position) {
213213
&& Positions.isBefore(position, range.getEnd()));
214214
}
215215

216+
/**
217+
* Проверяет, содержит ли диапазон указанную позицию.
218+
*
219+
* @param startLine - начальная строка диапазона
220+
* @param startCharacter - начальный символ диапазона
221+
* @param endLine - конечная строка диапазона
222+
* @param endCharacter - конечный символ диапазона
223+
* @param position - позиция для проверки
224+
* @return true, если позиция находится внутри диапазона (включая начало, исключая конец)
225+
*/
226+
public boolean containsPosition(
227+
int startLine, int startCharacter, int endLine, int endCharacter,
228+
Position position
229+
) {
230+
return containsPosition(startLine, startCharacter, endLine, endCharacter,
231+
position.getLine(), position.getCharacter());
232+
}
233+
234+
/**
235+
* Проверяет, содержит ли диапазон указанную позицию.
236+
*
237+
* @param startLine - начальная строка диапазона
238+
* @param startCharacter - начальный символ диапазона
239+
* @param endLine - конечная строка диапазона
240+
* @param endCharacter - конечный символ диапазона
241+
* @param line - строка позиции
242+
* @param character - символ позиции
243+
* @return true, если позиция находится внутри диапазона (включая начало, исключая конец)
244+
*/
245+
public boolean containsPosition(
246+
int startLine, int startCharacter, int endLine, int endCharacter,
247+
int line, int character
248+
) {
249+
// Позиция равна началу диапазона
250+
if (line == startLine && character == startCharacter) {
251+
return true;
252+
}
253+
// Позиция после начала и до конца
254+
return isBefore(startLine, startCharacter, line, character)
255+
&& isBefore(line, character, endLine, endCharacter);
256+
}
257+
258+
/**
259+
* Проверяет, что первая позиция строго раньше второй.
260+
*
261+
* @param line1 - строка первой позиции
262+
* @param character1 - символ первой позиции
263+
* @param line2 - строка второй позиции
264+
* @param character2 - символ второй позиции
265+
* @return true, если первая позиция строго раньше второй
266+
*/
267+
private boolean isBefore(int line1, int character1, int line2, int character2) {
268+
if (line1 < line2) {
269+
return true;
270+
}
271+
if (line1 > line2) {
272+
return false;
273+
}
274+
return character1 < character2;
275+
}
276+
277+
278+
216279
/**
217280
* Натуральный порядок сравнения Range
218281
*
@@ -257,17 +320,34 @@ public int compare(Position pos1, Position pos2) {
257320
}
258321

259322
/**
260-
* @deprecated Для совместимости метод оставлен, но будет удален в будущих версиях.
261-
* Вместо него стоит использовать метод {@link ModuleSymbol#getSelectionRange()}
323+
* Натуральный порядок сравнения двух диапазонов, заданных деконструированными координатами.
324+
*
325+
* @param startLine1 - начальная строка первого диапазона
326+
* @param startChar1 - начальный символ первого диапазона
327+
* @param endLine1 - конечная строка первого диапазона
328+
* @param endChar1 - конечный символ первого диапазона
329+
* @param startLine2 - начальная строка второго диапазона
330+
* @param startChar2 - начальный символ второго диапазона
331+
* @param endLine2 - конечная строка второго диапазона
332+
* @param endChar2 - конечный символ второго диапазона
333+
* @return 0 - равно, 1 - больше, -1 - меньше
262334
*/
263-
@Deprecated(since = "0.20")
264-
public Optional<Range> getFirstSignificantTokenRange(Collection<Token> tokens) {
265-
return tokens.stream()
266-
.filter(token -> token.getType() != Token.EOF)
267-
.filter(token -> token.getType() != BSLLexer.WHITE_SPACE)
268-
.map(Ranges::create)
269-
.filter(range -> (!range.getStart().equals(range.getEnd())))
270-
.findFirst();
335+
public int compare(
336+
int startLine1, int startChar1, int endLine1, int endChar1,
337+
int startLine2, int startChar2, int endLine2, int endChar2
338+
) {
339+
// Сравнение начальных позиций
340+
if (startLine1 != startLine2) {
341+
return Integer.compare(startLine1, startLine2);
342+
}
343+
if (startChar1 != startChar2) {
344+
return Integer.compare(startChar1, startChar2);
345+
}
346+
// Сравнение конечных позиций
347+
if (endLine1 != endLine2) {
348+
return Integer.compare(endLine1, endLine2);
349+
}
350+
return Integer.compare(endChar1, endChar2);
271351
}
272352

273353
}

src/test/java/com/github/_1c_syntax/bsl/languageserver/utils/RangesTest.java

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
*/
2222
package com.github._1c_syntax.bsl.languageserver.utils;
2323

24+
import org.eclipse.lsp4j.Position;
2425
import org.junit.jupiter.api.Test;
2526

2627
import static org.assertj.core.api.Assertions.assertThat;
@@ -34,4 +35,91 @@ void testIsEmpty() {
3435
assertThat(Ranges.isEmpty(emptyRangeCreate)).isTrue();
3536
assertThat(Ranges.isEmpty(Ranges.create(1, 1, 1, 1))).isFalse();
3637
}
38+
39+
@Test
40+
void testCompareDeconstructedRanges() {
41+
// Равные диапазоны
42+
assertThat(Ranges.compare(0, 0, 1, 1, 0, 0, 1, 1)).isEqualTo(0);
43+
44+
// Первый диапазон меньше по начальной строке
45+
assertThat(Ranges.compare(0, 0, 1, 1, 1, 0, 2, 1)).isEqualTo(-1);
46+
47+
// Первый диапазон больше по начальной строке
48+
assertThat(Ranges.compare(2, 0, 3, 1, 1, 0, 2, 1)).isEqualTo(1);
49+
50+
// Одинаковые начальные строки, первый меньше по начальному символу
51+
assertThat(Ranges.compare(1, 0, 1, 5, 1, 5, 1, 10)).isEqualTo(-1);
52+
53+
// Одинаковые начальные строки, первый больше по начальному символу
54+
assertThat(Ranges.compare(1, 10, 1, 15, 1, 5, 1, 10)).isEqualTo(1);
55+
56+
// Одинаковые начальные позиции, первый меньше по конечной строке
57+
assertThat(Ranges.compare(1, 0, 1, 5, 1, 0, 2, 5)).isEqualTo(-1);
58+
59+
// Одинаковые начальные позиции, первый больше по конечной строке
60+
assertThat(Ranges.compare(1, 0, 3, 5, 1, 0, 2, 5)).isEqualTo(1);
61+
62+
// Одинаковые начальные позиции и конечные строки, первый меньше по конечному символу
63+
assertThat(Ranges.compare(1, 0, 2, 5, 1, 0, 2, 10)).isEqualTo(-1);
64+
65+
// Одинаковые начальные позиции и конечные строки, первый больше по конечному символу
66+
assertThat(Ranges.compare(1, 0, 2, 15, 1, 0, 2, 10)).isEqualTo(1);
67+
}
68+
69+
@Test
70+
void testContainsPositionDeconstructed() {
71+
// Позиция совпадает с началом диапазона
72+
assertThat(Ranges.containsPosition(1, 5, 3, 10, 1, 5)).isTrue();
73+
74+
// Позиция внутри диапазона (на той же строке, что и начало)
75+
assertThat(Ranges.containsPosition(1, 5, 3, 10, 1, 7)).isTrue();
76+
77+
// Позиция внутри диапазона (на средней строке)
78+
assertThat(Ranges.containsPosition(1, 5, 3, 10, 2, 0)).isTrue();
79+
80+
// Позиция внутри диапазона (на конечной строке, но до конечного символа)
81+
assertThat(Ranges.containsPosition(1, 5, 3, 10, 3, 5)).isTrue();
82+
83+
// Позиция совпадает с концом диапазона (конец не включается)
84+
assertThat(Ranges.containsPosition(1, 5, 3, 10, 3, 10)).isFalse();
85+
86+
// Позиция до начала диапазона (на той же строке)
87+
assertThat(Ranges.containsPosition(1, 5, 3, 10, 1, 3)).isFalse();
88+
89+
// Позиция до начала диапазона (на предыдущей строке)
90+
assertThat(Ranges.containsPosition(1, 5, 3, 10, 0, 10)).isFalse();
91+
92+
// Позиция после конца диапазона (на той же строке)
93+
assertThat(Ranges.containsPosition(1, 5, 3, 10, 3, 15)).isFalse();
94+
95+
// Позиция после конца диапазона (на следующей строке)
96+
assertThat(Ranges.containsPosition(1, 5, 3, 10, 4, 0)).isFalse();
97+
98+
// Однострочный диапазон, позиция внутри
99+
assertThat(Ranges.containsPosition(5, 10, 5, 20, 5, 15)).isTrue();
100+
101+
// Однострочный диапазон, позиция равна началу
102+
assertThat(Ranges.containsPosition(5, 10, 5, 20, 5, 10)).isTrue();
103+
104+
// Однострочный диапазон, позиция равна концу
105+
assertThat(Ranges.containsPosition(5, 10, 5, 20, 5, 20)).isFalse();
106+
}
107+
108+
@Test
109+
void testContainsPositionDeconstructedWithPositionObject() {
110+
// Позиция совпадает с началом диапазона
111+
assertThat(Ranges.containsPosition(1, 5, 3, 10, new Position(1, 5))).isTrue();
112+
113+
// Позиция внутри диапазона
114+
assertThat(Ranges.containsPosition(1, 5, 3, 10, new Position(2, 0))).isTrue();
115+
116+
// Позиция совпадает с концом диапазона (конец не включается)
117+
assertThat(Ranges.containsPosition(1, 5, 3, 10, new Position(3, 10))).isFalse();
118+
119+
// Позиция до начала диапазона
120+
assertThat(Ranges.containsPosition(1, 5, 3, 10, new Position(0, 10))).isFalse();
121+
122+
// Позиция после конца диапазона
123+
assertThat(Ranges.containsPosition(1, 5, 3, 10, new Position(4, 0))).isFalse();
124+
}
37125
}

0 commit comments

Comments
 (0)