Skip to content

Commit 6543ad3

Browse files
committed
Merge remote-tracking branch 'origin/main' into feature/132-lsp-call-hierarchy
2 parents b41b413 + 979ba95 commit 6543ad3

File tree

15 files changed

+434
-178
lines changed

15 files changed

+434
-178
lines changed

rascal-lsp/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
3636
<junit.version>4.13.2</junit.version>
3737
<maven-surefire-plugin.version>3.5.3</maven-surefire-plugin.version>
38-
<log4j2.version>2.25.0</log4j2.version>
38+
<log4j2.version>2.25.1</log4j2.version>
3939
<lsp4j.version>0.24.0</lsp4j.version>
4040
<sonar.organization>usethesource</sonar.organization>
4141
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
@@ -331,7 +331,7 @@
331331
<plugin>
332332
<groupId>org.apache.maven.plugins</groupId>
333333
<artifactId>maven-enforcer-plugin</artifactId>
334-
<version>3.5.0</version>
334+
<version>3.6.1</version>
335335
<executions>
336336
<execution>
337337
<id>enforce-maven</id>

rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/parametric/ILanguageContributions.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ public interface ILanguageContributions {
5858
public InterruptibleFuture<ISet> references(IList focus);
5959
public InterruptibleFuture<ISet> implementation(IList focus);
6060
public InterruptibleFuture<IList> codeAction(IList focus);
61+
public InterruptibleFuture<IList> selectionRange(IList focus);
6162

6263
public CompletableFuture<IList> parseCodeActions(String command);
6364

@@ -72,6 +73,7 @@ public interface ILanguageContributions {
7273
public CompletableFuture<Boolean> hasReferences();
7374
public CompletableFuture<Boolean> hasImplementation();
7475
public CompletableFuture<Boolean> hasCodeAction();
76+
public CompletableFuture<Boolean> hasSelectionRange();
7577

7678
public CompletableFuture<Boolean> specialCaseHighlighting();
7779

rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/parametric/InterpretedLanguageContributions.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ public class InterpretedLanguageContributions implements ILanguageContributions
8888
private final CompletableFuture<@Nullable IFunction> references;
8989
private final CompletableFuture<@Nullable IFunction> implementation;
9090
private final CompletableFuture<@Nullable IFunction> codeAction;
91+
private final CompletableFuture<@Nullable IFunction> selectionRange;
9192

9293
private final CompletableFuture<Boolean> hasAnalysis;
9394
private final CompletableFuture<Boolean> hasBuild;
@@ -100,6 +101,7 @@ public class InterpretedLanguageContributions implements ILanguageContributions
100101
private final CompletableFuture<Boolean> hasReferences;
101102
private final CompletableFuture<Boolean> hasImplementation;
102103
private final CompletableFuture<Boolean> hasCodeAction;
104+
private final CompletableFuture<Boolean> hasSelectionRange;
103105

104106
private final CompletableFuture<Boolean> specialCaseHighlighting;
105107

@@ -142,6 +144,7 @@ public InterpretedLanguageContributions(LanguageParameter lang, IBaseTextDocumen
142144
this.references = getFunctionFor(contributions, LanguageContributions.REFERENCES);
143145
this.implementation = getFunctionFor(contributions, LanguageContributions.IMPLEMENTATION);
144146
this.codeAction = getFunctionFor(contributions, LanguageContributions.CODE_ACTION);
147+
this.selectionRange = getFunctionFor(contributions, LanguageContributions.SELECTION_RANGE);
145148

146149
// assign boolean properties once instead of wasting futures all the time
147150
this.hasAnalysis = nonNull(this.analysis);
@@ -155,6 +158,7 @@ public InterpretedLanguageContributions(LanguageParameter lang, IBaseTextDocumen
155158
this.hasReferences = nonNull(this.references);
156159
this.hasImplementation = nonNull(this.implementation);
157160
this.hasCodeAction = nonNull(this.codeAction);
161+
this.hasSelectionRange = nonNull(this.selectionRange);
158162

159163
this.specialCaseHighlighting = getContributionParameter(contributions,
160164
LanguageContributions.PARSING,
@@ -340,6 +344,12 @@ public InterruptibleFuture<IList> codeAction(IList focus) {
340344
return execFunction(LanguageContributions.CODE_ACTION, codeAction, VF.list(), focus);
341345
}
342346

347+
@Override
348+
public InterruptibleFuture<IList> selectionRange(IList focus) {
349+
debug(LanguageContributions.SELECTION_RANGE, focus.length());
350+
return execFunction(LanguageContributions.SELECTION_RANGE, selectionRange, VF.list(), focus);
351+
}
352+
343353
private void debug(String name, Object param) {
344354
logger.debug("{}({})", name, param);
345355
}
@@ -393,6 +403,11 @@ public CompletableFuture<Boolean> hasCodeAction() {
393403
return hasCodeAction;
394404
}
395405

406+
@Override
407+
public CompletableFuture<Boolean> hasSelectionRange() {
408+
return hasSelectionRange;
409+
}
410+
396411
@Override
397412
public CompletableFuture<Boolean> hasAnalysis() {
398413
return hasAnalysis;

rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/parametric/LanguageContributionsMultiplexer.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ private static final <T> CompletableFuture<T> failedInitialization() {
6464
private volatile CompletableFuture<ILanguageContributions> references = failedInitialization();
6565
private volatile CompletableFuture<ILanguageContributions> implementation = failedInitialization();
6666
private volatile CompletableFuture<ILanguageContributions> codeAction = failedInitialization();
67+
private volatile CompletableFuture<ILanguageContributions> selectionRange = failedInitialization();
6768

6869
private volatile CompletableFuture<Boolean> hasAnalysis = failedInitialization();
6970
private volatile CompletableFuture<Boolean> hasBuild = failedInitialization();
@@ -76,6 +77,7 @@ private static final <T> CompletableFuture<T> failedInitialization() {
7677
private volatile CompletableFuture<Boolean> hasReferences = failedInitialization();
7778
private volatile CompletableFuture<Boolean> hasImplementation = failedInitialization();
7879
private volatile CompletableFuture<Boolean> hasCodeAction = failedInitialization();
80+
private volatile CompletableFuture<Boolean> hasSelectionRange = failedInitialization();
7981

8082
private volatile CompletableFuture<Boolean> specialCaseHighlighting = failedInitialization();
8183

@@ -149,6 +151,7 @@ private synchronized void calculateRouting() {
149151
references = findFirstOrDefault(ILanguageContributions::hasReferences);
150152
implementation = findFirstOrDefault(ILanguageContributions::hasImplementation);
151153
codeAction = findFirstOrDefault(ILanguageContributions::hasCodeAction);
154+
selectionRange = findFirstOrDefault(ILanguageContributions::hasSelectionRange);
152155

153156
hasAnalysis = anyTrue(ILanguageContributions::hasAnalysis);
154157
hasBuild = anyTrue(ILanguageContributions::hasBuild);
@@ -160,6 +163,8 @@ private synchronized void calculateRouting() {
160163
hasDefinition = anyTrue(ILanguageContributions::hasDefinition);
161164
hasReferences = anyTrue(ILanguageContributions::hasReferences);
162165
hasImplementation = anyTrue(ILanguageContributions::hasImplementation);
166+
hasCodeAction = anyTrue(ILanguageContributions::hasCodeAction);
167+
hasSelectionRange = anyTrue(ILanguageContributions::hasSelectionRange);
163168

164169
// Always use the special-case highlighting status of *the first*
165170
// contribution (possibly using the default value in the Rascal ADT if
@@ -295,6 +300,16 @@ public InterruptibleFuture<IList> codeAction(IList focus) {
295300
return flatten(codeAction, c -> c.codeAction(focus));
296301
}
297302

303+
@Override
304+
public CompletableFuture<Boolean> hasSelectionRange() {
305+
return hasSelectionRange;
306+
}
307+
308+
@Override
309+
public InterruptibleFuture<IList> selectionRange(IList focus) {
310+
return flatten(selectionRange, c -> c.selectionRange(focus));
311+
}
312+
298313
@Override
299314
public CompletableFuture<Boolean> hasCodeAction() {
300315
return hasCodeAction;

rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/parametric/ParametricTextDocumentService.java

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@
7979
import org.eclipse.lsp4j.LocationLink;
8080
import org.eclipse.lsp4j.Position;
8181
import org.eclipse.lsp4j.ReferenceParams;
82+
import org.eclipse.lsp4j.SelectionRange;
83+
import org.eclipse.lsp4j.SelectionRangeParams;
8284
import org.eclipse.lsp4j.SemanticTokens;
8385
import org.eclipse.lsp4j.SemanticTokensDelta;
8486
import org.eclipse.lsp4j.SemanticTokensDeltaParams;
@@ -112,6 +114,7 @@
112114
import org.rascalmpl.vscode.lsp.util.Diagnostics;
113115
import org.rascalmpl.vscode.lsp.util.DocumentSymbols;
114116
import org.rascalmpl.vscode.lsp.util.FoldingRanges;
117+
import org.rascalmpl.vscode.lsp.util.SelectionRanges;
115118
import org.rascalmpl.vscode.lsp.util.SemanticTokenizer;
116119
import org.rascalmpl.vscode.lsp.util.Versioned;
117120
import org.rascalmpl.vscode.lsp.util.concurrent.InterruptibleFuture;
@@ -199,6 +202,7 @@ public void initializeServerCapabilities(ServerCapabilities result) {
199202
result.setCodeActionProvider(true);
200203
result.setCodeLensProvider(new CodeLensOptions(false));
201204
result.setExecuteCommandProvider(new ExecuteCommandOptions(Collections.singletonList(getRascalMetaCommandName())));
205+
result.setSelectionRangeProvider(true);
202206

203207
result.setFoldingRangeProvider(true);
204208
result.setInlayHintProvider(true);
@@ -476,7 +480,7 @@ public CompletableFuture<SemanticTokens> semanticTokensRange(SemanticTokensRange
476480
}
477481

478482
@Override
479-
public CompletableFuture<List<Either<SymbolInformation, DocumentSymbol>>>documentSymbol(DocumentSymbolParams params) {
483+
public CompletableFuture<List<Either<SymbolInformation, DocumentSymbol>>> documentSymbol(DocumentSymbolParams params) {
480484
logger.debug("Outline/documentSymbol: {}", params.getTextDocument());
481485

482486
final TextDocumentState file = getFile(params.getTextDocument());
@@ -608,6 +612,29 @@ public CompletableFuture<List<CallHierarchyItem>> prepareCallHierarchy(CallHiera
608612
return IBaseTextDocumentService.super.prepareCallHierarchy(params);
609613
}
610614

615+
@Override
616+
public CompletableFuture<List<SelectionRange>> selectionRange(SelectionRangeParams params) {
617+
logger.debug("Selection range: {} at {}", params.getTextDocument(), params.getPositions());
618+
final ILanguageContributions contrib = contributions(params.getTextDocument());
619+
final TextDocumentState file = getFile(params.getTextDocument());
620+
621+
return recoverExceptions(file.getCurrentTreeAsync()
622+
.thenApply(Versioned::get)
623+
.thenCompose(t -> params.getPositions().stream()
624+
.map(p -> Locations.toRascalPosition(params.getTextDocument(), p, columns))
625+
.map(p -> TreeSearch.computeFocusList(t, p.getLine(), p.getCharacter()))
626+
.map(focus -> contrib.hasSelectionRange().thenCompose(hasDef -> hasDef.booleanValue()
627+
? contrib.selectionRange(focus).get()
628+
: CompletableFuture.completedFuture(SelectionRanges.defaultImplementation(focus))))
629+
.map(rangeFut -> rangeFut.thenApply(range -> Collections.singletonList(SelectionRanges.toSelectionRange(range, columns)))) // produce singleton lists here to simplify reduction later
630+
.reduce((lf, rf) -> lf.thenCombine(rf, (l, r) -> {
631+
l.addAll(r);
632+
return l;
633+
}))
634+
.orElse(CompletableFuture.completedFuture(Collections.emptyList()))),
635+
Collections::emptyList);
636+
}
637+
611638
@Override
612639
public synchronized void registerLanguage(LanguageParameter lang) {
613640
logger.info("registerLanguage({})", lang.getName());

rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/parametric/ParserOnlyContribution.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,11 @@ public InterruptibleFuture<ISet> implementation(IList focus) {
176176
return InterruptibleFuture.completedFuture(VF.set());
177177
}
178178

179+
@Override
180+
public InterruptibleFuture<IList> selectionRange(IList focus) {
181+
return InterruptibleFuture.completedFuture(VF.list());
182+
}
183+
179184
@Override
180185
public CompletableFuture<Boolean> hasHover() {
181186
return CompletableFuture.completedFuture(false);
@@ -231,6 +236,11 @@ public CompletableFuture<Boolean> hasInlayHint() {
231236
return CompletableFuture.completedFuture(false);
232237
}
233238

239+
@Override
240+
public CompletableFuture<Boolean> hasSelectionRange() {
241+
return CompletableFuture.completedFuture(false);
242+
}
243+
234244
@Override
235245
public CompletableFuture<Boolean> specialCaseHighlighting() {
236246
return specialCaseHighlighting;

rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/parametric/model/RascalADTs.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ private LanguageContributions () {}
4646
public static final String REFERENCES = "references";
4747
public static final String IMPLEMENTATION = "implementation";
4848
public static final String CODE_ACTION = "codeAction";
49+
public static final String SELECTION_RANGE = "selectionRange";
4950

5051
public static class Parameters {
5152
private Parameters() {}

rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/util/DocumentSymbols.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,19 @@
2929
import java.util.Collections;
3030
import java.util.List;
3131
import java.util.stream.Collectors;
32+
3233
import org.eclipse.lsp4j.DocumentSymbol;
3334
import org.eclipse.lsp4j.Range;
3435
import org.eclipse.lsp4j.SymbolInformation;
3536
import org.eclipse.lsp4j.SymbolKind;
37+
import org.eclipse.lsp4j.SymbolTag;
3638
import org.eclipse.lsp4j.jsonrpc.messages.Either;
3739
import org.rascalmpl.vscode.lsp.util.locations.LineColumnOffsetMap;
3840
import org.rascalmpl.vscode.lsp.util.locations.Locations;
41+
3942
import io.usethesource.vallang.IConstructor;
4043
import io.usethesource.vallang.IList;
44+
import io.usethesource.vallang.ISet;
4145
import io.usethesource.vallang.ISourceLocation;
4246
import io.usethesource.vallang.IString;
4347
import io.usethesource.vallang.IWithKeywordParameters;
@@ -87,7 +91,18 @@ public static DocumentSymbol toLSP(IConstructor symbol, final LineColumnOffsetMa
8791
? Locations.toRange(((ISourceLocation) kwp.getParameter("selection")), om)
8892
: range;
8993
String detail = kwp.hasParameter("detail") ? ((IString) kwp.getParameter("detail")).getValue() : null;
94+
List<SymbolTag> tags = kwp.hasParameter("tags") ?
95+
((ISet) kwp.getParameter("tags"))
96+
.stream()
97+
.map(IConstructor.class::cast)
98+
.map(IConstructor::getName)
99+
.map(DocumentSymbols::capitalize)
100+
.map(SymbolTag::valueOf)
101+
.collect(Collectors.toList())
102+
: Collections.emptyList();
90103

91-
return new DocumentSymbol(symbolName, kind, range, selection, detail, children);
104+
var lspSymbol = new DocumentSymbol(symbolName, kind, range, selection, detail, children);
105+
lspSymbol.setTags(tags); // since 3.16
106+
return lspSymbol;
92107
}
93108
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* Copyright (c) 2018-2025, NWO-I CWI and Swat.engineering
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are met:
7+
*
8+
* 1. Redistributions of source code must retain the above copyright notice,
9+
* this list of conditions and the following disclaimer.
10+
*
11+
* 2. Redistributions in binary form must reproduce the above copyright notice,
12+
* this list of conditions and the following disclaimer in the documentation
13+
* and/or other materials provided with the distribution.
14+
*
15+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
19+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25+
* POSSIBILITY OF SUCH DAMAGE.
26+
*/
27+
package org.rascalmpl.vscode.lsp.util;
28+
29+
import java.util.Iterator;
30+
import java.util.List;
31+
import java.util.NoSuchElementException;
32+
import java.util.Objects;
33+
import java.util.stream.Collectors;
34+
35+
import org.checkerframework.checker.nullness.qual.Nullable;
36+
import org.eclipse.lsp4j.Range;
37+
import org.eclipse.lsp4j.SelectionRange;
38+
import org.rascalmpl.values.IRascalValueFactory;
39+
import org.rascalmpl.values.parsetrees.ITree;
40+
import org.rascalmpl.values.parsetrees.TreeAdapter;
41+
import org.rascalmpl.vscode.lsp.util.locations.ColumnMaps;
42+
import org.rascalmpl.vscode.lsp.util.locations.Locations;
43+
44+
import io.usethesource.vallang.IList;
45+
import io.usethesource.vallang.ISourceLocation;
46+
47+
public class SelectionRanges {
48+
private SelectionRanges() { /* hide implicit constructor */ }
49+
50+
/**
51+
* Folds a {@link IList} of {@link ISourceLocation}s into a single, nested {@link SelectionRange}.
52+
* @param ranges The range hierarchy. Should be ordered child-before-parent, where any source location is contained by the next.
53+
* @param columns The editor's column map.
54+
* @return A range with optional parent ranges.
55+
*/
56+
public static @Nullable SelectionRange toSelectionRange(IList ranges, ColumnMaps columns) {
57+
return toSelectionRange(ranges.stream()
58+
.map(ISourceLocation.class::cast)
59+
.map(l -> Locations.toRange(l, columns))
60+
.collect(Collectors.toList()));
61+
}
62+
63+
/**
64+
* Folds a {@link List} of {@link Range}s into a single, nested {@link SelectionRange}.
65+
* @param ranges The range hierarchy. Should be ordered child-before-parent, where any range is contained by the next.
66+
* @return A range with optional parent ranges
67+
*/
68+
public static @Nullable SelectionRange toSelectionRange(List<Range> ranges) {
69+
// assumes child-before-parent ordering
70+
SelectionRange selectionRange = null;
71+
for (var r : reverse(ranges)) {
72+
selectionRange = new SelectionRange(r, selectionRange);
73+
}
74+
return selectionRange;
75+
}
76+
77+
private static <T> Iterable<T> reverse(List<T> list) {
78+
return () -> new Iterator<T>() {
79+
int current = list.size() - 1;
80+
81+
@Override
82+
public boolean hasNext() {
83+
return current >= 0;
84+
}
85+
86+
@Override
87+
public T next() {
88+
try {
89+
return list.get(current--);
90+
} catch (IndexOutOfBoundsException e) {
91+
throw new NoSuchElementException();
92+
}
93+
}
94+
};
95+
}
96+
97+
public static IList defaultImplementation(IList focus) {
98+
return focus.stream()
99+
.filter(Objects::nonNull)
100+
.filter(ITree.class::isInstance)
101+
.map(ITree.class::cast)
102+
.map(TreeAdapter::getLocation)
103+
.distinct()
104+
.collect(IRascalValueFactory.getInstance().listWriter());
105+
}
106+
}

0 commit comments

Comments
 (0)