Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
40 changes: 40 additions & 0 deletions src/org/rascalmpl/library/Messages.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

Expand Down Expand Up @@ -43,6 +44,33 @@ public class Messages {
private static final io.usethesource.vallang.type.Type Message_warning = tf.constructor(ts, Message, "warning", tf.stringType(), "msg", tf.sourceLocationType(), "at");
private static final io.usethesource.vallang.type.Type Message_error = tf.constructor(ts, Message, "error", tf.stringType(), "msg", tf.sourceLocationType(), "at");

// These declarations mirror the data definitions in the `util::IDEServices` module of the standard library
private static final io.usethesource.vallang.type.Type CodeAction = tf.abstractDataType(ts, "CodeAction");
public static final io.usethesource.vallang.type.Type CodeAction_action = tf.constructor(ts, CodeAction, "action");

public static final io.usethesource.vallang.type.Type Command = tf.abstractDataType(ts, "Command");
private static final io.usethesource.vallang.type.Type Command_noop = tf.constructor(ts, Command, "noop");

// These declarations mirror the data definition in the `analysis::diff::edits::FileSystemChanges` module of the standard library
private static final io.usethesource.vallang.type.Type FileSystemChange = tf.abstractDataType(ts, "FileSystemChange");
private static final io.usethesource.vallang.type.Type FileSystemChange_removed = tf.constructor(ts, FileSystemChange, "removed", tf.sourceLocationType(), "file");
private static final io.usethesource.vallang.type.Type FileSystemChange_created = tf.constructor(ts, FileSystemChange, "created", tf.sourceLocationType(), "file");
private static final io.usethesource.vallang.type.Type FileSystemChange_renamed = tf.constructor(ts, FileSystemChange, "renamed", tf.sourceLocationType(), "from", tf.sourceLocationType(), "to");
private static final io.usethesource.vallang.type.Type FileSystemChange_changed = tf.constructor(ts, FileSystemChange, "changed", tf.sourceLocationType(), "file");

static {
// data Message(list[CodeAction] fixes = []]);
ts.declareKeywordParameter(Message, "fixes", tf.listType(CodeAction));

// data CodeAction(list[FileSystemChange] edits = [], Command command = noop(), str title = command.title);
ts.declareKeywordParameter(CodeAction, "edits", tf.listType(FileSystemChange));
ts.declareKeywordParameter(CodeAction, "command", Command);
ts.declareKeywordParameter(CodeAction, "title", tf.stringType());

// data Command(str title="");
ts.declareKeywordParameter(CodeAction, "title", tf.stringType());
}

public static IConstructor info(String message, ISourceLocation loc) {
return message(Message_info, message, loc);
}
Expand Down Expand Up @@ -73,6 +101,18 @@ public static IConstructor addCause(IConstructor msg, String cause, ISourceLocat
return msg.asWithKeywordParameters().setParameter("causes", causes);
}

public static IConstructor addFix(IConstructor msg, IConstructor fix) {
if (fix == null) {
return msg;
}

var kw = msg.asWithKeywordParameters();
IList fixes = (IList) kw.getParameter("fixes");
fixes = fixes == null ? vf.list(fix) : fixes.append(fix);

return kw.setParameter("fixes", fixes);
}

public static boolean isError(IValue v) {
return v instanceof IConstructor && ((IConstructor) v).getConstructorType() == Message_error;
}
Expand Down
26 changes: 23 additions & 3 deletions src/org/rascalmpl/library/util/PathConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,7 @@ private static void buildRascalSelfApplicationConfig(ISourceLocation workspaceRa
* Configure paths for the rascal-lsp project when it's open in the IDE (the runtime inside rascal-lsp is not configured here)
*/
private static void buildRascalLSPConfig(ISourceLocation manifestRoot, RascalConfigMode mode, List<Artifact> mavenClasspath, IListWriter srcs, IListWriter libs, IListWriter messages) throws IOException {
var rascalRuntime = getRelevantRascal(mavenClasspath, mode);
var rascalRuntime = getRelevantRascal(mavenClasspath, mode, manifestRoot, messages);
var insideRascalJar = JarURIResolver.jarify(MavenRepositoryURIResolver.mavenize(rascalRuntime));
var rascalLibrary = URIUtil.getChildLocation(insideRascalJar, "org/rascalmpl/library");
var rascalCompiler = URIUtil.getChildLocation(insideRascalJar, "org/rascalmpl/compiler");
Expand Down Expand Up @@ -576,7 +576,7 @@ public static boolean isRascalArtifact(Artifact art) {
&& coord.getGroupId().equals("org.rascalmpl");
}

private static ISourceLocation getRelevantRascal(List<Artifact> mavenClasspath, RascalConfigMode mode) throws IOException {
private static ISourceLocation getRelevantRascal(List<Artifact> mavenClasspath, RascalConfigMode mode, ISourceLocation manifestRoot, IListWriter messages) throws IOException {
if (mode == RascalConfigMode.INTERPRETER_EXTERNAL || mode == RascalConfigMode.COMPILER) {
var rascalFromPom = mavenClasspath.stream()
.filter(PathConfig::isRascalArtifact)
Expand All @@ -586,14 +586,34 @@ private static ISourceLocation getRelevantRascal(List<Artifact> mavenClasspath,
.map(vf::sourceLocation);
if (rascalFromPom.isPresent()) {
return rascalFromPom.get();
} else {
messages.append(makeMissingRascalMessage(manifestRoot));
}
}
return resolveCurrentRascalRuntime();
}

// This declaration does not mirror a constructor from the standard library; it is here to be able to send the command, but the declaration must live in the implementing project (e.g., `rascal-lsp`)
private static final io.usethesource.vallang.type.Type Command_addRascalDependencyToPom = tf.constructor(store, Messages.Command, "addRascalDependencyToPom", tf.sourceLocationType(), "pomLoc");

private static IConstructor addAddRascalDependencyFix(IConstructor msg, ISourceLocation pomXml) {
var title = "Add Rascal dependency to pom.xml";
var codeAction = vf.constructor(Command_addRascalDependencyToPom, new IValue[] { pomXml }, Map.of("title", vf.string(title)));
var fix = vf.constructor(Messages.CodeAction_action, new IValue[]{}, Map.of("command", codeAction));
return Messages.addFix(msg, fix);
}

private static IConstructor makeMissingRascalMessage(ISourceLocation manifestRoot) {
// The `pomXml` location is given an artificial range on the second line to make sure that the hover remains within the bounds of the editor
// Otherwise, the quick-fix pop-up immediately disappears as the editor loses focus
var pomXml = vf.sourceLocation(URIUtil.getChildLocation(manifestRoot, "pom.xml"), 0, 0, 2, 2, 0, 8);
var msg = Messages.warning("Missing required Rascal dependency in project " + URIUtil.getLocationName(manifestRoot), pomXml);
return addAddRascalDependencyFix(msg, pomXml);
}

private static void buildNormalProjectConfig(ISourceLocation manifestRoot, RascalConfigMode mode, List<Artifact> mavenClasspath, boolean isRoot, IListWriter srcs, IListWriter libs, IListWriter messages) throws IOException, URISyntaxException {
if (isRoot) {
var rascal = getRelevantRascal(mavenClasspath, mode);
var rascal = getRelevantRascal(mavenClasspath, mode, manifestRoot, messages);
var insideRascal = JarURIResolver.jarify(MavenRepositoryURIResolver.mavenize(rascal));

libs.append(rascal);
Expand Down
Loading