Skip to content

Commit

Permalink
Added integration with souffle-lint
Browse files Browse the repository at this point in the history
Added source code action to trigger souffle-lint
Added code action to extract types
  • Loading branch information
jdaridis committed Nov 13, 2022
1 parent 74740f4 commit 47a98fa
Show file tree
Hide file tree
Showing 19 changed files with 297 additions and 82 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ fabric.properties

bin/
build/
!build/libs/Souffle_Ide_Plugin-1.0-SNAPSHOT.jar
dist/
.gradle/*
*.lock
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Change Log
## [0.2.0]

- Added integration with [souffle-lint](https://github.com/langston-barrett/souffle-lint).
- Added source code action to trigger souffle-lint
- Added code action to extract types
## [0.1.6]

Added code actions for reformatting documentation comments.
Expand Down
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ This is a plugin adding basic smart features to the Soufflé language, using the

![Syntax error message](images/syntax_error.png)

- Integration with [souffle-lint](https://github.com/langston-barrett/souffle-lint) (If available on user system)

![Souffle-lint message](images/souffle-lint.png)

- Hover (Provide declaration info on hovers)

Provide documentation about relations taken from comments (for multiline comments use the /* */ style)
![Hover example](images/hover_1.png)

Expand Down Expand Up @@ -75,6 +80,12 @@ In libraries with heavy use of the C preprocessor macros, sometimes parsing fail

## Release Notes

### 0.2.0

- Added integration with [souffle-lint](https://github.com/langston-barrett/souffle-lint).
- Added source code action to trigger souffle-lint
- Added code action to extract types

### 0.1.6

Added code actions for reformatting documentation comments.
Expand Down
Binary file modified build/libs/Souffle_Ide_Plugin-1.0-SNAPSHOT.jar
Binary file not shown.
Binary file added images/souffle-lint.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "souffle-lang-server",
"displayName": "Soufflé Datalog Language Server",
"description": "Soufflé Datalog Language Server. Add smart features to the Soufflé Datalog Language with the help of LSP in a VS code plugin",
"version": "0.1.6",
"version": "0.2.0",
"engines": {
"vscode": "^1.65.0"
},
Expand Down
148 changes: 148 additions & 0 deletions src/main/java/CodeActionProvider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import org.eclipse.lsp4j.*;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import parsing.symbols.SouffleContext;
import parsing.symbols.SouffleProjectContext;
import parsing.symbols.SouffleSymbol;
import parsing.symbols.SouffleSymbolType;

import java.net.URI;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class CodeActionProvider {
public CodeActionProvider() {
}

public List<Either<Command, CodeAction>> getCodeAction(CodeActionParams params) {
List<Either<Command, CodeAction>> actions = new ArrayList<Either<Command, CodeAction>>();

lintCodeAction(params, actions);

Range cursor = params.getRange();
SouffleContext context = SouffleProjectContext.getInstance().getContext(params.getTextDocument().getUri(), cursor);
if (context != null) {
SouffleSymbol currentSymbol = context.getSymbol(cursor);
if (currentSymbol != null) {
switch (currentSymbol.getKind()) {
case RELATION_USE:
case RELATION_DECL:
generateIOCodeAction(params, actions, context, currentSymbol);
break;
case TYPE_USE:
case TYPE_DECL:
extractTypeCodeAction(params, actions, currentSymbol);
break;
}
}
}

return actions;
}

private void extractTypeCodeAction(CodeActionParams params, List<Either<Command, CodeAction>> actions, SouffleSymbol currentSymbol) {
CodeAction extractSubtypeAction = new CodeAction("Extract as Supertype");
extractSubtypeAction.setKind(CodeActionKind.RefactorExtract);
actions.add(Either.forRight(extractSubtypeAction));
WorkspaceEdit extractEdit = new WorkspaceEdit();
TextEdit newTypeText = new TextEdit();
newTypeText.setNewText(".type " + "subType <: " + currentSymbol + "\n\n");

int line = currentSymbol.getRange().getStart().getLine() + 1;

List<TextEdit> subtypeEdits = new ArrayList<TextEdit>();
if (currentSymbol.getKind() != SouffleSymbolType.TYPE_DECL) {
TextEdit replaceTypeEdit = new TextEdit();
replaceTypeEdit.setNewText("subType");
replaceTypeEdit.setRange(currentSymbol.getRange());
subtypeEdits.add(replaceTypeEdit);
line = currentSymbol.getRange().getStart().getLine() - 1;
}
extractSubtypeAction.setData("subType");
Position start = new Position(line, 0);

Range newTypeRange = new Range(start, start);
newTypeText.setRange(newTypeRange);

subtypeEdits.add(newTypeText);
extractEdit.setChanges(Map.of(params.getTextDocument().getUri(), subtypeEdits));
extractSubtypeAction.setEdit(extractEdit);

CodeAction extractTypeAction = new CodeAction("Extract Type");
extractTypeAction.setKind(CodeActionKind.RefactorExtract);
actions.add(Either.forRight(extractTypeAction));
WorkspaceEdit extractTypeEdit = new WorkspaceEdit();
TextEdit newTypeText1 = new TextEdit();
newTypeText1.setNewText(".type " + "extractedType = " + currentSymbol + "\n\n");

newTypeText1.setRange(newTypeRange);
List<TextEdit> extractEdits = new ArrayList<TextEdit>();
extractEdits.add(newTypeText1);
if (currentSymbol.getKind() != SouffleSymbolType.TYPE_DECL) {
TextEdit replaceTypeEdit1 = new TextEdit();
replaceTypeEdit1.setNewText("extractedType");
replaceTypeEdit1.setRange(currentSymbol.getRange());
extractEdits.add(replaceTypeEdit1);
}
extractTypeEdit.setChanges(Map.of(params.getTextDocument().getUri(), extractEdits));
extractTypeAction.setEdit(extractTypeEdit);
}

private void generateIOCodeAction(CodeActionParams params, List<Either<Command, CodeAction>> actions, SouffleContext context, SouffleSymbol currentSymbol) {
CodeAction inputAction = new CodeAction("Generate .input for relation " + currentSymbol.getName());
inputAction.setKind(CodeActionKind.Refactor);
actions.add(Either.forRight(inputAction));
WorkspaceEdit edit = new WorkspaceEdit();
TextEdit textEdit = new TextEdit();
textEdit.setNewText("\t.input " + currentSymbol.getName() + "()\n");

Position end;
if (currentSymbol.getKind() == SouffleSymbolType.RELATION_DECL) {
end = currentSymbol.getRange().getEnd();
} else {
end = context.getRange().getEnd();
}
Position position = new Position(end.getLine() + 2, 0);
Range newRange = new Range(position, position);
textEdit.setRange(newRange);
edit.setChanges(Map.of(params.getTextDocument().getUri(), List.of(textEdit)));
inputAction.setEdit(edit);

CodeAction outputAction = new CodeAction("Generate .output for relation " + currentSymbol.getName());
outputAction.setKind(CodeActionKind.Refactor);
actions.add(Either.forRight(outputAction));
WorkspaceEdit edit1 = new WorkspaceEdit();
TextEdit textEdit1 = new TextEdit();
textEdit1.setNewText("\t.output " + currentSymbol.getName() + "()\n");

textEdit1.setRange(newRange);
edit1.setChanges(Map.of(params.getTextDocument().getUri(), List.of(textEdit1)));
outputAction.setEdit(edit1);

if ((currentSymbol.getKind() == SouffleSymbolType.RELATION_DECL ||
currentSymbol.getKind() == SouffleSymbolType.COMPONENT_DECL) &&
currentSymbol.getPotentialDocumentation().getKey() != null) {
CodeAction formatComments = new CodeAction("Format documentation with /* */");
formatComments.setKind(CodeActionKind.RefactorRewrite);
WorkspaceEdit commentEdit = new WorkspaceEdit();
TextEdit commentTextEdit = new TextEdit();
commentTextEdit.setRange(currentSymbol.getPotentialDocumentation().getValue());
commentTextEdit.setNewText(currentSymbol.getPotentialDocumentation().getKey());
commentEdit.setChanges(Map.of(params.getTextDocument().getUri(), List.of(commentTextEdit)));
formatComments.setEdit(commentEdit);
actions.add(Either.forRight(formatComments));
}
}

private void lintCodeAction(CodeActionParams params, List<Either<Command, CodeAction>> actions) {
CodeAction codeAction = new CodeAction("Lint with souffle-lint");
Command command = new Command();
command.setCommand("souffle-lint");
Path path = Path.of(URI.create(params.getTextDocument().getUri()));
command.setArguments(List.of(path.toString()));
codeAction.setCommand(command);
codeAction.setKind(CodeActionKind.Source + ".lint");
actions.add(Either.forRight(codeAction));
}
}
25 changes: 25 additions & 0 deletions src/main/java/LSClientLogger.java
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,29 @@ public void reportHint(Range range, String uri, String message){
diagnostics.get(uri).add(diagnostic);
client.publishDiagnostics(new PublishDiagnosticsParams(uri, diagnostics.get(uri)));
}

public void reportLints(List<SouffleLint> lints, String uri){
for (SouffleLint lint: lints){
if(lint != null){
for(SouffleLintContext fragment: lint.fragments){
Diagnostic diagnostic = new Diagnostic();
diagnostic.setSeverity(DiagnosticSeverity.Warning);
String message = lint.rule.name + ": " + lint.rule.shortDescription + "\n\n";
message+= "Examples: \n" + lint.rule.getExamples();
diagnostic.setMessage(message);
Position start = new Position(fragment.start.row, fragment.start.column);
Position end = new Position(fragment.end.row, fragment.end.column);
Range range = new Range(start, end);
diagnostic.setRange(range);
diagnostics.get(uri).add(diagnostic);
}
}
}
if(lints.isEmpty()){
MessageParams messageParams = new MessageParams();
messageParams.setMessage("No problems found with souffle-lint");
client.showMessage(messageParams);
}
client.publishDiagnostics(new PublishDiagnosticsParams(uri, diagnostics.get(uri)));
}
}
10 changes: 8 additions & 2 deletions src/main/java/SouffleLanguageServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,20 @@ public CompletableFuture<InitializeResult> initialize(InitializeParams initializ
serverCapabilities.setRenameProvider(true);
// serverCapabilities.setCodeActionProvider(true);
CodeActionOptions codeActionOptions = new CodeActionOptions();
codeActionOptions.setResolveProvider(true);
codeActionOptions.setCodeActionKinds(List.of(CodeActionKind.Source, CodeActionKind.Empty, CodeActionKind.QuickFix));
serverCapabilities.setCodeActionProvider(codeActionOptions);
ExecuteCommandOptions executeCommandOptions = new ExecuteCommandOptions();
executeCommandOptions.setCommands(List.of("lint"));
executeCommandOptions.setCommands(List.of("souffle-lint", "souffle-lint-all"));
serverCapabilities.setExecuteCommandProvider(executeCommandOptions);

final InitializeResult response = new InitializeResult(serverCapabilities);
//Set the document synchronization capabilities to full.

this.clientCapabilities = initializeParams.getCapabilities();
CodeActionResolveSupportCapabilities codeActionResolveSupportCapabilities = new CodeActionResolveSupportCapabilities();
codeActionResolveSupportCapabilities.setProperties(List.of("edit"));
this.clientCapabilities.getTextDocument().getCodeAction().setResolveSupport(codeActionResolveSupportCapabilities);
/* Check if dynamic registration of completion capability is allowed by the client. If so we don't register the capability.
Else, we register the completion capability.
*/
Expand All @@ -86,7 +90,9 @@ public CompletableFuture<InitializeResult> initialize(InitializeParams initializ
projectContext = SouffleProjectContext.getInstance();
List<WorkspaceFolder> workspaceFolders = initializeParams.getWorkspaceFolders();
if(workspaceFolders != null && !workspaceFolders.isEmpty()){
traverseWorkspace(URI.create(workspaceFolders.get(0).getUri()).getPath());
String directory = URI.create(workspaceFolders.get(0).getUri()).getPath();
projectContext.setProjectPath(directory);
traverseWorkspace(directory);
}
return CompletableFuture.supplyAsync(() -> response);
}
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/SouffleLint.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import java.util.List;

public class SouffleLint {
List<SouffleLintContext> fragments;
SouffleLintRule rule;
String source;
String source_file;
}
7 changes: 7 additions & 0 deletions src/main/java/SouffleLintContext.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
public class SouffleLintContext {
String node_text;
SouffleLintContext context;
SouffleLintPoint start;
SouffleLintPoint end;

}
4 changes: 4 additions & 0 deletions src/main/java/SouffleLintExample.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
public class SouffleLintExample {
String before;
String after;
}
4 changes: 4 additions & 0 deletions src/main/java/SouffleLintPoint.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
public class SouffleLintPoint {
int row;
int column;
}
27 changes: 27 additions & 0 deletions src/main/java/SouffleLintRule.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import com.google.gson.annotations.SerializedName;

import java.util.List;

public class SouffleLintRule {
String name;
@SerializedName("short")
String shortDescription;
@SerializedName("long")
String longDescription;
boolean captures;
boolean slow;
List<String> queries;
List<SouffleLintExample> examples;


public String getExamples(){
StringBuilder stringBuilder = new StringBuilder();
for (SouffleLintExample example: examples){
stringBuilder.append(example.before);
stringBuilder.append(example.after.trim());
stringBuilder.append(",\n");
}
stringBuilder.deleteCharAt(stringBuilder.length() - 2);
return stringBuilder.toString();
}
}
5 changes: 5 additions & 0 deletions src/main/java/SouffleLints.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import java.util.List;

public class SouffleLints {
List<SouffleLint> lints;
}
Loading

0 comments on commit 47a98fa

Please sign in to comment.