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

Feature/lsp #23

Merged
merged 25 commits into from
Dec 8, 2023
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
010fd1d
feat: add language server interface
sillydan1 Nov 29, 2023
25083bf
feat: integrate language server interface
sillydan1 Nov 29, 2023
e484187
feat: add example language server for LTS syntax
sillydan1 Nov 29, 2023
0e2e3a8
feat: add tarjan implementation
sillydan1 Nov 29, 2023
9c1fcd1
feat: integrate the simple scc detection lts language server
sillydan1 Nov 29, 2023
91702db
fix: dont clear everything. there might be other buffers open
sillydan1 Nov 29, 2023
6ef22f5
refactor: rename LintPaneController to LintLayerController
sillydan1 Nov 30, 2023
b857e52
fix: only lint the supported language
sillydan1 Nov 30, 2023
2cea76e
cleanup: clean up some of the code by extracting diff checks into a f…
sillydan1 Dec 1, 2023
9cd4364
fix: viewmodelpoint removelistener now works
sillydan1 Dec 1, 2023
7c2538a
refactor: move convex hull polygon stuff into it's own dedicated class
sillydan1 Dec 1, 2023
cf77941
fix: convex hull polygons are now reactive, responsive and styled pro…
sillydan1 Dec 1, 2023
2f62a43
cleanup: remove old debugging lint panel code
sillydan1 Dec 1, 2023
9afb806
feat: add clipboard tool
sillydan1 Dec 2, 2023
25b018f
fix: key events will now be properly applied only to the selected tab
sillydan1 Dec 5, 2023
62aa87e
fix: integrate clipboard tool into unified modelling tool
sillydan1 Dec 5, 2023
6c1ea0b
feat: add mass delete tool and cut support to clipboard tool
sillydan1 Dec 5, 2023
e5b354b
docs: update progress
sillydan1 Dec 5, 2023
141e80b
chore: add help and tooltips for the new tools
sillydan1 Dec 5, 2023
ea167ec
docs: update progress.md
sillydan1 Dec 5, 2023
517a88f
nitpick: fix bad javadoc tag
sillydan1 Dec 7, 2023
a86a941
feat: add clispinner lsp progress indicator
sillydan1 Dec 8, 2023
e583583
nitpick: extract function
sillydan1 Dec 8, 2023
4e9d270
nitpick: remove unused ctor parameter in filebuffer container
sillydan1 Dec 8, 2023
ca9d452
nitpick: windows builds are so finicky
sillydan1 Dec 8, 2023
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
37 changes: 19 additions & 18 deletions PROGRESS.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,36 +106,37 @@
- [x] TODOs round
- [x] update readme (screenshots etc)
- [x] Release `v1.1.0
- [ ] GraphDiff algorithm (look at https://stackoverflow.com/questions/16553343/diff-for-directed-acyclic-graphs)
- [ ] Additions (new syntactic elements), Deletions (B does not have some syntactic element), Edits (Some syntactic element's properties has been changed)
- [ ] Cut / Copy / Paste support
- [ ] Plan for git integration
- [ ] Diff view?
- [x] GraphDiff algorithm (look at https://stackoverflow.com/questions/16553343/diff-for-directed-acyclic-graphs)
- [x] Additions (new syntactic elements), Deletions (B does not have some syntactic element), Edits (Some syntactic element's properties has been changed)
- [x] Cut / Copy / Paste support
- [x] Lint layer
- [ ] Lint protobuf specification
- [ ] Implement `ILint` / `ILinter` interfaces
- [x] Implement `ILint` / `ILinter` interfaces
- [x] Release core as a library on ossrh
- [ ] Additional Syntaxes
- [x] Additional Syntaxes
- [x] Simple Text
- [x] LTS
- [ ] NTTA
- [ ] HAWK
- [x] P/N
- [ ] TIOA
- [ ] Plugin Manager
- [ ] Fix TODOs
- [ ] Polish
- [ ] Release `v1.2.0
- [ ] Third party language plugins
- [ ] TIOA
- [ ] NTTA
- [ ] HAWK
- [ ] expr richtextfx.CodeArea implementation
- [ ] Trace-traverser & specification
- [ ] Release `v1.3.0`
- [ ] LSP like specification (use docusaurus, or github wiki)
- [ ] Plan for git integration
- [ ] Diff view?
- [x] Add plugin API and
- [x] LSP like specification (use docusaurus, or github wiki)
- [ ] Protobuf specification (that way, you are language agnostic)
- [ ] Implement `ILsp` / `ILspEngine` interfaces
- [ ] Release `v1.4.0`
- [x] Implement `ILsp` / `ILspEngine` interfaces
- [ ] Release `v1.3.0`
- [ ] DAP like specification
- [ ] Protobuf specification (that way, you are language agnostic)
- [ ] Implement `IDap` / `IDapEngine` interfaces
- [ ] Release `v1.5.0`
- [ ] Add plugin API and [LuaJava](https://github.com/gudzpoz/luajava/tree/main)
- [ ] Custom keybinds
- [ ] Rewrite the default plugins as a lua plugin. This will simplify the codebase tremendously
- [ ] Release `v1.4.0`
- [ ] Example lua plugin [LuaJava](https://github.com/gudzpoz/luajava/tree/main).
- [ ] Release `v2.0.0`
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ subprojects {
implementation group: 'dk.yalibs', name: 'yafunc', version: '1.0.0';
implementation group: 'dk.yalibs', name: 'yastreamgobbler', version: '1.0.0';
implementation group: 'dk.yalibs', name: 'yaundo', version: '1.0.0';
implementation group: 'dk.yalibs', name: 'yafunc', version: '1.0.0';
implementation group: 'io.github.mkpaz', name: 'atlantafx-base', version: '2.0.1';
implementation group: 'org.apache.tika', name: 'tika-core', version: '2.9.0'
implementation group: 'org.fxmisc.richtext', name: 'richtextfx', version: '0.11.0'
Expand Down
3 changes: 3 additions & 0 deletions core/src/main/java/dk/gtz/graphedit/model/ModelLint.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package dk.gtz.graphedit.model;

import java.util.List;
import java.util.Optional;
import java.util.UUID;

public record ModelLint(
String modelKey,
String lintIdentifier,
ModelLintSeverity severity,
String title,
String message,
Optional<String> lintDescription,
List<UUID> affectedElements,
List<List<ModelPoint>> affectedRegions) {}

Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package dk.gtz.graphedit.model;

public enum ModelLintSeverity {
HINT,
INFO,
WARNING,
ERROR
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package dk.gtz.graphedit.model.lsp;

public record ModelLanguageServerProgress(
sillydan1 marked this conversation as resolved.
Show resolved Hide resolved
String token,
ModelLanguageServerProgressType type,
String title,
String message) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package dk.gtz.graphedit.model.lsp;

public enum ModelLanguageServerProgressType {
BEGIN,
PROGRESS,
END
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package dk.gtz.graphedit.model.lsp;


public record ModelNotification(
String level,
String message) {}
51 changes: 51 additions & 0 deletions core/src/main/java/dk/gtz/graphedit/spi/ILanguageServer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package dk.gtz.graphedit.spi;

import java.io.File;
import java.util.Collection;

import dk.gtz.graphedit.model.ModelLint;
import dk.gtz.graphedit.model.lsp.ModelLanguageServerProgress;
import dk.gtz.graphedit.model.lsp.ModelNotification;
import dk.gtz.graphedit.viewmodel.IBufferContainer;
import dk.yalibs.yafunc.IRunnable1;

public interface ILanguageServer {
/**
* Get the name of the language that the server supports.
* Expected to match what {@link ISyntaxFactory#getSyntaxName} provides
* @return A string with the name of the supported language.
*/
String getLanguageName();

/**
* Get the name of the language server (not to be confused with {@link getLanguageName})
* @return A string representing the name of the language server
*/
String getServerName();

/**
* Get the version string of the language server.
* i.e. "v1.0.0"
* @return A string representing the version of the language server
*/
String getServerVersion();

/**
* Function to initialize (not start) the language server
* This will be called right before any callback functions are added
* @param projectFile The Graphedit project file.
* @param bufferContainer The collection of buffers, likely (but not guaranteed) to be empty at initialization
*/
void initialize(File projectFile, IBufferContainer bufferContainer);

/**
* Start the language server.
* This will be called from a seperate thread and will be interrupted via {@link Thread#interrupt} when it's time to close.
* This means that this function should be a blocking call
*/
void start();
Collection<ModelLint> getDiagnostics();
void addDiagnosticsCallback(IRunnable1<Collection<ModelLint>> callback);
void addNotificationCallback(IRunnable1<ModelNotification> callback);
void addProgressCallback(IRunnable1<ModelLanguageServerProgress> callback);
}
4 changes: 4 additions & 0 deletions core/src/main/java/dk/gtz/graphedit/spi/IPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,8 @@ default Collection<ISyntaxFactory> getSyntaxFactories() throws Exception {
default Collection<IPluginPanel> getPanels() throws Exception {
return List.of();
}

default Collection<ILanguageServer> getLanguageServers() throws Exception {
return List.of();
}
}
187 changes: 187 additions & 0 deletions core/src/main/java/dk/gtz/graphedit/tool/ClipboardTool.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
package dk.gtz.graphedit.tool;

import java.util.HashMap;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;

import org.kordamp.ikonli.bootstrapicons.BootstrapIcons;
import org.kordamp.ikonli.javafx.FontIcon;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import dk.gtz.graphedit.events.ViewportKeyEvent;
import dk.gtz.graphedit.exceptions.SerializationException;
import dk.gtz.graphedit.exceptions.UncomparableException;
import dk.gtz.graphedit.model.ModelGraph;
import dk.gtz.graphedit.serialization.IModelSerializer;
import dk.gtz.graphedit.viewmodel.IBufferContainer;
import dk.gtz.graphedit.viewmodel.ViewModelDiff;
import dk.gtz.graphedit.viewmodel.ViewModelEdge;
import dk.gtz.graphedit.viewmodel.ViewModelGraph;
import dk.gtz.graphedit.viewmodel.ViewModelPoint;
import dk.gtz.graphedit.viewmodel.ViewModelProjectResource;
import dk.gtz.graphedit.viewmodel.ViewModelSelection;
import dk.gtz.graphedit.viewmodel.ViewModelVertex;
import dk.yalibs.yadi.DI;
import dk.yalibs.yaundo.IUndoSystem;
import dk.yalibs.yaundo.Undoable;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;

/**
* Tool that enables cut/copy/paste support
*
* - ctrl + x to cut
* - ctrl + c to copy
* - ctrl + v to paste
*/
public class ClipboardTool extends AbstractBaseTool {
private final Logger logger = LoggerFactory.getLogger(ClipboardTool.class);
private final ObservableList<ViewModelSelection> selectedElements;
private final IModelSerializer serializer;
private final IBufferContainer buffers;
private final Clipboard clipboard;
private final IUndoSystem undoSystem;
private final MassDeleteTool deleteTool;

public ClipboardTool() {
selectedElements = DI.get("selectedElements");
serializer = DI.get(IModelSerializer.class);
buffers = DI.get(IBufferContainer.class);
undoSystem = DI.get(IUndoSystem.class);
clipboard = Clipboard.getSystemClipboard();
deleteTool = new MassDeleteTool();
}

@Override
public String getHelpDescription() {
return """
Tool that enables cut/copy/paste support

- <Shortcut> + X to cut
- <Shortcut> + C to copy
- <Shortcut> + V to paste
""";
}

@Override
public Optional<String> getTooltip() {
return Optional.of("copy and paste sections of models");
}

@Override
public Node getGraphic() {
return new FontIcon(BootstrapIcons.CLIPBOARD);
}

@Override
public void onKeyEvent(ViewportKeyEvent e) {
if(!e.event().isShortcutDown())
return;
if(!e.event().getEventType().equals(KeyEvent.KEY_PRESSED))
return;
if(e.event().getCode().equals(KeyCode.C))
copySelection(e);
if(e.event().getCode().equals(KeyCode.V))
pasteModel(e);
if(e.event().getCode().equals(KeyCode.X))
cutSelection(e);
}

public void copySelection(ViewportKeyEvent e) {
try {
if(selectedElements.isEmpty())
return;
var buffer = buffers.get(e.bufferId());
var selectedVertices = buffer.syntax().vertices().entrySet().stream()
.filter(elem -> selectedElements.stream().anyMatch(s -> s.id().equals(elem.getKey())))
.collect(Collectors.toMap(
elem -> elem.getKey(),
elem -> elem.getValue().toModel()));
var selectedEdges = buffer.syntax().edges().entrySet().stream()
.filter(elem -> selectedElements.stream().anyMatch(s -> s.id().equals(elem.getKey())))
.collect(Collectors.toMap(
elem -> elem.getKey(),
elem -> elem.getValue().toModel()));
for(var edge : selectedEdges.entrySet()) {
if(!selectedVertices.containsKey(edge.getValue().source))
selectedVertices.put(edge.getValue().source, buffer.syntax().vertices().get(edge.getValue().source).toModel());
if(!selectedVertices.containsKey(edge.getValue().target))
selectedVertices.put(edge.getValue().target, buffer.syntax().vertices().get(edge.getValue().target).toModel());
}
var modelGraph = new ModelGraph("", selectedVertices, selectedEdges); // NOTE: Declarations string cannot be copied by this tool
var resource = new ViewModelProjectResource(buffer.metadata(), new ViewModelGraph(modelGraph, e.syntax())).toModel();
var serializedModel = serializer.serialize(resource);
var content = new ClipboardContent();
content.putString(serializedModel);
clipboard.setContent(content);
logger.trace("copied {} vertices and {} edges to clipboard", selectedVertices.size(), selectedEdges.size());
} catch (Exception exc) {
throw new RuntimeException(exc);
}
}

public void pasteModel(ViewportKeyEvent e) {
try {
if(!clipboard.hasString()) {
logger.error("no plain text in clipboard");
return;
}
var syntaxFactory = e.syntax();
var buffer = buffers.get(e.bufferId());
var content = clipboard.getString();
var resource = new ViewModelProjectResource(serializer.deserializeProjectResource(content), e.syntax());
var vertexOffset = new ViewModelPoint(e.editorSettings().gridSizeX().get(), e.editorSettings().gridSizeY().get());
var rerolledVertex = new HashMap<UUID,ViewModelVertex>();
var rerolledEdges = new HashMap<UUID,ViewModelEdge>();
var remapping = new HashMap<UUID,UUID>();
for(var vertex : resource.syntax().vertices().entrySet()) {
var v = vertex.getValue();
var rerolled = UUID.randomUUID();
v.position().setValue(v.position().add(vertexOffset));
rerolledVertex.put(rerolled, syntaxFactory.createVertexViewModel(rerolled, v.toModel()));
remapping.put(vertex.getKey(), rerolled);
}
for(var edge : resource.syntax().edges().entrySet()) {
var ev = edge.getValue();
var rerolled = UUID.randomUUID();
ev.source().set(remapping.get(ev.source().get()));
ev.target().set(remapping.get(ev.target().get()));
assert ev.source().get() != null;
assert ev.target().get() != null;
rerolledEdges.put(rerolled, syntaxFactory.createEdgeViewModel(rerolled, ev.toModel()));
}
resource.syntax().vertices().clear();
resource.syntax().vertices().putAll(rerolledVertex);
resource.syntax().edges().clear();
resource.syntax().edges().putAll(rerolledEdges);
var diff = ViewModelDiff.compare(buffer, resource);
ViewModelDiff.applyAdditiveOnly(buffer, diff);
undoSystem.push(new Undoable("paste content",
() -> ViewModelDiff.revertAdditiveOnly(buffer, diff),
() -> ViewModelDiff.applyAdditiveOnly(buffer, diff)));
} catch (SerializationException exc) {
logger.trace("clipboard value not deserializable {}", exc.getMessage());
} catch (UncomparableException exc) {
logger.warn("clipboard model and target model are different syntaxes");
} catch (Exception exc) {
throw new RuntimeException(exc);
}
}

public void cutSelection(ViewportKeyEvent e) {
try {
if(selectedElements.isEmpty())
return;
copySelection(e);
deleteTool.deleteSelectedElements(e);
} catch(Exception exc) {
throw new RuntimeException(exc);
}
}
}
Loading