Skip to content

Commit ea10b50

Browse files
authored
Path config-less summaries for stdlib (#1053)
* Path config-less summaries for stdlib. * Support `mvn` scheme for libraries. * Fix log call arguments. * Improve function name. * Rewrite without `binFile`. * Test & rewrite library TPL computation. * Refactor JAR path splitting. * Add nullable annotation. * Remove throws declarations. * Generalize jar+ scheme.
1 parent fb1549a commit ea10b50

File tree

3 files changed

+174
-10
lines changed

3 files changed

+174
-10
lines changed

rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/rascal/RascalLanguageServices.java

Lines changed: 86 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
import java.io.IOException;
3333
import java.io.StringReader;
34+
import java.net.URISyntaxException;
3435
import java.util.ArrayList;
3536
import java.util.Arrays;
3637
import java.util.List;
@@ -40,6 +41,7 @@
4041
import java.util.concurrent.ExecutionException;
4142
import java.util.concurrent.Executor;
4243
import java.util.concurrent.ExecutorService;
44+
import java.util.function.Function;
4345
import java.util.stream.Collectors;
4446
import org.apache.logging.log4j.LogManager;
4547
import org.apache.logging.log4j.Logger;
@@ -130,17 +132,92 @@ public RascalLanguageServices(RascalTextDocumentService docService, BaseWorkspac
130132
this.workspaceService = workspaceService;
131133
}
132134

133-
public InterruptibleFuture<@Nullable IConstructor> getSummary(ISourceLocation occ, PathConfig pcfg) {
135+
static String pathToModuleName(ISourceLocation l) {
136+
var p = l.getPath();
137+
if (isInsideJar(l)) {
138+
p = jarFilePath(l);
139+
}
140+
return p.substring(1, p.lastIndexOf('.')).replace("/", "::");
141+
}
142+
143+
private static boolean isInsideJar(ISourceLocation l) {
144+
return l.getScheme().startsWith("jar+");
145+
}
146+
147+
static @Nullable ISourceLocation libraryTplLocation(ISourceLocation modPath) {
134148
try {
135-
IString moduleName = VF.string(pcfg.getModuleName(occ));
136-
return runEvaluator("Rascal makeSummary", semanticEvaluator, eval -> {
137-
IConstructor result = (IConstructor) eval.call("makeSummary", moduleName, pcfg.asConstructor());
138-
return result != null && result.asWithKeywordParameters().hasParameters() ? result : null;
139-
}, null, exec, false, client);
140-
} catch (IOException e) {
141-
logger.error("Error looking up module name from source location {}", occ, e);
142-
return InterruptibleFuture.completedFuture(null, exec);
149+
var tplFolder = libraryTplRoot(modPath);
150+
if (tplFolder != null) {
151+
var modPrefix = libraryModulePrefix(modPath);
152+
var tplFileName = "$" + URIUtil.getLocationName(URIUtil.changeExtension(modPath, "tpl"));
153+
return URIUtil.getChildLocation(URIUtil.getChildLocation(tplFolder, modPrefix), tplFileName);
154+
}
155+
} catch (URISyntaxException e) {
156+
logger.error("Error while finding TPL for {}", modPath, e);
143157
}
158+
return null;
159+
}
160+
161+
private static @Nullable ISourceLocation libraryTplRoot(ISourceLocation modPath) throws URISyntaxException {
162+
modPath = Locations.toPhysicalIfPossible(modPath); // resolve logical paths like `std:///`
163+
if (isInsideJar(modPath)) {
164+
return URIUtil.getChildLocation(jarBasePath(modPath), "rascal");
165+
} else if ("mvn".equals(modPath.getScheme())) {
166+
return URIUtil.changePath(modPath, "rascal");
167+
}
168+
return null;
169+
}
170+
171+
private static String libraryModulePrefix(ISourceLocation modPath) {
172+
modPath = URIUtil.getParentLocation(modPath);
173+
if (isInsideJar(modPath)) {
174+
// For a file within a JAR, return the sub-path within the JAR
175+
return jarFilePath(modPath);
176+
}
177+
// Otherwise, return just the sub-path
178+
return modPath.getPath();
179+
}
180+
181+
private static ISourceLocation jarBasePath(ISourceLocation l) throws URISyntaxException {
182+
if (!isInsideJar(l)) {
183+
throw new IllegalArgumentException("Location should have scheme jar+...: " + l);
184+
}
185+
186+
var path = l.getPath();
187+
return URIUtil.changePath(l, path.substring(0, path.lastIndexOf('!') + 1));
188+
}
189+
190+
private static String jarFilePath(ISourceLocation l) {
191+
if (!isInsideJar(l)) {
192+
throw new IllegalArgumentException("Location should have scheme jar+...: " + l);
193+
}
194+
195+
var path = l.getPath();
196+
return path.substring(path.lastIndexOf('!') + 1);
197+
}
198+
199+
public InterruptibleFuture<@Nullable IConstructor> getSummary(ISourceLocation occ, Function<ISourceLocation, PathConfig> computePathConfig) {
200+
Function<Evaluator, @Nullable IConstructor> computeSummary;
201+
var tplLoc = libraryTplLocation(occ);
202+
if (tplLoc != null) {
203+
computeSummary = eval -> (IConstructor) eval.call("makeSummary", VF.string(pathToModuleName(occ)), tplLoc);
204+
} else {
205+
computeSummary = eval -> {
206+
try {
207+
var pcfg = computePathConfig.apply(occ);
208+
var moduleName = VF.string(pcfg.getModuleName(occ));
209+
return (IConstructor) eval.call("makeSummary", moduleName, pcfg.asConstructor());
210+
} catch (IOException e) {
211+
logger.error("Error looking up module name for source location {}", occ, e);
212+
return null;
213+
}
214+
};
215+
}
216+
217+
return runEvaluator("Rascal makeSummary", semanticEvaluator, eval -> {
218+
var result = computeSummary.apply(eval);
219+
return result != null && result.asWithKeywordParameters().hasParameters() ? result : null;
220+
}, null, exec, false, client);
144221
}
145222

146223
private static Map<ISourceLocation, ISet> translateCheckResults(IMap messages) {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ public ActualFileFact(ISourceLocation file, Executor exec) {
159159
// only run get summary after the typechecker for this file is done running, because it needs the TPL
160160
// (we cannot now global running type checkers, that is a different subject)
161161
return InterruptibleFuture.flatten(typeCheckResults.get()
162-
.<InterruptibleFuture<@Nullable IConstructor>>thenApply(o -> rascal.getSummary(file, confs.lookupConfig(file))), exec)
162+
.<InterruptibleFuture<@Nullable IConstructor>>thenApply(o -> rascal.getSummary(file, confs::lookupConfig)), exec)
163163
.<@Nullable SummaryBridge>thenApply(s -> s == null ? null : new SummaryBridge(file, s, cm));
164164
});
165165
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
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.rascal;
28+
29+
import static org.junit.Assert.assertEquals;
30+
import static org.junit.Assert.assertTrue;
31+
32+
import java.net.URISyntaxException;
33+
import org.junit.Test;
34+
import org.rascalmpl.uri.URIUtil;
35+
import org.rascalmpl.values.IRascalValueFactory;
36+
37+
import io.usethesource.vallang.ISourceLocation;
38+
39+
public class RascalLanguageServicesTest {
40+
41+
private static final IRascalValueFactory VF = IRascalValueFactory.getInstance();
42+
43+
private void moduleNameTest(ISourceLocation sourcePath, String modPath, String modName) throws URISyntaxException {
44+
var actualName = RascalLanguageServices.pathToModuleName(URIUtil.getChildLocation(sourcePath, modPath));
45+
assertEquals(modName, actualName);
46+
}
47+
48+
@Test
49+
public void stdModuleName() throws URISyntaxException {
50+
moduleNameTest(URIUtil.rootLocation("std"), "IO.rsc", "IO");
51+
moduleNameTest(URIUtil.rootLocation("std"), "util/Maybe.rsc", "util::Maybe");
52+
}
53+
54+
@Test
55+
public void mvnModuleName() throws URISyntaxException {
56+
moduleNameTest(VF.sourceLocation("mvn", "org.rascalmpl--rascal--0.42.2", ""), "String.rsc", "String");
57+
moduleNameTest(VF.sourceLocation("mvn", "org.rascalmpl--typepal--0.16.6", ""), "analysis/typepal/Collector.rsc", "analysis::typepal::Collector");
58+
}
59+
60+
@Test
61+
public void jarModuleName() throws URISyntaxException {
62+
moduleNameTest(VF.sourceLocation("jar+file", "", "rascal-lsp.jar!/"), "util/LanguageServer.rsc", "util::LanguageServer");
63+
}
64+
65+
@Test
66+
public void stdTplLoc() throws URISyntaxException {
67+
var src = VF.sourceLocation("std", "", "util/Maybe.rsc");
68+
var actualTpl = RascalLanguageServices.libraryTplLocation(src);
69+
assertEquals("jar+file", actualTpl.getScheme());
70+
assertTrue(actualTpl.getPath().endsWith(".jar!/rascal/util/$Maybe.tpl"));
71+
}
72+
73+
@Test
74+
public void mvnTplLoc() throws URISyntaxException {
75+
var src = VF.sourceLocation("mvn", "org.rascalmpl--typepal--0.16.6", "analysis/typepal/Collector.rsc");
76+
var actualTpl = RascalLanguageServices.libraryTplLocation(src);
77+
assertEquals(VF.sourceLocation("mvn", "org.rascalmpl--typepal--0.16.6", "rascal/analysis/typepal/$Collector.tpl"), actualTpl);
78+
}
79+
80+
@Test
81+
public void jarFileTplLoc() throws URISyntaxException {
82+
var src = VF.sourceLocation("jar+file", "", "some/path/to/rascal-lsp.jar!/util/LanguageServer.rsc");
83+
var actualTpl = RascalLanguageServices.libraryTplLocation(src);
84+
assertEquals(VF.sourceLocation("jar+file", "", "some/path/to/rascal-lsp.jar!/rascal/util/$LanguageServer.tpl"), actualTpl);
85+
}
86+
87+
}

0 commit comments

Comments
 (0)