Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
806d89c
Add call hierarchy LSP API.
toinehartman Jul 29, 2025
907f36a
Update call hierarchy API + example.
toinehartman Jul 29, 2025
d992f3b
Extend incoming/outgoing call signatures to carry more information.
toinehartman Jul 31, 2025
7759d83
Name relation fields.
toinehartman Jul 31, 2025
62a7172
Prevent type-check by using pre-computed summary.
toinehartman Aug 4, 2025
ce663e1
Group & simplify callhierarchy APIs.
toinehartman Aug 14, 2025
f039091
Rename call item constructor, remove default, document fields.
toinehartman Aug 14, 2025
77c8128
Return ordered hierarchy items and leave parsing to implementer.
toinehartman Aug 14, 2025
4ee3719
Document call hierarchy service.
toinehartman Aug 15, 2025
0e0e811
Implement parametric call hierarchies.
toinehartman Aug 26, 2025
1066f04
Change constructor name.
toinehartman Aug 27, 2025
4577d67
Generate source locations from ranges.
toinehartman Aug 29, 2025
e5a1c8a
Document call hierarchy ADT.
toinehartman Sep 1, 2025
298b108
Remove stale pico examples.
toinehartman Sep 2, 2025
bf0f990
Improved Rascal <-> LSP mapping.
toinehartman Oct 1, 2025
fa5d3ec
Data as ADT, serialize as string.
toinehartman Oct 1, 2025
0b587cc
Use list instead of relation.
toinehartman Oct 1, 2025
b8d687d
Recover exceptions.
toinehartman Oct 1, 2025
d55e1d0
Improve getStore.
toinehartman Oct 1, 2025
d0bf2bc
Small fixes.
toinehartman Oct 1, 2025
5c0a0b5
Merge remote-tracking branch 'origin/main' into feature/132-lsp-call-…
toinehartman Oct 1, 2025
9a09c2b
Merge remote-tracking branch 'origin/main' into feature/132-lsp-call-…
toinehartman Oct 13, 2025
025dd33
Always pass the store from the correct evaluator.
toinehartman Oct 13, 2025
fd7fb98
Convert missing data correctly.
toinehartman Oct 13, 2025
a2cefac
Convert text correctly.
toinehartman Oct 13, 2025
82d1af3
Merge remote-tracking branch 'origin/main' into feature/132-lsp-call-…
toinehartman Oct 22, 2025
78c5a10
Only delegate data parsing to specific store.
toinehartman Oct 29, 2025
b6fe2f4
Inlining.
toinehartman Oct 29, 2025
b4a42e7
Move field names to common database.
toinehartman Oct 29, 2025
e1b4405
Document call hierarchy conversion.
toinehartman Oct 29, 2025
ef38ef2
Merge remote-tracking branch 'origin/main' into feature/132-lsp-call-…
toinehartman Oct 30, 2025
7731e1c
Add new interface methods.
toinehartman Oct 30, 2025
22d9217
Use equals.
toinehartman Oct 30, 2025
f37eefb
Add roundtrip test for source location conversions.
toinehartman Oct 30, 2025
d99030a
Add tests for several edge cases.
toinehartman Oct 30, 2025
b87e508
Merge remote-tracking branch 'origin/main' into feature/132-lsp-call-…
toinehartman Nov 3, 2025
8757822
Use column maps from Rascal.
toinehartman Nov 3, 2025
3f3bf61
Add pico call hierarchy example.
toinehartman Nov 3, 2025
84c640e
Simplify & fix warnings.
toinehartman Nov 4, 2025
61320c4
Fix incoming calls.
toinehartman Nov 6, 2025
7ba00c1
Fix and reuse typeOf.
toinehartman Nov 6, 2025
16cceef
Use id only as call site.
toinehartman Nov 6, 2025
4aad0e8
Fix nulls (h/t @DavyLandman).
toinehartman Nov 10, 2025
c0e5e8e
Naming.
toinehartman Nov 10, 2025
3654e18
Clarifications.
toinehartman Nov 10, 2025
d7a1727
Functions in outline.
toinehartman Nov 10, 2025
2672f71
Use wildcards for unused vars.
toinehartman Nov 10, 2025
488305b
Add changelog entry for call hierarchies.
toinehartman Nov 11, 2025
9d7cc51
Rewrite call hierarchies using summary.
toinehartman Nov 11, 2025
7f4ec50
Merge remote-tracking branch 'origin/main' into feature/132-lsp-call-…
toinehartman Nov 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.rascalmpl.values.IRascalValueFactory;
import org.rascalmpl.values.parsetrees.ITree;
import org.rascalmpl.vscode.lsp.util.concurrent.InterruptibleFuture;

import io.usethesource.vallang.IConstructor;
import io.usethesource.vallang.IList;
import io.usethesource.vallang.ISet;
Expand All @@ -58,12 +59,15 @@ public interface ILanguageContributions {
public InterruptibleFuture<ISet> implementation(IList focus);
public InterruptibleFuture<IList> codeAction(IList focus);
public InterruptibleFuture<IList> selectionRange(IList focus);
public InterruptibleFuture<IList> prepareCallHierarchy(IList focus);
public InterruptibleFuture<IList> incomingOutgoingCalls(IConstructor hierarchyItem, IConstructor direction);

public InterruptibleFuture<ISourceLocation> prepareRename(IList focus);
public InterruptibleFuture<ITuple> rename(IList focus, String name);
public InterruptibleFuture<ITuple> didRenameFiles(IList fileRenames);

public CompletableFuture<IList> parseCodeActions(String command);
public CompletableFuture<IConstructor> parseCallHierarchyData(String data);

public CompletableFuture<Boolean> hasAnalysis();
public CompletableFuture<Boolean> hasBuild();
Expand All @@ -79,6 +83,7 @@ public interface ILanguageContributions {
public CompletableFuture<Boolean> hasCodeAction();
public CompletableFuture<Boolean> hasDidRenameFiles();
public CompletableFuture<Boolean> hasSelectionRange();
public CompletableFuture<Boolean> hasCallHierarchy();

public CompletableFuture<Boolean> specialCaseHighlighting();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@
import java.util.concurrent.ExecutorService;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.rascalmpl.interpreter.Evaluator;
import org.rascalmpl.interpreter.env.ModuleEnvironment;
import org.rascalmpl.library.util.PathConfig;
Expand All @@ -53,6 +53,7 @@
import org.rascalmpl.vscode.lsp.util.EvaluatorUtil;
import org.rascalmpl.vscode.lsp.util.EvaluatorUtil.LSPContext;
import org.rascalmpl.vscode.lsp.util.concurrent.InterruptibleFuture;

import io.usethesource.vallang.IBool;
import io.usethesource.vallang.IConstructor;
import io.usethesource.vallang.IList;
Expand Down Expand Up @@ -94,6 +95,8 @@ public class InterpretedLanguageContributions implements ILanguageContributions
private final CompletableFuture<@Nullable IFunction> rename;
private final CompletableFuture<@Nullable IFunction> didRenameFiles;
private final CompletableFuture<@Nullable IFunction> selectionRange;
private final CompletableFuture<@Nullable IFunction> prepareCallHierarchy;
private final CompletableFuture<@Nullable IFunction> callHierarchyService;

private final CompletableFuture<Boolean> hasAnalysis;
private final CompletableFuture<Boolean> hasBuild;
Expand All @@ -109,6 +112,7 @@ public class InterpretedLanguageContributions implements ILanguageContributions
private final CompletableFuture<Boolean> hasRename;
private final CompletableFuture<Boolean> hasDidRenameFiles;
private final CompletableFuture<Boolean> hasSelectionRange;
private final CompletableFuture<Boolean> hasCallHierarchy;

private final CompletableFuture<Boolean> specialCaseHighlighting;

Expand Down Expand Up @@ -155,6 +159,8 @@ public InterpretedLanguageContributions(LanguageParameter lang, IBaseTextDocumen
this.rename = getFunctionFor(contributions, LanguageContributions.RENAME);
this.didRenameFiles = getFunctionFor(contributions, LanguageContributions.DID_RENAME_FILES);
this.selectionRange = getFunctionFor(contributions, LanguageContributions.SELECTION_RANGE);
this.prepareCallHierarchy = getFunctionFor(contributions, LanguageContributions.CALL_HIERARCHY, 0);
this.callHierarchyService = getFunctionFor(contributions, LanguageContributions.CALL_HIERARCHY, 1);

// assign boolean properties once instead of wasting futures all the time
this.hasAnalysis = nonNull(this.analysis);
Expand All @@ -171,6 +177,7 @@ public InterpretedLanguageContributions(LanguageParameter lang, IBaseTextDocumen
this.hasRename = nonNull(this.rename);
this.hasDidRenameFiles = nonNull(this.didRenameFiles);
this.hasSelectionRange = nonNull(this.selectionRange);
this.hasCallHierarchy = nonNull(this.prepareCallHierarchy);

this.specialCaseHighlighting = getContributionParameter(contributions,
LanguageContributions.PARSING,
Expand Down Expand Up @@ -263,6 +270,31 @@ public CompletableFuture<IList> parseCodeActions(String command) {
});
}

@Override
public CompletableFuture<IConstructor> parseCallHierarchyData(String data) {
return store.thenApply(completionStore -> {
try {
var callHierarchyDataAdt = completionStore.lookupAbstractDataType("CallHierarchyData");
if (callHierarchyDataAdt == null) {
throw new IllegalArgumentException("CallHierarchyData is not defined in environment");
}
if (data.isEmpty()) {
var none = completionStore.lookupConstructor(callHierarchyDataAdt, "none", TypeFactory.getInstance().tupleEmpty());
if (none == null) {
throw new IllegalArgumentException("CallHierarchyData::none() is not defined in environment");
}
return VF.constructor(none);
}
return (IConstructor) new StandardTextReader().read(VF, completionStore, callHierarchyDataAdt, new StringReader(data));
} catch (FactTypeUseException | IOException e) {
// this should never happen as long as the Rascal code
// for creating errors is type-correct. So it _might_ happen
// when running the interpreter on broken code.
throw new IllegalArgumentException("The call hierarchy item data could not be parsed", e);
}
});
}

private CompletableFuture<IConstructor> parseCommand(String command) {
return store.thenApply(commandStore -> {
try {
Expand Down Expand Up @@ -301,7 +333,11 @@ private static CompletableFuture<IFunction> requireFunction(CompletableFuture<IS
}

private static CompletableFuture<@Nullable IFunction> getFunctionFor(CompletableFuture<ISet> contributions, String cons) {
return getContribution(contributions, cons).thenApply(contribution -> contribution != null ? (IFunction) contribution.get(0) : null);
return getFunctionFor(contributions, cons, 0);
}

private static CompletableFuture<@Nullable IFunction> getFunctionFor(CompletableFuture<ISet> contributions, String cons, int argumentPos) {
return getContribution(contributions, cons).thenApply(contribution -> contribution != null ? (IFunction) contribution.get(argumentPos) : null);
}

private static CompletableFuture<@Nullable IFunction> getKeywordParamFunctionFor(CompletableFuture<ISet> contributions, String cons, String kwParam) {
Expand Down Expand Up @@ -405,6 +441,18 @@ public InterruptibleFuture<IList> selectionRange(IList focus) {
return execFunction(LanguageContributions.SELECTION_RANGE, selectionRange, VF.list(), focus);
}

@Override
public InterruptibleFuture<IList> prepareCallHierarchy(IList focus) {
debug(LanguageContributions.CALL_HIERARCHY, "prepare", focus.length());
return execFunction(LanguageContributions.CALL_HIERARCHY, prepareCallHierarchy, VF.list(), focus);
}

@Override
public InterruptibleFuture<IList> incomingOutgoingCalls(IConstructor hierarchyItem, IConstructor direction) {
debug(LanguageContributions.CALL_HIERARCHY, hierarchyItem.has("name") ? hierarchyItem.get("name") : "?", direction.getName());
return execFunction(LanguageContributions.CALL_HIERARCHY, callHierarchyService, VF.list(), hierarchyItem, direction);
}

private void debug(String name, Object param) {
logger.debug("{}({})", name, param);
}
Expand Down Expand Up @@ -473,6 +521,11 @@ public CompletableFuture<Boolean> hasSelectionRange() {
return hasSelectionRange;
}

@Override
public CompletableFuture<Boolean> hasCallHierarchy() {
return hasCallHierarchy;
}

@Override
public CompletableFuture<Boolean> hasAnalysis() {
return hasAnalysis;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.checkerframework.checker.nullness.qual.Nullable;
import org.rascalmpl.values.parsetrees.ITree;
import org.rascalmpl.vscode.lsp.util.concurrent.InterruptibleFuture;

import io.usethesource.vallang.IConstructor;
import io.usethesource.vallang.IList;
import io.usethesource.vallang.ISet;
Expand Down Expand Up @@ -69,6 +70,8 @@ private static final <T> CompletableFuture<T> failedInitialization() {
private volatile CompletableFuture<ILanguageContributions> rename = failedInitialization();
private volatile CompletableFuture<ILanguageContributions> didRenameFiles = failedInitialization();
private volatile CompletableFuture<ILanguageContributions> selectionRange = failedInitialization();
private volatile CompletableFuture<ILanguageContributions> prepareCallHierarchy = failedInitialization();
private volatile CompletableFuture<ILanguageContributions> incomingOutgoingCalls = failedInitialization();

private volatile CompletableFuture<Boolean> hasAnalysis = failedInitialization();
private volatile CompletableFuture<Boolean> hasBuild = failedInitialization();
Expand All @@ -84,6 +87,7 @@ private static final <T> CompletableFuture<T> failedInitialization() {
private volatile CompletableFuture<Boolean> hasRename = failedInitialization();
private volatile CompletableFuture<Boolean> hasDidRenameFiles = failedInitialization();
private volatile CompletableFuture<Boolean> hasSelectionRange = failedInitialization();
private volatile CompletableFuture<Boolean> hasCallHierarchy = failedInitialization();

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

Expand Down Expand Up @@ -161,6 +165,8 @@ private synchronized void calculateRouting() {
prepareRename = findFirstOrDefault(ILanguageContributions::hasRename);
didRenameFiles = findFirstOrDefault(ILanguageContributions::hasDidRenameFiles);
selectionRange = findFirstOrDefault(ILanguageContributions::hasSelectionRange);
prepareCallHierarchy = findFirstOrDefault(ILanguageContributions::hasCallHierarchy);
incomingOutgoingCalls = findFirstOrDefault(ILanguageContributions::hasCallHierarchy);

hasAnalysis = anyTrue(ILanguageContributions::hasAnalysis);
hasBuild = anyTrue(ILanguageContributions::hasBuild);
Expand All @@ -172,10 +178,11 @@ private synchronized void calculateRouting() {
hasDefinition = anyTrue(ILanguageContributions::hasDefinition);
hasReferences = anyTrue(ILanguageContributions::hasReferences);
hasImplementation = anyTrue(ILanguageContributions::hasImplementation);
hasCodeAction = anyTrue(ILanguageContributions::hasCodeAction);
hasRename = anyTrue(ILanguageContributions::hasRename);
hasDidRenameFiles = anyTrue(ILanguageContributions::hasDidRenameFiles);
hasCodeAction = anyTrue(ILanguageContributions::hasCodeAction);
hasSelectionRange = anyTrue(ILanguageContributions::hasSelectionRange);
hasCallHierarchy = anyTrue(ILanguageContributions::hasCallHierarchy);

// Always use the special-case highlighting status of *the first*
// contribution (possibly using the default value in the Rascal ADT if
Expand Down Expand Up @@ -281,6 +288,11 @@ public CompletableFuture<IList> parseCodeActions(String command) {
return execution.thenApply(c -> c.parseCodeActions(command)).thenCompose(Function.identity());
}

@Override
public CompletableFuture<IConstructor> parseCallHierarchyData(String data) {
return incomingOutgoingCalls.thenApply(c -> c.parseCallHierarchyData(data)).thenCompose(Function.identity());
}

@Override
public InterruptibleFuture<IList> inlayHint(ITree input) {
return flatten(inlayHint, c -> c.inlayHint(input));
Expand Down Expand Up @@ -336,6 +348,16 @@ public InterruptibleFuture<IList> selectionRange(IList focus) {
return flatten(selectionRange, c -> c.selectionRange(focus));
}

@Override
public InterruptibleFuture<IList> prepareCallHierarchy(IList focus) {
return flatten(prepareCallHierarchy, c -> c.prepareCallHierarchy(focus));
}

@Override
public InterruptibleFuture<IList> incomingOutgoingCalls(IConstructor hierarchyItem, IConstructor direction) {
return flatten(incomingOutgoingCalls, c -> c.incomingOutgoingCalls(hierarchyItem, direction));
}

@Override
public CompletableFuture<Boolean> hasCodeAction() {
return hasCodeAction;
Expand Down Expand Up @@ -401,6 +423,11 @@ public CompletableFuture<Boolean> hasDidRenameFiles() {
return hasDidRenameFiles;
}

@Override
public CompletableFuture<Boolean> hasCallHierarchy() {
return hasCallHierarchy;
}

@Override
public CompletableFuture<Boolean> specialCaseHighlighting() {
return specialCaseHighlighting;
Expand All @@ -425,4 +452,5 @@ public CompletableFuture<SummaryConfig> getOndemandSummaryConfig() {
public void cancelProgress(String progressId) {
contributions.forEach(klc -> klc.contrib.cancelProgress(progressId));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -145,11 +145,26 @@ public InterruptibleFuture<ITuple> didRenameFiles(IList fileRenames) {
throw new NoContributionException("didRenameFiles");
}

@Override
public InterruptibleFuture<IList> prepareCallHierarchy(IList focus) {
throw new NoContributionException("prepareCallHierarchy");
}

@Override
public InterruptibleFuture<IList> incomingOutgoingCalls(IConstructor hierarchyItem, IConstructor direction) {
throw new NoContributionException("incomingOutgoingCalls");
}

@Override
public CompletableFuture<IList> parseCodeActions(String command) {
throw new NoContributionException("parseCodeActions");
}

@Override
public CompletableFuture<IConstructor> parseCallHierarchyData(String data) {
throw new NoContributionException("parseCallHierarchyData");
}

@Override
public CompletableFuture<Boolean> hasAnalysis() {
return FALSE;
Expand Down Expand Up @@ -220,6 +235,11 @@ public CompletableFuture<Boolean> hasSelectionRange() {
return FALSE;
}

@Override
public CompletableFuture<Boolean> hasCallHierarchy() {
return FALSE;
}

@Override
public CompletableFuture<Boolean> specialCaseHighlighting() {
return FALSE;
Expand Down
Loading
Loading