Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changes in the Lingua Franca Language Server to support improvements in the VSCode extension #2370

Merged
merged 18 commits into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from 11 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
11 changes: 11 additions & 0 deletions lsp/src/main/java/org/lflang/diagram/lsp/LFLanguageClient.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.lflang.diagram.lsp;

import de.cau.cs.kieler.klighd.lsp.KGraphLanguageClient;
import org.eclipse.lsp4j.jsonrpc.services.JsonNotification;
import org.eclipse.lsp4j.services.LanguageClient;

public interface LFLanguageClient extends KGraphLanguageClient, LanguageClient {

@JsonNotification("notify/sendLibraryReactors")
public void sendLibraryReactors(LibraryFileTree libraryFileTree);
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
package org.lflang.diagram.lsp;

import com.google.inject.Inject;
import com.google.inject.Injector;
import java.util.ArrayList;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Stream;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.lsp4j.jsonrpc.services.JsonNotification;
import org.eclipse.lsp4j.jsonrpc.services.JsonRequest;
import org.eclipse.lsp4j.services.LanguageClient;
import org.eclipse.xtext.ide.server.ILanguageServerAccess;
import org.eclipse.xtext.ide.server.ILanguageServerExtension;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
import org.eclipse.xtext.resource.XtextResourceSet;
import org.lflang.LFRuntimeModule;
import org.lflang.LFStandaloneSetup;
import org.lflang.ast.ToSExpr;
import org.lflang.generator.GeneratorResult;
import org.lflang.generator.GeneratorResult.Status;
import org.lflang.generator.IntegratedBuilder;
import org.lflang.lf.Model;
import org.lflang.lf.Reactor;
import org.lflang.lf.TargetDecl;
import org.lflang.util.LFCommand;

/**
Expand All @@ -30,17 +40,23 @@ class LFLanguageServerExtension implements ILanguageServerExtension {
.getInstance(IntegratedBuilder.class);

/** The access point for reading documents, communicating with the language client, etc. */
private LanguageClient client;
private LFLanguageClient client;

@Inject Injector injector;

@Override
public void initialize(ILanguageServerAccess access) {
// This method is never invoked.
}

public void setClient(LanguageClient client) {
public void setClient(LFLanguageClient client) {
this.client = client;
}

public XtextResourceSet getXtextResourceSet(final URI uri) {
return injector.getInstance(XtextResourceSet.class);
}

@JsonRequest("parser/ast")
public CompletableFuture<String> getAst(String uri) {
return CompletableFuture.supplyAsync(
Expand Down Expand Up @@ -84,6 +100,82 @@ public CompletableFuture<String> build(BuildArgs args) {
});
}

/**
* Manage requests to retrieve a hierarchical structure of reactor libraries based on the provided
* {@code filePath}.
*
* @param filePath the URI of the LF file of interest
* @return A message describing the outcome of the build process.
*/
@JsonRequest("generator/getLibraryReactors")
public CompletableFuture<LibraryFileTree> getLibraryReactors(String filePath) {
return CompletableFuture.supplyAsync(
() -> {
try {
// LF program file parsing
URI uri = URI.createURI(filePath);
// Return a list of reactors within the file at the specific uri
return parseLibraryReactors(uri);
} catch (Exception e) {
return null;
}
});
}

/**
* Retrieves the target position specified in the LF program file at the given path.
vinzbarbuto marked this conversation as resolved.
Show resolved Hide resolved
*
* @param path The path to the LF program file.
* @return A CompletableFuture containing the NodePosition object representing the position of the
* target, or null if an error occurs during parsing or if the target position is not found.
*/
@JsonRequest("generator/getTargetPosition")
public CompletableFuture<NodePosition> getTargetPosition(String path) {
return CompletableFuture.supplyAsync(
() -> {
NodePosition targetPosition = null;
try {
URI uri = URI.createURI(path);
// LF program file parsing
Resource resource = getXtextResourceSet(uri).getResource(uri, true);
Model m = (Model) resource.getContents().get(0);
TargetDecl target = m.getTarget();
INode node = NodeModelUtils.getNode(target);
targetPosition = new NodePosition(node.getStartLine(), node.getEndLine());
return targetPosition;
} catch (Exception e) {
return null;
}
});
}

/**
* Parses a library of reactors specified by the provided URI and constructs a hierarchical
vinzbarbuto marked this conversation as resolved.
Show resolved Hide resolved
* libraryFileTree representation.
*
* @param uri The URI specifying the location of the library.
* @return A Tree object representing the hierarchical structure of the reactor library, or null
* if an error occurs during parsing.
*/
public LibraryFileTree parseLibraryReactors(URI uri) {
LibraryFileTree res = new LibraryFileTree(uri.toString());
try {
Resource resource = getXtextResourceSet(uri).getResource(uri, true);
Model m = (Model) resource.getContents().get(0);
Stream<Reactor> reactors =
m.getReactors().stream().filter(r -> r.getName() != null && !r.getName().isEmpty());
reactors.forEach(
r -> {
INode node = NodeModelUtils.getNode(r);
NodePosition nodePosition = new NodePosition(node.getStartLine(), node.getEndLine());
res.addChild(new LibraryFileTreeNode(r.getName(), res.getUri(), nodePosition));
});
} catch (Exception e) {
return null;
vinzbarbuto marked this conversation as resolved.
Show resolved Hide resolved
}
return res;
}

/**
* Handles a request for the most complete build of the specified Lingua Franca file that can be
* done in a limited amount of time.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,16 +69,16 @@ public List<ILanguageServerExtension> getLanguageServerExtensions() {

@Override
public Class<? extends KGraphLanguageClient> getRemoteInterface() {
return KGraphLanguageClient.class;
return LFLanguageClient.class;
}

@Override
public void onConnect() {
super.onConnect();
constraints.setClient((KGraphLanguageClient) languageClient);
rectPack.setClient((KGraphLanguageClient) languageClient);
rectPack.setClient((LFLanguageClient) languageClient);
LanguageServerMessageReporter.setClient(languageClient);
lfExtension.setClient(languageClient);
lfExtension.setClient((LFLanguageClient) languageClient);
// The following is needed because VS Code treats System.err like System.out and System.out
// like a shout
// into the void.
Expand Down
59 changes: 59 additions & 0 deletions lsp/src/main/java/org/lflang/diagram/lsp/LibraryFileTree.java
vinzbarbuto marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package org.lflang.diagram.lsp;

import java.util.ArrayList;
import java.util.List;

/** Represents a tree structure, where each node can have zero or more children nodes. */
public class LibraryFileTree {
private String label; // The label associated with the tree.
private String uri; // The URI specifying the location of the tree.
private List<LibraryFileTreeNode> children; // The list of children nodes of the tree.

/**
* Constructs a new Tree with the specified URI.
*
* @param uri The URI specifying the location of the tree.
*/
public LibraryFileTree(String uri) {
this.uri = uri;
String[] splits = uri.toString().split("/");
this.label = splits[splits.length - 1];
this.children = new ArrayList<>();
}

/**
* Retrieves the label of the tree.
*
* @return The label of the tree.
*/
public String getLabel() {
return label;
}

/**
* Retrieves the URI specifying the location of the tree.
*
* @return The URI specifying the location of the tree.
*/
public String getUri() {
return uri;
}

/**
* Adds a child node to the tree.
*
* @param child The child node to be added.
*/
public void addChild(LibraryFileTreeNode child) {
children.add(child);
}

/**
* Retrieves the list of children nodes of the tree.
*
* @return The list of children nodes of the tree.
*/
public List<LibraryFileTreeNode> getChildren() {
return children;
}
}
51 changes: 51 additions & 0 deletions lsp/src/main/java/org/lflang/diagram/lsp/LibraryFileTreeNode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package org.lflang.diagram.lsp;

/**
* Represents a node in a tree structure, typically used to store information about elements in a
* hierarchical context.
*/
vinzbarbuto marked this conversation as resolved.
Show resolved Hide resolved
public class LibraryFileTreeNode {
private String label; // The label associated with the node.
private String uri; // The URI specifying the location of the associated element.
private NodePosition position; // The position information of the associated element.

/**
* Constructs a new TreeNode with the specified label, URI, and position.
*
* @param label The label of the node.
* @param uri The URI specifying the location of the associated element.
* @param position The position information of the associated element.
*/
public LibraryFileTreeNode(String label, String uri, NodePosition position) {
this.label = label;
this.uri = uri;
this.position = position;
}

/**
* Retrieves the label of the node.
*
* @return The label of the node.
*/
public String getLabel() {
return label;
}

/**
* Retrieves the URI specifying the location of the associated element.
*
* @return The URI specifying the location of the associated element.
*/
public String getUri() {
return uri;
}

/**
* Retrieves the position information of the associated element.
*
* @return The position information of the associated element.
*/
public NodePosition getPosition() {
return position;
}
}
36 changes: 36 additions & 0 deletions lsp/src/main/java/org/lflang/diagram/lsp/NodePosition.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.lflang.diagram.lsp;

/** Represents the position of a node within a text document or code file. */
vinzbarbuto marked this conversation as resolved.
Show resolved Hide resolved
public class NodePosition {
private int start; // The starting position of the node.
private int end; // The ending position of the node.

/**
* Constructs a new NodePosition with the specified start and end positions.
*
* @param start The starting position of the node.
* @param end The ending position of the node.
*/
public NodePosition(int start, int end) {
this.start = start;
this.end = end;
}

/**
* Retrieves the starting position of the node.
*
* @return The starting position of the node.
*/
public int getStart() {
return start;
}

/**
* Retrieves the ending position of the node.
*
* @return The ending position of the node.
*/
public int getEnd() {
return end;
}
}
Loading