Compare commits

..

2 Commits

Author SHA1 Message Date
Ruben 0d50cf1517 feat: [NOT WORKING] maybe slowly starts working 2025-09-15 17:11:20 +02:00
Ruben 39876f3866 feat: [NOT WORKING] add initial Version of async calculation of compiler 2025-09-15 15:47:14 +02:00
49 changed files with 2464 additions and 3736 deletions
@@ -0,0 +1,17 @@
package org.example;
//TIP To <b>Run</b> code, press <shortcut actionId="Run"/> or
// click the <icon src="AllIcons.Actions.Execute"/> icon in the gutter.
public class Main {
public static void main(String[] args) {
//TIP Press <shortcut actionId="ShowIntentionActions"/> with your caret at the highlighted text
// to see how IntelliJ IDEA suggests fixing it.
System.out.printf("Hello and welcome!");
for (int i = 1; i <= 5; i++) {
//TIP Press <shortcut actionId="Debug"/> to start debugging your code. We have set one <icon src="AllIcons.Debugger.Db_set_breakpoint"/> breakpoint
// for you, but you can always add more by pressing <shortcut actionId="ToggleLineBreakpoint"/>.
System.out.println("i = " + i);
}
}
}
+2 -2
View File
@@ -1,3 +1,3 @@
# Java-TX Language Server # lspclient README
This is the official Plugin for Java-TX. This is the README for the Java-TX LSP Client.
File diff suppressed because it is too large Load Diff
+1 -2
View File
@@ -3,7 +3,7 @@
"name": "java-tx-language-extension", "name": "java-tx-language-extension",
"displayName": "Java-TX Language Extension", "displayName": "Java-TX Language Extension",
"description": "The Language Extension for Java-TX with Typehints and Syntax Checks", "description": "The Language Extension for Java-TX with Typehints and Syntax Checks",
"version": "0.0.16", "version": "0.0.5",
"engines": { "engines": {
"vscode": "^1.94.0" "vscode": "^1.94.0"
}, },
@@ -42,7 +42,6 @@
"typescript": "^5.6.2" "typescript": "^5.6.2"
}, },
"dependencies": { "dependencies": {
"@vscode/vsce": "^3.6.2",
"vscode-languageclient": "^9.0.1" "vscode-languageclient": "^9.0.1"
} }
} }
+35 -20
View File
@@ -5,19 +5,28 @@ import {
ServerOptions ServerOptions
} from 'vscode-languageclient/node'; } from 'vscode-languageclient/node';
let client: LanguageClient | undefined; let client: LanguageClient | undefined; // <— global, damit wir neu starten können
function createClient(context: vscode.ExtensionContext): LanguageClient { function createClient(context: vscode.ExtensionContext): LanguageClient {
const extensionPath = context.extensionPath; const workspaceFolder = context.extensionPath;
const jarPath = extensionPath + '/JavaTXLanguageServer-1.0-SNAPSHOT-jar-with-dependencies.jar';
const serverOptions: ServerOptions = { const serverOptions: ServerOptions = {
run: { command: 'java', args: ['-Xss10m', '-jar', jarPath] }, run: {
debug: { command: 'java', args: ['-Xss10m', '-jar', jarPath] } command: 'java',
args: ['-Xss2m', '-jar', workspaceFolder + "/JavaTXLanguageServer-1.0-SNAPSHOT-jar-with-dependencies.jar"],
},
debug: {
command: 'java',
args: [
'-Xss2m',
'-jar',
workspaceFolder + '/JavaTXLanguageServer-1.0-SNAPSHOT-jar-with-dependencies.jar',
],
}
}; };
const clientOptions: LanguageClientOptions = { const clientOptions: LanguageClientOptions = {
documentSelector: [{ scheme: 'file', pattern: '**/*.jav' }], documentSelector: [{ scheme: 'file', language: 'java' }],
synchronize: { synchronize: {
fileEvents: vscode.workspace.createFileSystemWatcher('**/*.jav') fileEvents: vscode.workspace.createFileSystemWatcher('**/*.jav')
}, },
@@ -35,31 +44,37 @@ function createClient(context: vscode.ExtensionContext): LanguageClient {
export async function activate(context: vscode.ExtensionContext) { export async function activate(context: vscode.ExtensionContext) {
client = createClient(context); client = createClient(context);
try { client.start()
await client.start(); .then(() => console.log("Language Client erfolgreich gestartet"))
console.log('Java-TX Language Server started successfully.'); .catch(err => console.error("Fehler beim Starten des Language Clients:", err));
} catch (err) {
console.error('Failed to start Language Client:', err);
}
console.log('Congratulations, your extension "tx" is now active!');
// Beispiel-Command aus deinem Code bleibt
const hello = vscode.commands.registerCommand('lspclient.helloWorld', () => {
vscode.window.showInformationMessage('Hello World from TX!');
});
context.subscriptions.push(hello);
// *** NEU: Restart-Command ***
const restart = vscode.commands.registerCommand('tx.restartLanguageServer', async () => { const restart = vscode.commands.registerCommand('tx.restartLanguageServer', async () => {
if (!client) { if (!client) {
vscode.window.showWarningMessage('Language Client is not initialized.'); vscode.window.showWarningMessage('Language Client ist nicht initialisiert.');
return; return;
} }
try { try {
await client.stop(); await client.stop(); // stoppt den Serverprozess
client.dispose(); client.dispose(); // räumt Ressourcen auf
} catch (e) { } catch (e) {
console.error('Error stopping Language Client:', e); console.error('Fehler beim Stoppen des Language Clients:', e);
} }
client = createClient(context); client = createClient(context); // komplett neu erzeugen
try { try {
await client.start(); await client.start();
vscode.window.showInformationMessage('Java-TX Language Server restarted.'); vscode.window.showInformationMessage('Java-TX Language Server neu gestartet.');
} catch (e) { } catch (e) {
console.error('Error restarting Language Client:', e); console.error('Fehler beim Neustarten des Language Clients:', e);
vscode.window.showErrorMessage('Failed to restart Language Server.'); vscode.window.showErrorMessage('Neustart des Language Servers fehlgeschlagen Details in der Konsole.');
} }
}); });
context.subscriptions.push(restart); context.subscriptions.push(restart);
@@ -1,49 +1,80 @@
package de.dhbw; package de.dhbw;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.eclipse.lsp4j.*; import org.eclipse.lsp4j.*;
import org.eclipse.lsp4j.services.LanguageClient; import org.eclipse.lsp4j.services.LanguageClient;
import org.eclipse.lsp4j.services.LanguageServer;
import org.eclipse.lsp4j.services.TextDocumentService; import org.eclipse.lsp4j.services.TextDocumentService;
import org.eclipse.lsp4j.services.WorkspaceService; import org.eclipse.lsp4j.services.WorkspaceService;
import org.eclipse.lsp4j.services.LanguageServer;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
/**
*
* Configuration of the Language Server
*
* */
public class JavaTXLanguageServer implements LanguageServer { public class JavaTXLanguageServer implements LanguageServer {
private static final Logger logger = LogManager.getLogger(JavaTXLanguageServer.class);
private LanguageClient client;
public void connect(LanguageClient client) {
this.client = client;
textDocumentService.setClient(client);
}
private final JavaTXTextDocumentService textDocumentService = new JavaTXTextDocumentService(); private final JavaTXTextDocumentService textDocumentService = new JavaTXTextDocumentService();
private final JavaTXWorkspaceService workspaceService = new JavaTXWorkspaceService(); private final JavaTXWorkspaceService workspaceService = new JavaTXWorkspaceService();
public void connect(LanguageClient client) { @Override
textDocumentService.setClient(client); public void setTrace(SetTraceParams params) {
} }
/**
* Configure the Features of the LanguageServer
* */
@Override @Override
public CompletableFuture<InitializeResult> initialize(InitializeParams params) { public CompletableFuture<InitializeResult> initialize(InitializeParams params) {
ServerCapabilities capabilities = new ServerCapabilities(); ServerCapabilities capabilities = new ServerCapabilities();
capabilities.setDocumentFormattingProvider(true); capabilities.setDocumentFormattingProvider(true);
capabilities.setTextDocumentSync(TextDocumentSyncKind.Full); capabilities.setTextDocumentSync(TextDocumentSyncKind.Full);
capabilities.setHoverProvider(false);
capabilities.setInlayHintProvider(true); capabilities.setInlayHintProvider(true);
capabilities.setCodeActionProvider(true); capabilities.setCodeActionProvider(true);
capabilities.setTextDocumentSync(TextDocumentSyncKind.Full);
capabilities.setCompletionProvider(new CompletionOptions(true, List.of())); capabilities.setCompletionProvider(new CompletionOptions(true, List.of()));
capabilities.setWorkspaceSymbolProvider(false);
if (params.getWorkspaceFolders() != null && !params.getWorkspaceFolders().isEmpty()) { if(params.getWorkspaceFolders() != null && !params.getWorkspaceFolders().isEmpty()) {
textDocumentService.setFileRoot(params.getWorkspaceFolders()); textDocumentService.setFileRoot(params.getWorkspaceFolders());
} }
return CompletableFuture.completedFuture(new InitializeResult(capabilities)); return CompletableFuture.supplyAsync(() -> new InitializeResult(capabilities));
} }
/**
* @return the TextDocumentService
* */
@Override @Override
public TextDocumentService getTextDocumentService() { public TextDocumentService getTextDocumentService() {
return textDocumentService; return textDocumentService;
} }
/**
* @return the WorkspaceService
* */
@Override @Override
public WorkspaceService getWorkspaceService() { public WorkspaceService getWorkspaceService() {
return workspaceService; return workspaceService;
} }
/**
* @return the Client
* */
public LanguageClient getClient() {
return client;
}
@Override @Override
public CompletableFuture<Object> shutdown() { public CompletableFuture<Object> shutdown() {
return CompletableFuture.completedFuture(null); return CompletableFuture.completedFuture(null);
@@ -1,15 +1,30 @@
package de.dhbw; package de.dhbw;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.eclipse.lsp4j.launch.LSPLauncher; import org.eclipse.lsp4j.launch.LSPLauncher;
import org.eclipse.lsp4j.services.LanguageClient; import org.eclipse.lsp4j.services.LanguageClient;
/**
*
* Start the JavaTX language Server and use System In and System Out for Communication
*
* */
public class JavaTXLanguageServerLauncher { public class JavaTXLanguageServerLauncher {
public static void main(String[] args) throws Exception { private static final Logger logger = LogManager.getLogger(JavaTXLanguageServerLauncher.class);
JavaTXLanguageServer server = new JavaTXLanguageServer();
var launcher = LSPLauncher.createServerLauncher(server, System.in, System.out); public static void main(String[] args) {
LanguageClient client = launcher.getRemoteProxy(); try {
server.connect(client); JavaTXLanguageServer server = new JavaTXLanguageServer();
launcher.startListening().get(); var launcher = LSPLauncher.createServerLauncher(server, System.in, System.out);
LanguageClient client = launcher.getRemoteProxy();
server.connect(client);
launcher.startListening().get();
} catch (Exception e) {
e.printStackTrace();
}
} }
} }
@@ -1,18 +1,14 @@
package de.dhbw; package de.dhbw;
import de.dhbw.handler.ChangeHandler; import de.dhbw.handler.*;
import de.dhbw.handler.CodeActionHandler; import de.dhbw.service.CacheService;
import de.dhbw.handler.FormattingHandler; import de.dhbw.service.ClientService;
import de.dhbw.handler.SaveHandler; import de.dhbw.service.LogService;
import de.dhbw.helper.CodeSnippetOptions; import de.dhbw.helper.CodeSnippetOptions;
import de.dhbw.helper.ConversionHelper; import de.dhbw.helper.ConversionHelper;
import de.dhbw.helper.TextHelper; import de.dhbw.helper.TextHelper;
import de.dhbw.helper.TypeResolver; import de.dhbw.helper.TypeResolver;
import de.dhbw.model.SnippetWithName; import de.dhbw.model.SnippetWithName;
import de.dhbw.service.CacheService;
import de.dhbw.service.ClientService;
import de.dhbw.service.DocumentStore;
import de.dhbw.service.LogService;
import de.dhbw.service.ParserService; import de.dhbw.service.ParserService;
import org.eclipse.lsp4j.*; import org.eclipse.lsp4j.*;
import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.eclipse.lsp4j.jsonrpc.messages.Either;
@@ -21,95 +17,130 @@ import org.eclipse.lsp4j.services.TextDocumentService;
import java.net.URI; import java.net.URI;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList; import java.util.*;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
public class JavaTXTextDocumentService implements TextDocumentService { /**
* Handles Actions in Documents, such as Autocompletion, Change-Events and Syntax-Checks
*/
public class JavaTXTextDocumentService implements org.eclipse.lsp4j.services.TextDocumentService {
private final FormattingHandler formattingHandler; private final FormattingHandler formattingHandler;
private final SaveHandler saveHandler; private final SaveHandler saveHandler;
private final ChangeHandler changeHandler;
private final CodeActionHandler codeActionHandler;
private final ParserService parserService; private final ParserService parserService;
private final DocumentStore documentStore; private final TypeResolver typeResolver;
private final de.dhbw.service.TextDocumentService textDocumentService;
private final LogService logService; private final LogService logService;
private final ClientService clientService; private final ClientService clientService;
private final CacheService cacheService; private final CacheService cacheService;
private final CodeSnippetOptions codeSnippetOptions; private final ConversionHelper conversionHelper;
private final TypeResolver typeResolver; private final ChangeHandler changeHandler;
private final TextHelper textHelper;
private final CodeActionHandler codeActionHandler;
private LanguageClient client; LanguageClient client;
HashMap<String, String> textDocuments = new HashMap<>();
CodeSnippetOptions codeSnippetOptions = new CodeSnippetOptions();
Boolean dontShowHints = false;
Path fileRoot = null;
Boolean singleFileOpened = false;
public JavaTXTextDocumentService() { public JavaTXTextDocumentService() {
TextHelper textHelper = new TextHelper(); this.textHelper = new TextHelper();
this.cacheService = new CacheService(); this.cacheService = new CacheService();
this.clientService = new ClientService(null); this.clientService = new ClientService(null);
this.typeResolver = new TypeResolver(); this.typeResolver = new TypeResolver();
this.documentStore = new DocumentStore(); this.textDocumentService = new de.dhbw.service.TextDocumentService();
this.conversionHelper = new ConversionHelper(textHelper, documentStore); this.conversionHelper = new ConversionHelper(textHelper, textDocumentService);
this.logService = new LogService(clientService); this.logService = new LogService(clientService);
this.formattingHandler = new FormattingHandler(documentStore); this.formattingHandler = new FormattingHandler(textDocumentService);
this.parserService = new ParserService(conversionHelper); this.parserService = new ParserService(conversionHelper, clientService, cacheService);
this.codeActionHandler = new CodeActionHandler(documentStore, typeResolver, logService, conversionHelper); this.codeActionHandler = new CodeActionHandler(textHelper, textDocumentService, cacheService, typeResolver, logService);
this.saveHandler = new SaveHandler(typeResolver, documentStore, logService, cacheService, conversionHelper, clientService, parserService); this.saveHandler = new SaveHandler(typeResolver, textDocumentService, logService, cacheService, conversionHelper, clientService, parserService);
this.changeHandler = new ChangeHandler(documentStore, parserService, conversionHelper, clientService, typeResolver, cacheService, logService); this.changeHandler = new ChangeHandler(textDocumentService, parserService, conversionHelper, clientService, typeResolver, cacheService, logService);
this.codeSnippetOptions = new CodeSnippetOptions();
} }
private final ConversionHelper conversionHelper;
public void setClient(LanguageClient client) { public void setClient(LanguageClient client) {
this.client = client; this.client = client;
clientService.setClient(client); clientService.setClient(client);
} }
public void setFileRoot(List<WorkspaceFolder> root) { public void setFileRoot(List<WorkspaceFolder> root) {
if (root != null && !root.isEmpty()) {
Path.of(URI.create(root.getFirst().getUri())); if (root == null) {
singleFileOpened = true;
} }
//TODO: Nicht nur das erste Element nehmen sondern alle beachten
fileRoot = Path.of(URI.create(root.get(0).getUri()));
} }
/**
* Handles Completion Events.
* In this case you can select codeSnippets.
*
* @param params the completion Context
* @return the Auto-Completion Items that will be displayed
*/
@Override @Override
public CompletableFuture<Either<List<CompletionItem>, CompletionList>> completion(CompletionParams params) { public CompletableFuture<Either<List<CompletionItem>, CompletionList>> completion(CompletionParams params) {
List<CompletionItem> completions = new ArrayList<>(); List<CompletionItem> completions = new ArrayList<>();
for (SnippetWithName snippet : codeSnippetOptions.getSnippets()) {
CompletionItem item = new CompletionItem(snippet.name());
for (SnippetWithName elem : codeSnippetOptions.getSnippets()) {
CompletionItem item = new CompletionItem(elem.getName());
item.setKind(CompletionItemKind.Snippet); item.setKind(CompletionItemKind.Snippet);
item.setInsertText(snippet.snippet()); item.setInsertText(elem.getSnippet());
item.setInsertTextFormat(InsertTextFormat.Snippet); item.setInsertTextFormat(InsertTextFormat.Snippet);
completions.add(item); completions.add(item);
} }
return CompletableFuture.completedFuture(Either.forLeft(completions)); return CompletableFuture.completedFuture(Either.forLeft(completions));
} }
/**
* Handles didOpen-Event.
* Puts the Text-Document in the text-Document Hashmap
*
* @param params the Context of the newly opened Document
*/
@Override @Override
public void didOpen(DidOpenTextDocumentParams params) { public void didOpen(DidOpenTextDocumentParams params) {
cacheService.reset(); cacheService.reset();
typeResolver.reset(); typeResolver.reset();
String uri = params.getTextDocument().getUri(); List<Diagnostic> syntaxErrors = parserService.getDiagnosticsOfErrors(params.getTextDocument().getText(), params.getTextDocument().getUri());
String text = params.getTextDocument().getText();
List<Diagnostic> syntaxErrors = parserService.getDiagnosticsOfErrors(text, uri);
if (!syntaxErrors.isEmpty()) { if (!syntaxErrors.isEmpty()) {
clientService.publishDiagnostics(uri, syntaxErrors); clientService.publishDiagnostics(params.getTextDocument().getUri(), syntaxErrors);
} }
client.refreshDiagnostics(); client.refreshDiagnostics();
client.refreshInlayHints(); client.refreshInlayHints();
documentStore.saveFile(uri, text); textDocuments.put(params.getTextDocument().getUri(), params.getTextDocument().getText());
textDocumentService.saveFileWithUri(params.getTextDocument().getUri(), params.getTextDocument().getText());
} }
/**
* Handles didChange-Event.
* updates textDocument-State on Server and run Syntax-Check. If an Error is found it will get displayed as a Diagnostic.
*
* @param params the Context of the Changed Document, including the incremental change as well as the whole Document
*/
@Override @Override
public void didChange(DidChangeTextDocumentParams params) { public void didChange(DidChangeTextDocumentParams params) {
logService.log("[didChange] Client triggered didChange event.", MessageType.Info); logService.log("[didChange] Client triggered didChange Event.", MessageType.Info);
cacheService.reset(); cacheService.reset();
String uri = params.getTextDocument().getUri(); String fileInput = textDocumentService.getFileOfUri(params.getTextDocument().getUri());
String fileContent = documentStore.getFileContent(uri); textDocuments.put(params.getTextDocument().getUri(), fileInput);
documentStore.saveFile(uri, fileContent); textDocumentService.saveFileWithUri(params.getTextDocument().getUri(), fileInput);
changeHandler.didChange(params); changeHandler.didChange(params);
} }
/**
* Handles a Formatting-Event
*
* @param params the Context of the Formatting
*/
@Override @Override
public CompletableFuture<List<? extends TextEdit>> formatting(DocumentFormattingParams params) { public CompletableFuture<List<? extends TextEdit>> formatting(DocumentFormattingParams params) {
logService.log("[formatting] Client requested formatting.", MessageType.Info); logService.log("[formatting] Client requested formatting.", MessageType.Info);
@@ -121,24 +152,191 @@ public class JavaTXTextDocumentService implements TextDocumentService {
} }
@Override @Override
public void didSave(DidSaveTextDocumentParams params) { public void didSave(DidSaveTextDocumentParams didSaveTextDocumentParams) {
logService.log("[didSave] Client triggered didSave event."); logService.log("[didSave] Client triggered didSave-Event.");
clientService.startProgress("compile-task", "Inferring types..."); clientService.startLoading("compile-task", "Inferring types...", client);
saveHandler.handleSave(params); saveHandler.asyncHandle(didSaveTextDocumentParams);
clientService.stopProgress("compile-task", "Types successfully inferred"); clientService.stopLoading("compile-task", "Types successfully inferred", client);
clientService.refreshClient(); clientService.updateClient(client);
} }
@Override @Override
public CompletableFuture<List<InlayHint>> inlayHint(InlayHintParams params) { public CompletableFuture<List<InlayHint>> inlayHint(InlayHintParams params) {
String uri = params.getTextDocument().getUri(); logService.log("[inlayHint] The Client requested Inlay-Hints.", MessageType.Info);
List<InlayHint> hints = cacheService.getInlayHintsByUri().get(uri); return CompletableFuture.supplyAsync(() -> dontShowHints ? Collections.emptyList() : cacheService.getGlobalInlayHintMap().get(params.getTextDocument().getUri()) == null ? Collections.emptyList() : cacheService.getGlobalInlayHintMap().get(params.getTextDocument().getUri()));
return CompletableFuture.completedFuture(hints != null ? hints : Collections.emptyList()); }
@Override
public void willSave(WillSaveTextDocumentParams params) {
TextDocumentService.super.willSave(params);
}
@Override
public CompletableFuture<List<TextEdit>> willSaveWaitUntil(WillSaveTextDocumentParams params) {
return CompletableFuture.completedFuture(Collections.emptyList());
}
@Override
public CompletableFuture<List<DocumentLink>> documentLink(DocumentLinkParams params) {
return CompletableFuture.completedFuture(Collections.emptyList());
}
@Override
public CompletableFuture<DocumentLink> documentLinkResolve(DocumentLink params) {
return CompletableFuture.completedFuture(new DocumentLink());
}
@Override
public CompletableFuture<List<ColorInformation>> documentColor(DocumentColorParams params) {
return CompletableFuture.completedFuture(Collections.emptyList());
}
@Override
public CompletableFuture<List<ColorPresentation>> colorPresentation(ColorPresentationParams params) {
return CompletableFuture.completedFuture(Collections.emptyList());
}
@Override
public CompletableFuture<List<FoldingRange>> foldingRange(FoldingRangeRequestParams params) {
return CompletableFuture.completedFuture(Collections.emptyList());
}
@Override
public CompletableFuture<List<CallHierarchyItem>> prepareCallHierarchy(CallHierarchyPrepareParams params) {
return CompletableFuture.completedFuture(Collections.emptyList());
}
@Override
public CompletableFuture<List<CallHierarchyIncomingCall>> callHierarchyIncomingCalls(CallHierarchyIncomingCallsParams params) {
return CompletableFuture.completedFuture(Collections.emptyList());
}
@Override
public CompletableFuture<List<CallHierarchyOutgoingCall>> callHierarchyOutgoingCalls(CallHierarchyOutgoingCallsParams params) {
return CompletableFuture.completedFuture(Collections.emptyList());
}
@Override
public CompletableFuture<List<SelectionRange>> selectionRange(SelectionRangeParams params) {
return CompletableFuture.completedFuture(Collections.emptyList());
}
@Override
public CompletableFuture<SemanticTokens> semanticTokensFull(SemanticTokensParams params) {
return TextDocumentService.super.semanticTokensFull(params);
}
@Override
public CompletableFuture<Either<SemanticTokens, SemanticTokensDelta>> semanticTokensFullDelta(SemanticTokensDeltaParams params) {
return CompletableFuture.completedFuture(Either.forLeft(new SemanticTokens()));
}
@Override
public CompletableFuture<SemanticTokens> semanticTokensRange(SemanticTokensRangeParams params) {
return CompletableFuture.completedFuture(new SemanticTokens());
}
@Override
public CompletableFuture<List<Moniker>> moniker(MonikerParams params) {
return CompletableFuture.completedFuture(Collections.emptyList());
}
@Override
public CompletableFuture<CompletionItem> resolveCompletionItem(CompletionItem unresolved) {
return CompletableFuture.completedFuture(new CompletionItem());
}
@Override
public CompletableFuture<Hover> hover(HoverParams params) {
return CompletableFuture.completedFuture(new Hover());
}
@Override
public CompletableFuture<SignatureHelp> signatureHelp(SignatureHelpParams params) {
return CompletableFuture.completedFuture(new SignatureHelp());
}
@Override
public CompletableFuture<Either<List<? extends Location>, List<? extends LocationLink>>> declaration(DeclarationParams params) {
return CompletableFuture.completedFuture(Either.forLeft(Collections.emptyList()));
}
@Override
public CompletableFuture<Either<List<? extends Location>, List<? extends LocationLink>>> definition(DefinitionParams params) {
return CompletableFuture.completedFuture(Either.forLeft(Collections.emptyList()));
}
@Override
public CompletableFuture<Either<List<? extends Location>, List<? extends LocationLink>>> typeDefinition(TypeDefinitionParams params) {
return CompletableFuture.completedFuture(Either.forLeft(Collections.emptyList()));
}
@Override
public CompletableFuture<Either<List<? extends Location>, List<? extends LocationLink>>> implementation(ImplementationParams params) {
return CompletableFuture.completedFuture(Either.forLeft(Collections.emptyList()));
}
@Override
public CompletableFuture<List<? extends Location>> references(ReferenceParams params) {
return CompletableFuture.completedFuture(Collections.emptyList());
}
@Override
public CompletableFuture<List<? extends DocumentHighlight>> documentHighlight(DocumentHighlightParams params) {
return CompletableFuture.completedFuture(Collections.emptyList());
}
@Override
public CompletableFuture<List<Either<SymbolInformation, DocumentSymbol>>> documentSymbol(DocumentSymbolParams params) {
return CompletableFuture.completedFuture(Collections.emptyList());
} }
@Override @Override
public CompletableFuture<List<Either<Command, CodeAction>>> codeAction(CodeActionParams params) { public CompletableFuture<List<Either<Command, CodeAction>>> codeAction(CodeActionParams params) {
logService.log("[codeAction] Client requested code action.", MessageType.Info);
return CompletableFuture.supplyAsync(() -> codeActionHandler.handleCodeAction(params)); logService.log("[codeAction] Client requested Insert at Line [" + params.getRange().getStart().getLine() + "] and from Char [" + params.getRange().getStart().getCharacter() + "] to [" + params.getRange().getEnd().getCharacter() + "].", MessageType.Info);
logService.log("Code-Action Context was: " + params.getContext().getTriggerKind().name(), MessageType.Info);
return CompletableFuture.supplyAsync(() -> {
return codeActionHandler.handleNewCodeAction(params);
});
} }
}
@Override
public CompletableFuture<CodeAction> resolveCodeAction(CodeAction unresolved) {
return CompletableFuture.completedFuture(new CodeAction());
}
@Override
public CompletableFuture<List<? extends CodeLens>> codeLens(CodeLensParams params) {
return CompletableFuture.completedFuture(Collections.emptyList());
}
@Override
public CompletableFuture<CodeLens> resolveCodeLens(CodeLens unresolved) {
return CompletableFuture.completedFuture(new CodeLens());
}
@Override
public CompletableFuture<List<? extends TextEdit>> rangeFormatting(DocumentRangeFormattingParams params) {
return CompletableFuture.completedFuture(Collections.emptyList());
}
@Override
public CompletableFuture<List<? extends TextEdit>> onTypeFormatting(DocumentOnTypeFormattingParams params) {
return CompletableFuture.completedFuture(Collections.emptyList());
}
@Override
public CompletableFuture<WorkspaceEdit> rename(RenameParams params) {
return CompletableFuture.completedFuture(new WorkspaceEdit());
}
@Override
public CompletableFuture<LinkedEditingRanges> linkedEditingRange(LinkedEditingRangeParams params) {
return CompletableFuture.completedFuture(new LinkedEditingRanges());
}
}
@@ -1,16 +1,25 @@
package de.dhbw; package de.dhbw;
import org.eclipse.lsp4j.DidChangeConfigurationParams; import org.apache.log4j.LogManager;
import org.eclipse.lsp4j.DidChangeWatchedFilesParams; import org.apache.log4j.Logger;
import org.eclipse.lsp4j.*;
import org.eclipse.lsp4j.services.WorkspaceService; import org.eclipse.lsp4j.services.WorkspaceService;
/**
*
* Handles Actions in Workspace
*
* */
public class JavaTXWorkspaceService implements WorkspaceService { public class JavaTXWorkspaceService implements WorkspaceService {
private static final Logger logger = LogManager.getLogger(JavaTXWorkspaceService.class);
@Override @Override
public void didChangeConfiguration(DidChangeConfigurationParams params) { public void didChangeConfiguration(DidChangeConfigurationParams didChangeConfigurationParams) {
} }
@Override @Override
public void didChangeWatchedFiles(DidChangeWatchedFilesParams params) { public void didChangeWatchedFiles(DidChangeWatchedFilesParams params) {
} }
} }
@@ -3,26 +3,24 @@ package de.dhbw.handler;
import com.google.common.base.Stopwatch; import com.google.common.base.Stopwatch;
import de.dhbw.helper.ConversionHelper; import de.dhbw.helper.ConversionHelper;
import de.dhbw.helper.TypeResolver; import de.dhbw.helper.TypeResolver;
import de.dhbw.model.DiagnosticsAndTypehints; import de.dhbw.model.*;
import de.dhbw.model.LSPVariable; import de.dhbw.service.*;
import de.dhbw.service.CacheService; import de.dhbwstuttgart.syntaxtree.factory.NameGenerator;
import de.dhbw.service.ClientService; import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
import de.dhbw.service.DocumentStore; import de.dhbwstuttgart.typeinference.result.PairTPHequalRefTypeOrWildcardType;
import de.dhbw.service.LogService;
import de.dhbw.service.ParserService;
import org.eclipse.lsp4j.*; import org.eclipse.lsp4j.*;
import java.io.BufferedWriter; import java.io.BufferedWriter;
import java.io.File; import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
import java.net.URI; import java.util.*;
import java.nio.file.Path; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.ArrayList; import java.util.concurrent.atomic.AtomicReference;
import java.util.List; import java.util.stream.Collectors;
public class ChangeHandler { public class ChangeHandler {
private final DocumentStore documentStore; private final TextDocumentService textDocumentService;
private final ParserService parserService; private final ParserService parserService;
private final ConversionHelper conversionHelper; private final ConversionHelper conversionHelper;
private final ClientService clientService; private final ClientService clientService;
@@ -30,10 +28,8 @@ public class ChangeHandler {
private final CacheService cacheService; private final CacheService cacheService;
private final LogService logService; private final LogService logService;
public ChangeHandler(DocumentStore documentStore, ParserService parserService, public ChangeHandler(TextDocumentService textDocumentService, ParserService parserService, ConversionHelper conversionHelper, ClientService clientService, TypeResolver typeResolver, CacheService cacheService, LogService logService) {
ConversionHelper conversionHelper, ClientService clientService, this.textDocumentService = textDocumentService;
TypeResolver typeResolver, CacheService cacheService, LogService logService) {
this.documentStore = documentStore;
this.parserService = parserService; this.parserService = parserService;
this.conversionHelper = conversionHelper; this.conversionHelper = conversionHelper;
this.clientService = clientService; this.clientService = clientService;
@@ -42,54 +38,166 @@ public class ChangeHandler {
this.logService = logService; this.logService = logService;
} }
public static String findAddedWord(String str1, String str2) {
int i = 0, j = 0;
// Suche den ersten Unterschied
while (i < str1.length() && j < str2.length() && str1.charAt(i) == str2.charAt(j)) {
i++;
j++;
}
// Falls alles gleich ist, gibt es nichts Neues
if (i == str1.length() && j == str2.length()) {
return null;
}
// Suche ab Ende rückwärts, um den gemeinsamen Suffix zu finden
int iEnd = str1.length() - 1;
int jEnd = str2.length() - 1;
while (iEnd >= i && jEnd >= j && str1.charAt(iEnd) == str2.charAt(jEnd)) {
iEnd--;
jEnd--;
}
// Der Unterschied liegt nun zwischen j und jEnd in str2
return str2.substring(j, jEnd + 1);
}
public void didChange(DidChangeTextDocumentParams params) { public void didChange(DidChangeTextDocumentParams params) {
String uri = params.getTextDocument().getUri(); String currentText = textDocumentService.getFileOfUri(params.getTextDocument().getUri());
StringBuilder content = new StringBuilder(); AtomicReference<String> summedUp = new AtomicReference<>("");
params.getContentChanges().forEach(change -> content.append(change.getText())); params.getContentChanges().forEach(el -> summedUp.set(summedUp.get() + el.getText()));
String input = content.toString(); textDocumentService.saveFileWithUri(params.getTextDocument().getUri(), summedUp.get());
documentStore.saveFile(uri, input);
List<Diagnostic> syntaxErrors = parserService.getDiagnosticsOfErrors(input, uri); List<Diagnostic> syntaxErrors = parserService.getDiagnosticsOfErrors(summedUp.get(), params.getTextDocument().getUri());
if (!syntaxErrors.isEmpty()) { if (!syntaxErrors.isEmpty()) {
clientService.publishDiagnostics(uri, syntaxErrors); clientService.publishDiagnostics(params.getTextDocument().getUri(), syntaxErrors);
return;
} }
clientService.refreshClient(); logService.log("Found [" + syntaxErrors.size() + "] Syntax Errors in Document.");
inferAndPublish(uri, input); if (syntaxErrors.isEmpty()) {
}
private void inferAndPublish(String uri, String input) { String type = findAddedWord(currentText, summedUp.get());
Stopwatch stopwatch = Stopwatch.createStarted(); String input = summedUp.get();
File tempFile = null; logService.log("Variables are: " + cacheService.getVariables().size());
try {
File tempDir = Path.of(new URI(uri)).getParent().toFile();
tempFile = File.createTempFile("jtx_temp", ".tmp", tempDir);
try (BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile))) { boolean shouldCalculate = false;
writer.write(input);
logService.log("Type is here: " + type);
for (LSPVariable variable : cacheService.getVariables()) {
for (Type variableType : variable.getPossibleTypes()) {
logService.log(variableType.getType() + " <> "+ type.replaceAll(" ", "") + ": " + type.replaceAll(" ", "").contains(variableType.getType().replaceAll(" ", "")));
shouldCalculate = shouldCalculate || type.replaceAll(" ", "").contains(variableType.getType().replaceAll(" ", ""));
if (shouldCalculate) {
break;
}
}
} }
List<LSPVariable> variables = typeResolver.inferTypes(tempFile.toURI().toString(), input); logService.log("Should Calculate is: " + shouldCalculate);
cacheService.setVariables(variables);
DiagnosticsAndTypehints result = conversionHelper.variablesToDiagnosticsAndTypehints(variables, input); if (false) {
cacheService.updateGlobalMaps(result.diagnostics(), result.inlayHints(), uri); try {
clientService.publishDiagnostics(uri, new ArrayList<>(result.diagnostics())); File tempDir = new File(System.getProperty("java.io.tmpdir"));
File tempFile = File.createTempFile("newText", ".tmp", tempDir);
FileWriter fileWriter = new FileWriter(tempFile, true);
System.out.println(tempFile.getAbsolutePath());
BufferedWriter bw = new BufferedWriter(fileWriter);
bw.write(summedUp.get());
bw.close();
typeResolver.updateAst(tempFile.toURI().getPath());
tempFile.delete();
} catch (Exception e) {
logService.log(e.getMessage());
}
var sWatch = Stopwatch.createUnstarted();
sWatch.start();
try {
ArrayList<LSPVariable> typesOfMethodAndParameters = typeResolver.infereChangeInput();
DiagnosticsAndTypehints diagnosticsAndTypehints = conversionHelper.variablesToDiagnosticsAndTypehints(typesOfMethodAndParameters, params.getTextDocument().getUri());
List<InlayHint> typeHint = diagnosticsAndTypehints.getInlayHints();
List<Diagnostic> diagnostics = diagnosticsAndTypehints.getDiagnostics();
cacheService.updateGlobalMaps(diagnostics, typeHint, params.getTextDocument().getUri());
} catch (Exception e) {
logService.log("[didChange] Error trying to get Inlay-Hints and Diagnostics for Client: " + e.getMessage(), MessageType.Error);
for (var stackTrace : e.getStackTrace()) {
logService.log(stackTrace.toString());
}
clientService.showMessage(MessageType.Error, e.getMessage());
cacheService.updateGlobalMaps(new ArrayList<>(), new ArrayList<>(), params.getTextDocument().getUri());
} finally {
sWatch.stop();
logService.log("[didChange] Finished Calculating in [" + sWatch.elapsed().toSeconds() + "s]", MessageType.Info);
}
List<Diagnostic> typeDiagnostics = cacheService.getGlobalDiagnosticsMap().get(params.getTextDocument().getUri());
ArrayList<Diagnostic> allDiagnostics = new ArrayList<>(typeDiagnostics);
clientService.publishDiagnostics(params.getTextDocument().getUri(), allDiagnostics);
} else {
logService.log("Calculating again.");
var sWatch = Stopwatch.createUnstarted();
sWatch.start();
try {
try {
File tempDir2 = new File(System.getProperty("java.io.tmpdir"));
File tempFile2 = File.createTempFile("newText", ".tmp", tempDir2);
FileWriter fileWriter = new FileWriter(tempFile2, true);
BufferedWriter bw = new BufferedWriter(fileWriter);
bw.write(input);
bw.close();
ArrayList<LSPVariable> variables = typeResolver.infereInput(tempFile2.toURI().toString(), input, false);
cacheService.setVariables(variables);
DiagnosticsAndTypehints diagnosticsAndTypehints = conversionHelper.variablesToDiagnosticsAndTypehintsWithInput(variables, input);
List<InlayHint> typeHint = diagnosticsAndTypehints.getInlayHints();
List<Diagnostic> diagnostics = diagnosticsAndTypehints.getDiagnostics();
cacheService.updateGlobalMaps(diagnostics, typeHint, params.getTextDocument().getUri());
List<Diagnostic> allDiagnostics = new ArrayList<>(diagnostics);
allDiagnostics.addAll(parserService.getDiagnosticsOfErrors(input, params.getTextDocument().getUri()));
clientService.publishDiagnostics(params.getTextDocument().getUri(), allDiagnostics);
tempFile2.delete();
} catch (Exception e) {
logService.log("[didSave] Error trying to get Inlay-Hints and Diagnostics for Client: " + e.getMessage(), MessageType.Error);
for (StackTraceElement elem : e.getStackTrace()) {
logService.log(elem.toString());
}
clientService.showMessage(MessageType.Error, e.getMessage() == null ? "null" : e.getMessage());
cacheService.updateGlobalMaps(new ArrayList<>(), new ArrayList<>(), params.getTextDocument().getUri());
} finally {
sWatch.stop();
logService.log("[didSave] Finished Calculating in [" + sWatch.elapsed().toSeconds() + "s]", MessageType.Info);
}
} catch (Exception e) {
logService.log(e.getMessage());
}
} catch (Exception | VerifyError e) {
logService.log("[didChange] Error: " + e.getMessage(), MessageType.Error);
clientService.showMessage(MessageType.Error, e.getMessage() != null ? e.getMessage() : "Unknown error");
cacheService.updateGlobalMaps(List.of(), List.of(), uri);
} finally {
if (tempFile != null) {
tempFile.delete();
} }
stopwatch.stop();
logService.log("[didChange] Completed in " + stopwatch.elapsed().toSeconds() + "s", MessageType.Info);
} }
} }
} }
@@ -1,9 +1,10 @@
package de.dhbw.handler; package de.dhbw.handler;
import de.dhbw.helper.ConversionHelper; import de.dhbw.helper.TextHelper;
import de.dhbw.helper.TypeResolver; import de.dhbw.helper.TypeResolver;
import de.dhbw.service.DocumentStore; import de.dhbw.service.CacheService;
import de.dhbw.service.LogService; import de.dhbw.service.LogService;
import de.dhbw.service.TextDocumentService;
import de.dhbw.model.PlaceholderVariable; import de.dhbw.model.PlaceholderVariable;
import org.eclipse.lsp4j.*; import org.eclipse.lsp4j.*;
import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.eclipse.lsp4j.jsonrpc.messages.Either;
@@ -16,111 +17,201 @@ import java.util.stream.Collectors;
public class CodeActionHandler { public class CodeActionHandler {
private final DocumentStore documentStore; private final TextHelper textHelper;
private final TextDocumentService textDocumentService;
private final CacheService cacheService;
private final TypeResolver typeResolver; private final TypeResolver typeResolver;
private final LogService logService; private final LogService logService;
private final ConversionHelper conversionHelper;
public CodeActionHandler(DocumentStore documentStore, TypeResolver typeResolver, public CodeActionHandler(TextHelper textHelper, TextDocumentService textDocumentService, CacheService cacheService, TypeResolver typeResolver, LogService logService) {
LogService logService, ConversionHelper conversionHelper) { this.textHelper = textHelper;
this.documentStore = documentStore; this.textDocumentService = textDocumentService;
this.cacheService = cacheService;
this.typeResolver = typeResolver; this.typeResolver = typeResolver;
this.logService = logService; this.logService = logService;
this.conversionHelper = conversionHelper;
} }
public List<Either<Command, CodeAction>> handleCodeAction(CodeActionParams params) { public static Range wholeDocumentRange(String text) {
if (typeResolver.getInserts() == null) {
return List.of();
}
String uri = params.getTextDocument().getUri();
Range range = params.getRange();
Map<String, List<PlaceholderVariable>> overlapping = getOverlapping(
typeResolver.getInserts(),
range.getStart().getLine() + 1,
range.getStart().getCharacter(),
range.getEnd().getCharacter()
);
List<Either<Command, CodeAction>> actions = new ArrayList<>();
List<String> addedTitles = new ArrayList<>();
for (List<PlaceholderVariable> placeholders : overlapping.values()) {
for (PlaceholderVariable placeholder : placeholders) {
try {
String currentContent = documentStore.getFileContent(uri);
String inserted = placeholder.insert(currentContent);
String title = "Insert " + conversionHelper.cleanType(placeholder.getInsertString());
if (addedTitles.contains(title)) {
continue;
}
TextEdit edit = new TextEdit(wholeDocumentRange(currentContent), inserted);
WorkspaceEdit workspaceEdit = new WorkspaceEdit();
workspaceEdit.setChanges(Map.of(uri, List.of(edit)));
CodeAction action = new CodeAction(title);
action.setKind(CodeActionKind.QuickFix);
action.setEdit(workspaceEdit);
actions.add(Either.forRight(action));
addedTitles.add(title);
} catch (Exception e) {
logService.log("Error creating code action: " + e.getMessage(), MessageType.Error);
}
}
}
return actions;
}
static Range wholeDocumentRange(String text) {
if (text == null || text.isEmpty()) { if (text == null || text.isEmpty()) {
return new Range(new Position(0, 0), new Position(0, 0)); return new Range(new Position(0, 0), new Position(0, 0));
} }
int lastLine = 0; int lastLine = 0;
int lastBreak = -1; int lastBreak = -1; // Index des letzten Zeilenumbruchszeichens, das zum Zeilenende gehört
for (int i = 0; i < text.length(); i++) { final int n = text.length();
for (int i = 0; i < n; i++) {
char c = text.charAt(i); char c = text.charAt(i);
if (c == '\n') { if (c == '\n') {
lastLine++; lastLine++;
lastBreak = i; lastBreak = i;
} else if (c == '\r') { } else if (c == '\r') {
lastLine++; // Unterscheide \r\n von alleine stehendem \r
if (i + 1 < text.length() && text.charAt(i + 1) == '\n') { if (i + 1 < n && text.charAt(i + 1) == '\n') {
lastLine++;
lastBreak = i + 1; lastBreak = i + 1;
i++; i++; // \n überspringen
} else { } else {
lastLine++;
lastBreak = i; lastBreak = i;
} }
} }
} }
int endChar = text.length() - (lastBreak + 1); int lastLineStart = lastBreak + 1;
int endChar = n - lastLineStart; // Länge der letzten Zeile in UTF-16 Code Units
return new Range(new Position(0, 0), new Position(lastLine, endChar)); return new Range(new Position(0, 0), new Position(lastLine, endChar));
} }
private static <V> Map<String, V> getOverlapping(Map<String, V> map, int line,
int startChar, int endChar) { public static <V> Map<String, V> getOverlapping(
int s = Math.min(startChar, endChar); Map<String, V> map,
int e = Math.max(startChar, endChar); int line,
int startChar,
int endChar
) {
if (startChar > endChar) {
int t = startChar;
startChar = endChar;
endChar = t;
}
final int s = startChar, e = endChar;
return map.entrySet().stream() return map.entrySet().stream()
.filter(entry -> { .filter(entry -> {
String[] parts = entry.getKey().split("\\s+"); String key = entry.getKey();
String[] parts = key.split("\\s+");
if (parts.length != 2) return false; if (parts.length != 2) return false;
try { try {
int kLine = Integer.parseInt(parts[0]); int kLine = Integer.parseInt(parts[0]);
int kChar = Integer.parseInt(parts[1]); int kChar = Integer.parseInt(parts[1]);
return kLine == line && kChar >= s && kChar <= e; return kLine == line && kChar >= s && kChar <= e;
} catch (NumberFormatException ex) { } catch (NumberFormatException ex) {
// Key nicht im erwarteten Format
return false; return false;
} }
}) })
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
} }
public List<Either<Command, CodeAction>> handleNewCodeAction(CodeActionParams params) {
String documentUri = params.getTextDocument().getUri();
Range rangeOfInsert = params.getRange();
//All Diagnostics that are in range of the hover -> All Diagnostics of the selected Variable and thus all Types of the Variable
Map<String, List<PlaceholderVariable>> typeInsertsOverlapping = getOverlapping(typeResolver.getInserts(), rangeOfInsert.getStart().getLine()+1, rangeOfInsert.getStart().getCharacter(), rangeOfInsert.getEnd().getCharacter());
logService.log("Inserts are:");
typeResolver.getInserts().forEach((key, value) -> logService.log(key));
logService.log("Size is: " + typeInsertsOverlapping.size());
logService.log("Range is: " + rangeOfInsert.getStart().getLine() + " -> " + rangeOfInsert.getStart().getCharacter() + " - " + rangeOfInsert.getEnd().getCharacter());
List<Either<Command, CodeAction>> actions = new ArrayList<>();
for (var typeInsertList : typeInsertsOverlapping.values()) {
for (var typeInsert : typeInsertList) {
try {
String typeWithReplacedVariable = typeInsert.insert(textDocumentService.getFileOfUri(documentUri));
ArrayList<TextEdit> listOfChanges = new ArrayList<>();
listOfChanges.add(new TextEdit(wholeDocumentRange(textDocumentService.getFileOfUri(documentUri)), typeWithReplacedVariable));
var isTypeImported = false;
Map<String, List<TextEdit>> changes = new HashMap<>();
changes.put(documentUri, listOfChanges);
WorkspaceEdit edit = new WorkspaceEdit();
edit.setChanges(changes);
CodeAction action = new CodeAction("Insert " + typeInsert.getInsertString());
action.setKind(CodeActionKind.QuickFix);
action.setEdit(edit);
actions.add(Either.forRight(action));
} catch (Exception e) {
logService.log("Error creating Actions, returning empty List. The Error was: " + e.getMessage(), MessageType.Error);
}
}
}
return actions;
}
public List<Either<Command, CodeAction>> handleCodeAction(CodeActionParams params) {
String documentUri = params.getTextDocument().getUri();
List<Diagnostic> diagnosticInCurrentDocument = params.getContext().getDiagnostics();
Range rangeOfInsert = params.getRange();
//All Diagnostics that are in range of the hover -> All Diagnostics of the selected Variable and thus all Types of the Variable
List<Diagnostic> diagnosticsOverlappingHover = diagnosticInCurrentDocument.stream()
.filter(diagnostic -> textDocumentService.rangesOverlap(diagnostic.getRange(), rangeOfInsert)).toList();
List<Either<Command, CodeAction>> actions = new ArrayList<>();
for (Diagnostic typeDiagnostic : diagnosticsOverlappingHover) {
try {
String typeWithReplacedVariable = typeDiagnostic.getMessage() +
" " +
textHelper.getTextOfChars(
textDocumentService.getFileOfUri(documentUri),
rangeOfInsert.getStart().getLine(),
rangeOfInsert.getStart().getCharacter(),
rangeOfInsert.getEnd().getCharacter()
);
ArrayList<TextEdit> listOfChanges = new ArrayList<>();
if (typeDiagnostic.getCode().getLeft().equals("GENERIC")) {
for (var diagnostic : cacheService.getGlobalDiagnosticsMap().get(documentUri)) {
if (diagnostic.getCode().getLeft().equals("GENERIC")) {
String genericType = diagnostic.getMessage() +
" " +
textHelper.getTextOfChars(
textDocumentService.getFileOfUri(params.getTextDocument().getUri()),
diagnostic.getRange().getStart().getLine(),
diagnostic.getRange().getStart().getCharacter(),
diagnostic.getRange().getEnd().getCharacter()
);
listOfChanges.add(new TextEdit(diagnostic.getRange(), genericType));
}
}
} else {
listOfChanges.add(new TextEdit(rangeOfInsert, typeWithReplacedVariable));
}
var isTypeImported = false;
if (!typeDiagnostic.getCode().getLeft().equalsIgnoreCase("generic")) {
isTypeImported = typeResolver.isTypeImported(textDocumentService.getFileOfUri(params.getTextDocument().getUri()), typeDiagnostic.getMessage());
}
if (!isTypeImported && !typeDiagnostic.getMessage().equals("void") && !typeDiagnostic.getCode().getLeft().equals("GENERIC")) {
Range importRange = new Range(new Position(0, 0), new Position(0, 0));
var typeWithoutGenerics = typeDiagnostic.getMessage().contains("<") ? typeDiagnostic.getMessage().substring(0, typeDiagnostic.getMessage().indexOf('<')) : typeDiagnostic.getMessage();
listOfChanges.add(new TextEdit(importRange, "import " + typeWithoutGenerics + ";\n"));
}
Map<String, List<TextEdit>> changes = new HashMap<>();
changes.put(documentUri, listOfChanges);
WorkspaceEdit edit = new WorkspaceEdit();
edit.setChanges(changes);
CodeAction action = new CodeAction("Insert " + typeDiagnostic.getMessage());
action.setKind(CodeActionKind.QuickFix);
action.setEdit(edit);
actions.add(Either.forRight(action));
} catch (Exception e) {
logService.log("Error creating Actions, returning empty List. The Error was: " + e.getMessage(), MessageType.Error);
}
}
return actions;
}
} }
@@ -1,34 +1,38 @@
package de.dhbw.handler; package de.dhbw.handler;
import de.dhbw.service.DocumentStore; import de.dhbw.service.TextDocumentService;
import org.eclipse.lsp4j.DocumentFormattingParams; import org.eclipse.lsp4j.DocumentFormattingParams;
import org.eclipse.lsp4j.Position; import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.TextEdit; import org.eclipse.lsp4j.TextEdit;
import java.util.ArrayList;
import java.util.List; import java.util.List;
public class FormattingHandler { public class FormattingHandler {
private final DocumentStore documentStore; private final TextDocumentService textDocumentService;
public FormattingHandler(DocumentStore documentStore) { public FormattingHandler(TextDocumentService textDocumentService) {
this.documentStore = documentStore; this.textDocumentService = textDocumentService;
} }
public List<TextEdit> handleFormat(DocumentFormattingParams params) { public List<TextEdit> handleFormat(DocumentFormattingParams params){
String content = documentStore.getFileContent(params.getTextDocument().getUri()); List<TextEdit> edits = new ArrayList<>();
String[] lines = content.split("\n");
String[] lines = textDocumentService.getFileOfUri(params.getTextDocument().getUri()).split("\n");
StringBuilder formattedText = new StringBuilder();
StringBuilder formatted = new StringBuilder();
for (String line : lines) { for (String line : lines) {
formatted.append(line.stripTrailing()).append("\n"); formattedText.append(line.stripTrailing()).append("\n");
} }
TextEdit edit = new TextEdit( TextEdit edit = new TextEdit();
new Range(new Position(0, 0), new Position(lines.length, 0)), edit.setRange(new Range(new Position(0, 0), new Position(lines.length, 0)));
formatted.toString().stripTrailing() edit.setNewText(formattedText.toString().trim());
);
return List.of(edit); edits.add(edit);
return edits;
} }
} }
@@ -1,35 +1,45 @@
package de.dhbw.handler; package de.dhbw.handler;
import com.google.common.base.Stopwatch; import com.google.common.base.Stopwatch;
import de.dhbw.helper.ResultListener;
import de.dhbw.model.DiagnosticsAndTypehints; import de.dhbw.model.DiagnosticsAndTypehints;
import de.dhbw.model.LSPVariable; import de.dhbw.service.*;
import de.dhbw.service.CacheService;
import de.dhbw.service.ClientService;
import de.dhbw.service.DocumentStore;
import de.dhbw.service.LogService;
import de.dhbw.service.ParserService;
import de.dhbw.helper.ConversionHelper; import de.dhbw.helper.ConversionHelper;
import de.dhbw.helper.TypeResolver; import de.dhbw.helper.TypeResolver;
import de.dhbw.model.LSPVariable;
import de.dhbwstuttgart.core.JavaTXCompiler;
import de.dhbwstuttgart.languageServerInterface.LanguageServerInterface;
import de.dhbwstuttgart.languageServerInterface.model.ParserError;
import de.dhbwstuttgart.syntaxtree.SourceFile;
import de.dhbwstuttgart.target.generate.GenerateGenerics;
import de.dhbwstuttgart.target.generate.GenericsResult;
import de.dhbwstuttgart.typeinference.result.ResultSet;
import org.apache.commons.io.output.NullOutputStream;
import org.eclipse.lsp4j.*; import org.eclipse.lsp4j.*;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public class SaveHandler { public class SaveHandler {
private final TypeResolver typeResolver; private final TypeResolver typeResolver;
private final DocumentStore documentStore; private final TextDocumentService textDocumentService;
private final LogService logService; private final LogService logService;
private final CacheService cacheService; private final CacheService cacheService;
private final ConversionHelper conversionHelper; private final ConversionHelper conversionHelper;
private final ClientService clientService; private final ClientService clientService;
private final ParserService parserService; private final ParserService parserService;
public SaveHandler(TypeResolver typeResolver, DocumentStore documentStore, LogService logService, public SaveHandler(TypeResolver typeResolver, TextDocumentService textDocumentService, LogService logService, CacheService cacheService, ConversionHelper conversionHelper, ClientService clientService, ParserService parserService) {
CacheService cacheService, ConversionHelper conversionHelper,
ClientService clientService, ParserService parserService) {
this.typeResolver = typeResolver; this.typeResolver = typeResolver;
this.documentStore = documentStore; this.textDocumentService = textDocumentService;
this.logService = logService; this.logService = logService;
this.cacheService = cacheService; this.cacheService = cacheService;
this.conversionHelper = conversionHelper; this.conversionHelper = conversionHelper;
@@ -37,44 +47,85 @@ public class SaveHandler {
this.parserService = parserService; this.parserService = parserService;
} }
public void handleSave(DidSaveTextDocumentParams params) { public void handleSave(DidSaveTextDocumentParams didSaveTextDocumentParams) {
cacheService.reset(); cacheService.reset();
typeResolver.reset(); typeResolver.reset();
Stopwatch stopwatch = Stopwatch.createStarted(); var sWatch = Stopwatch.createUnstarted();
sWatch.start();
String uri = params.getTextDocument().getUri();
try { try {
String fileContent = documentStore.getFileContent(uri);
List<Diagnostic> syntaxErrors = parserService.getDiagnosticsOfErrors(fileContent, uri); String fileInput = textDocumentService.getFileOfUri(didSaveTextDocumentParams.getTextDocument().getUri());
List<Diagnostic> syntaxErrors = parserService.getDiagnosticsOfErrors(fileInput, didSaveTextDocumentParams.getTextDocument().getUri());
if (!syntaxErrors.isEmpty()) { if (!syntaxErrors.isEmpty()) {
clientService.publishDiagnostics(uri, syntaxErrors); clientService.publishDiagnostics(didSaveTextDocumentParams.getTextDocument().getUri(), syntaxErrors);
return;
} }
clientService.refreshClient(); logService.log("Found [" + syntaxErrors.size() + "] Syntax Errors in Document.");
cacheService.getLastSavedFiles().put(uri, fileContent); if (syntaxErrors.isEmpty()) {
cacheService.getLastSavedFiles().put(didSaveTextDocumentParams.getTextDocument().getUri(), fileInput);
//typeResolver.getCompilerInput(didSaveTextDocumentParams.getTextDocument().getUri(), fileInput);
if (fileInput == null) {
logService.log("[didSave] Input of Text Document is null in TextDocument-Hashmap.", MessageType.Error);
}
ArrayList<LSPVariable> variables = typeResolver.infereInput(didSaveTextDocumentParams.getTextDocument().getUri(), fileInput, false);
cacheService.setVariables(variables);
DiagnosticsAndTypehints diagnosticsAndTypehints = conversionHelper.variablesToDiagnosticsAndTypehints(variables, didSaveTextDocumentParams.getTextDocument().getUri());
List<InlayHint> typeHint = diagnosticsAndTypehints.getInlayHints();
List<Diagnostic> diagnostics = diagnosticsAndTypehints.getDiagnostics();
cacheService.updateGlobalMaps(diagnostics, typeHint, didSaveTextDocumentParams.getTextDocument().getUri());
List<Diagnostic> allDiagnostics = new ArrayList<>(diagnostics);
clientService.publishDiagnostics(didSaveTextDocumentParams.getTextDocument().getUri(), allDiagnostics);
if (fileContent == null) {
logService.log("[didSave] File content is null.", MessageType.Error);
return;
} }
List<LSPVariable> variables = typeResolver.inferTypes(uri, fileContent); } catch (Exception e) {
cacheService.setVariables(variables); logService.log("[didSave] Error trying to get Inlay-Hints and Diagnostics for Client: " + e.getMessage(), MessageType.Error);
DiagnosticsAndTypehints result = conversionHelper.variablesToDiagnosticsAndTypehints(variables, fileContent); for (StackTraceElement elem : e.getStackTrace()) {
cacheService.updateGlobalMaps(result.diagnostics(), result.inlayHints(), uri); logService.log(elem.toString());
clientService.publishDiagnostics(uri, new ArrayList<>(result.diagnostics())); }
clientService.showMessage(MessageType.Error, e.getMessage() == null ? "null" : e.getMessage());
cacheService.updateGlobalMaps(new ArrayList<>(), new ArrayList<>(), didSaveTextDocumentParams.getTextDocument().getUri());
} catch (Exception | VerifyError e) {
logService.log("[didSave] Error: " + e.getMessage(), MessageType.Error);
clientService.showMessage(MessageType.Error, e.getMessage() != null ? e.getMessage() : "Unknown error");
cacheService.updateGlobalMaps(List.of(), List.of(), uri);
} finally { } finally {
stopwatch.stop(); sWatch.stop();
logService.log("[didSave] Completed in " + stopwatch.elapsed().toSeconds() + "s", MessageType.Info); logService.log("[didSave] Finished Calculating in [" + sWatch.elapsed().toSeconds() + "s]", MessageType.Info);
}
}
public void asyncHandle(DidSaveTextDocumentParams didSaveTextDocumentParams) {
var sWatch = Stopwatch.createUnstarted();
sWatch.start();
try {
ResultListener resultListener = new ResultListener(typeResolver, didSaveTextDocumentParams, cacheService, conversionHelper, clientService);
resultListener.init(didSaveTextDocumentParams);
} catch (Exception e) {
logService.log("[didSave] Error trying to get Inlay-Hints and Diagnostics for Client: " + e.getMessage(), MessageType.Error);
for (StackTraceElement elem : e.getStackTrace()) {
logService.log(elem.toString());
}
clientService.showMessage(MessageType.Error, e.getMessage() == null ? "null" : e.getMessage());
cacheService.updateGlobalMaps(new ArrayList<>(), new ArrayList<>(), didSaveTextDocumentParams.getTextDocument().getUri());
} }
} }
} }
@@ -6,85 +6,93 @@ import de.dhbw.model.PlaceholderVariable;
import de.dhbwstuttgart.syntaxtree.ClassOrInterface; import de.dhbwstuttgart.syntaxtree.ClassOrInterface;
import de.dhbwstuttgart.syntaxtree.Method; import de.dhbwstuttgart.syntaxtree.Method;
import de.dhbwstuttgart.syntaxtree.SourceFile; import de.dhbwstuttgart.syntaxtree.SourceFile;
import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric; import de.dhbwstuttgart.syntaxtree.type.*;
import de.dhbwstuttgart.target.generate.GenerateGenerics; import de.dhbwstuttgart.target.generate.GenerateGenerics;
import de.dhbwstuttgart.target.generate.GenericsResult; import de.dhbwstuttgart.target.generate.GenericsResult;
import de.dhbwstuttgart.target.generate.GenericsResultSet; import de.dhbwstuttgart.target.generate.GenericsResultSet;
import de.dhbwstuttgart.typeinference.result.ResolvedType;
import de.dhbwstuttgart.typeinference.result.ResultSet; import de.dhbwstuttgart.typeinference.result.*;
import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.Token;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.StringJoiner;
public class ASTTransformationHelper { public class ASTTransformationHelper {
private ASTTransformationHelper() {} public static Set<PlaceholderVariable> createTypeInsertPoints(SourceFile forSourcefile, ResultSet withResults, GenericsResult generics) {
return new PlaceholderPlacer().getTypeInserts(forSourcefile, withResults, generics);
public static Set<PlaceholderVariable> createTypeInsertPoints(SourceFile source, ResultSet results,
GenericsResult generics) {
return new PlaceholderPlacer().getTypeInserts(source, results, generics);
} }
public static PlaceholderVariable createInsertPoints(RefTypeOrTPHOrWildcardOrGeneric type, Token offset, public static PlaceholderVariable createInsertPoints(RefTypeOrTPHOrWildcardOrGeneric type, Token offset, ClassOrInterface cl, Method m,
ClassOrInterface cl, Method m, ResultSet resultSet, ResultSet resultSet, GenericsResultSet constraints, GenericsResultSet classConstraints) {
GenericsResultSet constraints,
GenericsResultSet classConstraints) { ResolvedType resolvedType = resultSet.resolveType(type);
ResolvedType resolvedType = resultSet.resolveType(type); PlaceholderPoint insertPoint = new PlaceholderPoint(offset,
PlaceholderPoint insertPoint = new PlaceholderPoint( new PlaceholderToInsertString(resolvedType.resolvedType, constraints, classConstraints).insert, PlaceholderType.NORMAL_INSERT);
offset, return new PlaceholderVariable(insertPoint, createGenericInsert(constraints, classConstraints, cl, m, resultSet, offset), resolvedType.getResultPair());
new PlaceholderToInsertString(resolvedType.resolvedType, constraints, classConstraints).insert,
PlaceholderType.NORMAL_INSERT
);
Set<PlaceholderPoint> genericInserts = createGenericInserts(constraints, classConstraints, cl, m, resultSet, offset);
return new PlaceholderVariable(insertPoint, genericInserts, resolvedType.getResultPair());
} }
private static synchronized Set<PlaceholderPoint> createGenericInserts(GenericsResultSet methodConstraints, private static synchronized Set<PlaceholderPoint> createGenericInsert(GenericsResultSet methodConstraints, GenericsResultSet classConstraints, ClassOrInterface cl, Method m, ResultSet resultSet, Token mOffset){
GenericsResultSet classConstraints,
ClassOrInterface cl, Method m,
ResultSet resultSet, Token mOffset) {
Set<PlaceholderPoint> result = createGenericClassInserts(classConstraints, cl); Set<PlaceholderPoint> result = createGenericClassInserts(classConstraints, cl);
if (m != null) {
Token offset = m.getOffset() != null ? m.getOffset() : mOffset; if (m != null) {
result.addAll(createMethodConstraints(methodConstraints, offset)); result.addAll(createMethodConstraints(methodConstraints, m.getOffset() != null ? m.getOffset() : mOffset));
} }
return result; return result;
} }
private static Set<PlaceholderPoint> createMethodConstraints(GenericsResultSet constraints, Token mOffset) {
Set<PlaceholderPoint> result = new HashSet<>();
Token offset = mOffset;
private static Set<PlaceholderPoint> createMethodConstraints(GenericsResultSet constraints, Token offset) { if (constraints.size() == 0) {
if (constraints == null || constraints.size() == 0) { return result;
return new HashSet<>(); }
String insert = " <";
for (var genericInsertConstraint : constraints) {
if (genericInsertConstraint instanceof GenerateGenerics.PairEQ peq) {
insert += peq.left.resolve().getName();
} else if (genericInsertConstraint instanceof GenerateGenerics.PairLT psm) {
insert += psm.left.resolve().getName() + " extends " + psm.right.resolve().getName();
}
insert += ", ";
} }
insert = insert.substring(0, insert.length() -2);
insert += ">";
String insert = buildGenericString(constraints); result.add(new PlaceholderPoint(offset, insert, PlaceholderType.GENERERIC_METHOD_INSERT));
Set<PlaceholderPoint> result = new HashSet<>(); return result;
result.add(new PlaceholderPoint(offset, insert, PlaceholderType.GENERIC_METHOD_INSERT));
return result;
} }
private static Set<PlaceholderPoint> createGenericClassInserts(GenericsResultSet classConstraints, private static Set<PlaceholderPoint> createGenericClassInserts(GenericsResultSet classConstraints, ClassOrInterface cl) {
ClassOrInterface cl) { Set<PlaceholderPoint> result = new HashSet<>();
if (classConstraints == null || classConstraints.size() == 0) { Token offset = cl.getGenerics().getOffset();
return new HashSet<>();
}
String insert = buildGenericString(classConstraints); if (classConstraints == null || classConstraints.size() == 0) {
Set<PlaceholderPoint> result = new HashSet<>(); return result;
result.add(new PlaceholderPoint(cl.getGenerics().getOffset(), insert, PlaceholderType.GENERIC_CLASS_INSERT)); }
return result;
} String insert = " <";
private static String buildGenericString(GenericsResultSet constraints) { for (var genericInsertConstraint : classConstraints) {
StringJoiner joiner = new StringJoiner(", ", " <", ">"); if (genericInsertConstraint instanceof GenerateGenerics.PairEQ peq) {
for (var constraint : constraints) { insert += peq.left.resolve().getName();
if (constraint instanceof GenerateGenerics.PairEQ peq) { } else if (genericInsertConstraint instanceof GenerateGenerics.PairLT psm) {
joiner.add(peq.left.resolve().getName()); insert += psm.left.resolve().getName() + " extends " + psm.right.resolve().getName();
} else if (constraint instanceof GenerateGenerics.PairLT psm) {
joiner.add(psm.left.resolve().getName() + " extends " + psm.right.resolve().getName());
} }
insert += ", ";
} }
return joiner.toString();
} insert = insert.substring(0, insert.length() -2);
insert += ">";
result.add(new PlaceholderPoint(offset, insert, PlaceholderType.GENERIC_CLASS_INSERT));
return result;
}
} }
@@ -2,17 +2,34 @@ package de.dhbw.helper;
import de.dhbw.model.SnippetWithName; import de.dhbw.model.SnippetWithName;
import java.util.List; import java.util.ArrayList;
/**
* Helper-Class containing all Snippets
*
* */
public class CodeSnippetOptions { public class CodeSnippetOptions {
private ArrayList<SnippetWithName> snippets = new ArrayList<>();
private static final List<SnippetWithName> SNIPPETS = List.of( public CodeSnippetOptions() {
new SnippetWithName("main", "public main(args){\n ${1:}\n}\n"), snippets.add(getMainSnippet());
new SnippetWithName("forLoop", "for(i = 0; i < ${1:listSize}; i++){\n\n}"), snippets.add(getForLoopSnippet());
new SnippetWithName("forEachLoop", "for(el : ${1:listSize}){\n\n}") snippets.add(getForEachSnippet());
); }
public List<SnippetWithName> getSnippets() { public SnippetWithName getMainSnippet(){
return SNIPPETS; return new SnippetWithName("main", "public main(args){\n ${1:}\n}\n");
}
public SnippetWithName getForLoopSnippet(){
return new SnippetWithName("forLoop", "for(i = 0; i < ${1:listSize}; i++){\n\n}");
}
public SnippetWithName getForEachSnippet(){
return new SnippetWithName("forEachLoop", "for(el : ${1:listSize}){\n\n}");
}
public ArrayList<SnippetWithName> getSnippets() {
return snippets;
} }
} }
@@ -1,7 +1,7 @@
package de.dhbw.helper; package de.dhbw.helper;
import de.dhbw.model.DiagnosticsAndTypehints; import de.dhbw.model.DiagnosticsAndTypehints;
import de.dhbw.service.DocumentStore; import de.dhbw.service.TextDocumentService;
import de.dhbw.model.LSPVariable; import de.dhbw.model.LSPVariable;
import de.dhbw.model.Type; import de.dhbw.model.Type;
import de.dhbwstuttgart.languageServerInterface.model.ParserError; import de.dhbwstuttgart.languageServerInterface.model.ParserError;
@@ -13,70 +13,111 @@ import java.util.List;
public class ConversionHelper { public class ConversionHelper {
private final TextHelper textHelper; private final TextHelper textHelper;
private final DocumentStore documentStore; private final TextDocumentService textDocumentService;
public ConversionHelper(TextHelper textHelper, DocumentStore documentStore) { public ConversionHelper(TextHelper textHelper, TextDocumentService textDocumentService) {
this.textHelper = textHelper; this.textHelper = textHelper;
this.documentStore = documentStore; this.textDocumentService = textDocumentService;
} }
public String cleanType(String type) { public InlayHint getInlayHint(LSPVariable variable) {
return type.replace("java.lang.", "").replace("java.util.", ""); InlayHint inlayHint = new InlayHint();
}
public DiagnosticsAndTypehints variablesToDiagnosticsAndTypehints(List<LSPVariable> variables, String input) { String typeDisplay = "";
List<InlayHint> hints = new ArrayList<>(); for (Type type : variable.getPossibleTypes()) {
List<Diagnostic> diagnostics = new ArrayList<>(); typeDisplay += " | " + type.getType().replaceAll("GTV ", "");
for (LSPVariable variable : variables) {
hints.add(createInlayHint(variable));
for (Type type : variable.getPossibleTypes()) {
diagnostics.add(createDiagnostic(variable, input, type));
}
} }
return new DiagnosticsAndTypehints(diagnostics, hints);
inlayHint.setLabel(typeDisplay.length() > 2 ? typeDisplay.substring(2) : typeDisplay);
inlayHint.setPosition(new Position(variable.getLine() - 1, variable.getCharPosition()));
inlayHint.setKind(InlayHintKind.Parameter);
inlayHint.setPaddingRight(true);
inlayHint.setPaddingRight(true);
return inlayHint;
} }
public List<Diagnostic> parseErrorsToDiagnostics(List<ParserError> parserErrors) { public Diagnostic getDiagnostic(LSPVariable variable, String fileUri, Type type) {
return parserErrors.stream().map(error -> new Diagnostic( Range errorRange = new Range(
new Range( new Position(variable.getLine() - 1, variable.getCharPosition()), // Startposition
new Position(error.getLine() - 1, error.getCharPositionInLine()), new Position(variable.getLine() - 1, textHelper.getEndingCharOfStartingChar(variable.getLine() - 1, variable.getCharPosition(), textDocumentService.getFileOfUri(fileUri))) // Endposition
new Position(error.getLine() - 1, error.getEndCharPosition())
),
error.getMsg(),
DiagnosticSeverity.Error,
"JavaTX Language Server"
)).toList();
}
private InlayHint createInlayHint(LSPVariable variable) {
String label = variable.getPossibleTypes().stream()
.map(type -> cleanType(type.type().replaceAll("GTV ", "")))
.reduce((a, b) -> a + " | " + b)
.orElse("");
InlayHint hint = new InlayHint();
hint.setLabel(label);
hint.setPosition(new Position(variable.getLine() - 1, variable.getCharPosition()));
hint.setKind(InlayHintKind.Parameter);
hint.setPaddingRight(true);
return hint;
}
private Diagnostic createDiagnostic(LSPVariable variable, String input, Type type) {
Range range = new Range(
new Position(variable.getLine() - 1, variable.getCharPosition()),
new Position(variable.getLine() - 1,
textHelper.getEndingCharOfStartingChar(variable.getLine() - 1, variable.getCharPosition(), input))
); );
Diagnostic diagnostic = new Diagnostic( Diagnostic diagnostic = new Diagnostic(
range, errorRange,
cleanType(type.type().replaceAll("GTV ", "")), //TODO: REMOVE! Temporary Fix because GTV, like TPH can be thrown away in the TypeResolver
type.getType().replaceAll("GTV ", ""),
DiagnosticSeverity.Hint, DiagnosticSeverity.Hint,
"JavaTX Language Server" "JavaTX Language Server"
); );
diagnostic.setCode(type.generic() ? "GENERIC" : "TYPE"); diagnostic.setCode(type.isGeneric() ? "GENERIC" : "TYPE");
return diagnostic; return diagnostic;
} }
public Diagnostic getDiagnosticWithInput(LSPVariable variable, String input, Type type) {
Range errorRange = new Range(
new Position(variable.getLine() - 1, variable.getCharPosition()), // Startposition
new Position(variable.getLine() - 1, textHelper.getEndingCharOfStartingChar(variable.getLine() - 1, variable.getCharPosition(), input)) // Endposition
);
Diagnostic diagnostic = new Diagnostic(
errorRange,
//TODO: REMOVE! Temporary Fix because GTV, like TPH can be thrown away in the TypeResolver
type.getType().replaceAll("GTV ", ""),
DiagnosticSeverity.Hint,
"JavaTX Language Server"
);
diagnostic.setCode(type.isGeneric() ? "GENERIC" : "TYPE");
return diagnostic;
}
public List<Diagnostic> parseErrorToDiagnostic(List<ParserError> parserErrors){
return parserErrors.stream().map(el -> {
Range errorRange = new Range(
new Position(el.getLine() - 1, el.getCharPositionInLine()), // Startposition
new Position(el.getLine() - 1, el.getEndCharPosition()) // Endposition
);
return new Diagnostic(
errorRange,
el.getMsg(),
DiagnosticSeverity.Error,
"JavaTX Language Server"
);
}).toList();
}
public DiagnosticsAndTypehints variablesToDiagnosticsAndTypehints(ArrayList<LSPVariable> typesOfMethodAndParameters, String uri){
List<InlayHint> typeHint = new ArrayList<>();
ArrayList<Diagnostic> diagnostics = new ArrayList<>();
for (var variable : typesOfMethodAndParameters) {
InlayHint inlayHint = getInlayHint(variable);
typeHint.add(inlayHint);
for (var type : variable.getPossibleTypes()) {
Diagnostic diagnostic = getDiagnostic(variable, uri, type);
diagnostics.add(diagnostic);
}
}
return new DiagnosticsAndTypehints(diagnostics, typeHint);
}
public DiagnosticsAndTypehints variablesToDiagnosticsAndTypehintsWithInput(ArrayList<LSPVariable> typesOfMethodAndParameters, String input){
List<InlayHint> typeHint = new ArrayList<>();
ArrayList<Diagnostic> diagnostics = new ArrayList<>();
for (var variable : typesOfMethodAndParameters) {
InlayHint inlayHint = getInlayHint(variable);
typeHint.add(inlayHint);
for (var type : variable.getPossibleTypes()) {
Diagnostic diagnostic = getDiagnosticWithInput(variable, input, type);
diagnostics.add(diagnostic);
}
}
return new DiagnosticsAndTypehints(diagnostics, typeHint);
}
} }
@@ -0,0 +1,34 @@
package de.dhbw.helper;
import de.dhbw.model.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class DuplicationUtils {
public ArrayList<Type> filterOutDuplicates(List<Type> typeListOne, List<Type> typeListTwo) {
ArrayList<Type> filteredArrayList = new ArrayList<>();
typeListOne.forEach(el -> {
boolean found = false;
for (Type typeInListTwo : typeListTwo) {
found = found || typeInListTwo.getType().equals(el.getType());
}
if (!found) {
filteredArrayList.add(el);
}
});
return filteredArrayList;
}
public ArrayList<Type> filterOutDuplicates(List<Type> typeListOne) {
HashMap<String, Type> hashMap = new HashMap<>();
typeListOne.forEach(el -> {
hashMap.put(el.getType(), el);
});
return new ArrayList<>(hashMap.values());
}
}
@@ -9,23 +9,23 @@ import java.util.HashSet;
import java.util.Set; import java.util.Set;
public class PlaceholderPlacer extends AbstractASTWalker { public class PlaceholderPlacer extends AbstractASTWalker {
Set<PlaceholderVariable> inserts = new HashSet<>();
private final Set<PlaceholderVariable> inserts = new HashSet<>();
private ResultSet withResults; private ResultSet withResults;
String pkgName;
private GenericsResult genericsResult; private GenericsResult genericsResult;
public Set<PlaceholderVariable> getTypeInserts(SourceFile sourceFile, ResultSet withResults, public Set<PlaceholderVariable> getTypeInserts(SourceFile forSourceFile, ResultSet withResults, GenericsResult genericsResult){
GenericsResult genericsResult) {
this.withResults = withResults; this.withResults = withResults;
this.genericsResult = genericsResult; this.genericsResult = genericsResult;
sourceFile.accept(this); pkgName = forSourceFile.getPkgName();
inserts.forEach(PlaceholderVariable::reducePackage); forSourceFile.accept(this);
return inserts; return inserts;
} }
@Override @Override
public void visit(ClassOrInterface classOrInterface) { public void visit(ClassOrInterface classOrInterface) {
PlaceholderPlacerClass cl = new PlaceholderPlacerClass(classOrInterface, withResults, genericsResult); de.dhbw.helper.PlaceholderPlacerClass cl = new de.dhbw.helper.PlaceholderPlacerClass(classOrInterface, withResults, genericsResult);
inserts.addAll(cl.inserts); this.inserts.addAll(cl.inserts);
} }
} }
@@ -60,6 +60,6 @@ class PlaceholderPlacerClass extends AbstractASTWalker{
@Override @Override
public void visit(LambdaExpression lambdaExpression) { public void visit(LambdaExpression lambdaExpression) {
// Lambda expressions do not need type inserts //Lambda-Ausdrücke brauchen keine Typeinsetzungen
} }
} }
@@ -0,0 +1,6 @@
package de.dhbw.helper;
public class Recalculator {
}
@@ -0,0 +1,123 @@
package de.dhbw.helper;
import com.google.common.base.Stopwatch;
import de.dhbw.handler.SaveHandler;
import de.dhbw.model.DiagnosticsAndTypehints;
import de.dhbw.model.LSPVariable;
import de.dhbw.service.CacheService;
import de.dhbw.service.ClientService;
import de.dhbwstuttgart.core.JavaTXCompiler;
import de.dhbwstuttgart.syntaxtree.SourceFile;
import de.dhbwstuttgart.target.generate.GenericsResult;
import de.dhbwstuttgart.typeinference.unify.UnifyResultEvent;
import de.dhbwstuttgart.typeinference.unify.UnifyResultListener;
import org.apache.commons.io.output.NullOutputStream;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.DidSaveTextDocumentParams;
import org.eclipse.lsp4j.InlayHint;
import org.eclipse.lsp4j.MessageType;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
public class ResultListener implements UnifyResultListener {
TypeResolver typeResolver;
SourceFile ast;
DidSaveTextDocumentParams didSaveTextDocumentParams;
CacheService cacheService;
ConversionHelper conversionHelper;
ClientService clientService;
JavaTXCompiler compiler;
SourceFile sourceFile;
public ResultListener(TypeResolver saveHandler, DidSaveTextDocumentParams didSaveTextDocumentParams, CacheService cacheService, ConversionHelper conversionHelper, ClientService clientService){
this.typeResolver = saveHandler;
this.didSaveTextDocumentParams = didSaveTextDocumentParams;
this.cacheService = cacheService;
this.conversionHelper = conversionHelper;
this.clientService = clientService;
}
public void init(DidSaveTextDocumentParams didSaveTextDocumentParams){
var sWatch = Stopwatch.createUnstarted();
sWatch.start();
try {
System.setOut(new PrintStream(OutputStream.nullOutputStream()));
var uri = new URI(didSaveTextDocumentParams.getTextDocument().getUri());
var path = Path.of(uri);
var file = path.toFile();
Files.createDirectories(path.getParent().resolve("out"));
this.compiler = new JavaTXCompiler(file, false);
sourceFile = compiler.sourceFiles.get(file);
compiler.typeInferenceAsync(this, new OutputStreamWriter(new NullOutputStream()));
} catch (Exception e) {
clientService.sendClientLog("KRITISCHE");
clientService.showMessage(MessageType.Error, e.getMessage() == null ? "null" : e.getMessage());
for (StackTraceElement stack : e.getStackTrace()){
clientService.sendClientLog(stack.toString());
}
cacheService.updateGlobalMaps(new ArrayList<>(), new ArrayList<>(), didSaveTextDocumentParams.getTextDocument().getUri());
}
}
@Override
public void onNewTypeResultFound(UnifyResultEvent unifyResultEvent) {
try {
clientService.sendClientLog("YEEEEEEEEEEEP: " + (unifyResultEvent.getNewTypeResult() == null ? "Null" : "Nicht null"));
List<LSPVariable> lspVariables = typeResolver.infereIncremental(unifyResultEvent.getNewTypeResult(), this.compiler.getGeneratedGenerics().get(sourceFile), ast);
if(cacheService.getVariables() == null){
clientService.sendClientLog("Variablen null");
cacheService.setVariables(new ArrayList<>());
}
cacheService.getVariables().addAll(lspVariables);
DiagnosticsAndTypehints diagnosticsAndTypehints = conversionHelper.variablesToDiagnosticsAndTypehints(new ArrayList<>(lspVariables), didSaveTextDocumentParams.getTextDocument().getUri());
clientService.sendClientLog("Diagnostiken erstellt");
List<InlayHint> typeHint = diagnosticsAndTypehints.getInlayHints();
List<Diagnostic> diagnostics = diagnosticsAndTypehints.getDiagnostics();
cacheService.getGlobalInlayHintMap().computeIfAbsent(didSaveTextDocumentParams.getTextDocument().getUri(), k -> new ArrayList<>());
cacheService.getGlobalDiagnosticsMap().computeIfAbsent(didSaveTextDocumentParams.getTextDocument().getUri(), k -> new ArrayList<>());
cacheService.getGlobalInlayHintMap().get(didSaveTextDocumentParams.getTextDocument().getUri()).addAll(typeHint);
cacheService.getGlobalDiagnosticsMap().get(didSaveTextDocumentParams.getTextDocument().getUri()).addAll(diagnostics);
clientService.sendClientLog("paaaaaaaaaaaaast");
clientService.publishDiagnostics(didSaveTextDocumentParams.getTextDocument().getUri(), cacheService.getGlobalDiagnosticsMap().get(didSaveTextDocumentParams.getTextDocument().getUri()));
} catch (IOException e) {
clientService.sendClientLog("KOMISCHER FEHLER!");
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
clientService.sendClientLog("KOMISCHER FEHLER!");
throw new RuntimeException(e);
} catch (URISyntaxException e) {
clientService.sendClientLog("KOMISCHER FEHLER!");
throw new RuntimeException(e);
}
}
}
@@ -1,27 +1,85 @@
package de.dhbw.helper; package de.dhbw.helper;
import java.util.Set; import org.apache.log4j.Logger;
import java.util.List;
public class TextHelper { public class TextHelper {
private static final Set<String> WORD_DELIMITERS = Set.of("(", ")", " ", "{", "}", ";", ","); private static final Logger log = Logger.getLogger(TextHelper.class);
public Integer getClassPositionForGeneric(Integer line, String input, Integer startChar){
log.info("Calculating Position of Class-Generic Variable at Line [" + line + "] and Char [" + startChar + "].");
public int getEndingCharOfStartingChar(int line, int startChar, String input) {
String[] lines = input.split("\n"); String[] lines = input.split("\n");
if (lines.length <= line) {
if(lines.length < line){
log.warn("Returning hardcoded Value because the requested Line [" + line + "] does not exist in Text Document.");
return startChar+3;
}
String[] linesInChar = lines[line].split("");
var index = startChar;
var found = false;
for (int i = startChar; i < linesInChar.length; i++) {
if(linesInChar[i].contains("{")){
index = i;
found = true;
break;
}
}
if(!found){
index = linesInChar.length-1;
}
for(int j = index; j <= linesInChar.length; j--){
if(!linesInChar[j].isEmpty() && !linesInChar[j].equals(" ")){
return j;
}
}
return index;
}
public Integer getEndingCharOfStartingChar(Integer line, Integer startChar, String input){
log.info("Calculating ending-Position for Variable at Line [" + line + "] and Char [" + startChar + "].");
List<String> endingChars = List.of("(", ")", " ", "{", "}", ";", ",");
String[] lines = input.split("\n");
if(lines.length < line){
log.warn("Returning hardcoded Value because the requested Line [" + line + "] does not exist in Text Document.");
return startChar; return startChar;
} }
String lineText = lines[line]; String[] linesInChar = lines[line].split("");
for (int i = startChar; i < lineText.length(); i++) {
if (WORD_DELIMITERS.contains(String.valueOf(lineText.charAt(i)))) {
var index = startChar;
for (int i = startChar; i < linesInChar.length; i++) {
index++;
if(endingChars.contains(linesInChar[i])){
return i; return i;
} }
} }
return lineText.length();
return index-1;
} }
public String getTextOfChars(String textDocument, int line, int charStart, int charEnd) { public String getTextOfChars(String textDocument, Integer line, Integer charStart, Integer charEnd){
return textDocument.split("\n")[line].substring(charStart, charEnd); String[] splittedText = textDocument.split("\n");
return splittedText[line].substring(charStart, charEnd);
} }
} }
@@ -1,111 +1,328 @@
package de.dhbw.helper; package de.dhbw.helper;
import de.dhbw.model.LSPVariable;
import de.dhbw.model.*;
import de.dhbw.model.PlaceholderVariable; import de.dhbw.model.PlaceholderVariable;
import de.dhbw.model.Type; import de.dhbwstuttgart.core.JavaTXCompiler;
import de.dhbwstuttgart.languageServerInterface.LanguageServerInterface; import de.dhbwstuttgart.languageServerInterface.LanguageServerInterface;
import de.dhbwstuttgart.languageServerInterface.model.LanguageServerTransferObject; import de.dhbwstuttgart.languageServerInterface.model.LanguageServerTransferObject;
import de.dhbwstuttgart.parser.scope.JavaClassName;
import de.dhbwstuttgart.syntaxtree.SourceFile; import de.dhbwstuttgart.syntaxtree.SourceFile;
import de.dhbwstuttgart.syntaxtree.factory.NameGenerator;
import de.dhbwstuttgart.syntaxtree.visual.ASTPrinter;
import de.dhbwstuttgart.target.generate.GenericsResult; import de.dhbwstuttgart.target.generate.GenericsResult;
import de.dhbwstuttgart.typedeployment.TypeInsert;
import de.dhbwstuttgart.typeinference.result.ResultSet; import de.dhbwstuttgart.typeinference.result.ResultSet;
import org.apache.log4j.LogManager; import org.apache.log4j.LogManager;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*; import java.util.*;
public class TypeResolver {
/**
* Helper-Class for finding the Type of a selected Word
*/
public class TypeResolver {
private static final Logger logger = LogManager.getLogger(TypeResolver.class); private static final Logger logger = LogManager.getLogger(TypeResolver.class);
private List<GenericsResult> generatedGenerics; private final TypeUtils typeUtils;
private final DuplicationUtils duplicationUtils;
private final boolean ENABLE_GENERICS = true;
private List<GenericsResult> generatedGenerics = null;
private HashMap<String, List<PlaceholderVariable>> inserts; private HashMap<String, List<PlaceholderVariable>> inserts;
private LanguageServerTransferObject current;
public HashMap<String, List<PlaceholderVariable>> getInserts() { public HashMap<String, List<PlaceholderVariable>> getInserts() {
return inserts; return inserts;
} }
//Somehow you have to reset to the Letter N to keep the naming of the TPHs consistent
private final String RESET_TO_LETTER = "A";
private LanguageServerTransferObject current;
public void reset() { public void reset() {
inserts = null; HashMap<String, List<TypeInsert>> inserts = null;
generatedGenerics = null; List<GenericsResult> generatedGenerics = null;
current = null; current = null;
} }
public List<LSPVariable> inferTypes(String path, String input) throws IOException, ClassNotFoundException, URISyntaxException {
LanguageServerInterface lsi = new LanguageServerInterface();
LanguageServerTransferObject transfer = lsi.getResultSetAndAbstractSyntax(path);
SourceFile ast = transfer.getAst(); public TypeResolver() {
List<ResultSet> resultSets = transfer.getResultSets(); this.typeUtils = new TypeUtils();
generatedGenerics = transfer.getGeneratedGenerics().get(ast); this.duplicationUtils = new DuplicationUtils();
current = transfer;
Set<PlaceholderVariable> insertPoints = new HashSet<>();
for (int i = 0; i < resultSets.size(); i++) {
insertPoints.addAll(ASTTransformationHelper.createTypeInsertPoints(ast, resultSets.get(i), generatedGenerics.get(i)));
}
inserts = groupInsertsByPosition(insertPoints);
List<LSPVariable> variables = createVariablesFromInserts(inserts);
deduplicateTypes(variables);
return variables;
} }
public List<LSPVariable> inferTypesFromCurrent() throws IOException, ClassNotFoundException, URISyntaxException {
Set<PlaceholderVariable> insertPoints = new HashSet<>();
for (int i = 0; i < current.getResultSets().size(); i++) {
insertPoints.addAll(ASTTransformationHelper.createTypeInsertPoints(
current.getAst(), current.getResultSets().get(i), generatedGenerics.get(i)));
}
inserts = groupInsertsByPosition(insertPoints);
List<LSPVariable> variables = createVariablesFromInserts(inserts);
deduplicateTypes(variables);
return variables;
}
public boolean isTypeImported(String input, String type) { public boolean isTypeImported(String input, String type) {
// try {
// var transferObject = getCacheOrCalculate(input);
// var abstractSyntax = transferObject.getAst();
//
// var isAlreadyImported = false;
// for (var importStatement : abstractSyntax.getImports()) {
// isAlreadyImported = !isAlreadyImported && importStatement.toString().equals(type);
// }
//
// return isAlreadyImported;
//
// } catch (Exception e) {
// return true;
// }
return true; return true;
} }
private HashMap<String, List<PlaceholderVariable>> groupInsertsByPosition(Set<PlaceholderVariable> insertPoints) { public void getCompilerInput(String path, String input) throws IOException, URISyntaxException, ClassNotFoundException {
HashMap<String, List<PlaceholderVariable>> grouped = new HashMap<>(); LanguageServerInterface languageServer = new LanguageServerInterface();
for (PlaceholderVariable insert : insertPoints) { var transferObj = languageServer.getResultSetAndAbastractSyntax(path, RESET_TO_LETTER);
String key = insert.point.point.getLine() + " " + insert.point.point.getCharPositionInLine(); current = transferObj;
grouped.computeIfAbsent(key, k -> new ArrayList<>()).add(insert);
}
return grouped;
} }
private List<LSPVariable> createVariablesFromInserts(HashMap<String, List<PlaceholderVariable>> insertsMap) { public LanguageServerTransferObject updateIfNotPresent(String path, String input) throws IOException, URISyntaxException, ClassNotFoundException {
List<LSPVariable> variables = new ArrayList<>(); if (current == null) {
for (var entry : insertsMap.entrySet()) { LanguageServerInterface languageServer = new LanguageServerInterface();
List<PlaceholderVariable> placeholders = entry.getValue(); LanguageServerTransferObject transferObj = languageServer.getResultSetAndAbstractSyntax(path);
List<Type> types = placeholders.stream() return transferObj;
.map(p -> new Type(p.getInsertString(), p.point.isGenericClassInsertPoint()))
.toList();
PlaceholderVariable first = placeholders.getFirst();
variables.add(new LSPVariable(
"test",
new ArrayList<>(types),
first.point.point.getLine(),
first.point.point.getCharPositionInLine(),
first.point.point.getStopIndex(),
first.getResultPair().getLeft()
));
} }
return current;
}
public ArrayList<LSPVariable> infereChangeInput() throws IOException, ClassNotFoundException, URISyntaxException {
Set<PlaceholderVariable> tips = new HashSet<>();
for (int i = 0; i < current.getResultSets().size(); i++) {
ResultSet tiResult = current.getResultSets().get(i);
tips.addAll(ASTTransformationHelper.createTypeInsertPoints(current.getAst(), tiResult, generatedGenerics.get(i)));
}
HashMap<String, List<PlaceholderVariable>> insertsOnLines = new HashMap<>();
for (PlaceholderVariable insert : tips) {
if (!insertsOnLines.containsKey(insert.point.point.getLine() + " " + insert.point.point.getCharPositionInLine())) {
insertsOnLines.put(insert.point.point.getLine() + " " + insert.point.point.getCharPositionInLine(), new ArrayList<>(List.of(insert)));
} else {
insertsOnLines.get(insert.point.point.getLine() + " " + insert.point.point.getCharPositionInLine()).add(insert);
}
}
inserts = insertsOnLines;
ArrayList<LSPVariable> variables = new ArrayList<>(insertsOnLines.entrySet().stream().map(el -> new LSPVariable("test", new ArrayList<>(el.getValue().stream().map(typeinsert -> new Type(typeinsert.getInsertString(), typeinsert.point.isGenericClassInsertPoint())).toList()), el.getValue().getFirst().point.point.getLine(), el.getValue().getFirst().point.point.getCharPositionInLine(), el.getValue().get(0).point.point.getStopIndex(), el.getValue().get(0).getResultPair().getLeft())).toList());
for (var variable : variables) {
HashMap<String, Type> uniqueType = new HashMap<>();
for (var type : variable.getPossibleTypes()) {
if (!uniqueType.containsKey(type.getType().replaceAll(" ", ""))) {
uniqueType.put(type.getType(), type);
}
}
variable.setPossibleTypes(new ArrayList<>(uniqueType.values()));
logger.info(variable.getLine() + ":" + variable.getCharPosition());
for (Type t : variable.getPossibleTypes()) {
logger.info(t.getType());
}
}
return variables; return variables;
} }
private void deduplicateTypes(List<LSPVariable> variables) { public ArrayList<LSPVariable> infereInput(String pathString, String input, boolean a) throws IOException, ClassNotFoundException, URISyntaxException {
for (LSPVariable variable : variables) { System.setOut(new PrintStream(OutputStream.nullOutputStream()));
LinkedHashMap<String, Type> unique = new LinkedHashMap<>();
for (Type type : variable.getPossibleTypes()) { var uri = new URI(pathString);
unique.putIfAbsent(type.type().replaceAll(" ", ""), type); var path = Path.of(uri);
logger.info("Path is for calculation: " + path.toString());
var file = path.toFile();
Files.createDirectories(path.getParent().resolve("out"));
JavaTXCompiler compiler = new JavaTXCompiler(List.of(file), List.of(path.getParent().toFile()), path.getParent().resolve("out").toFile());
var parsedSource = compiler.sourceFiles.get(file);
var tiResults = compiler.typeInference(file);
Set<PlaceholderVariable> tips = new HashSet<>();
// for (var sf : compiler.sourceFiles.values()) {
// Map<JavaClassName, byte[]> bytecode = compiler.generateBytecode(sf, tiResults);
// logger.info("Path for Class-File is: " + path.getParent().resolve("out").toFile());
// Files.createDirectories(path.getParent().resolve("out"));
// compiler.writeClassFile(bytecode, path.getParent().resolve("out").toFile(), false);
//
// }
Map<JavaClassName, byte[]> bytecode = compiler.generateBytecode(parsedSource, tiResults);
logger.info("Path for Class-File is: " + path.getParent().resolve("out").toFile());
Files.createDirectories(path.getParent().resolve("out"));
compiler.writeClassFile(bytecode, path.getParent().resolve("out").toFile(), false);
generatedGenerics = compiler.getGeneratedGenerics().get(compiler.sourceFiles.get(file));
for (int i = 0; i < tiResults.size(); i++) {
ResultSet tiResult = tiResults.get(i);
tips.addAll(ASTTransformationHelper.createTypeInsertPoints(parsedSource, tiResult, compiler.getGeneratedGenerics().get(compiler.sourceFiles.get(file)).get(i)));
}
System.setOut(System.out);
this.current = new LanguageServerTransferObject(tiResults, parsedSource, "", compiler.getGeneratedGenerics());
HashMap<String, List<PlaceholderVariable>> insertsOnLines = new HashMap<>();
for (PlaceholderVariable insert : tips) {
if (!insertsOnLines.containsKey(insert.point.point.getLine() + " " + insert.point.point.getCharPositionInLine())) {
insertsOnLines.put(insert.point.point.getLine() + " " + insert.point.point.getCharPositionInLine(), new ArrayList<>(List.of(insert)));
} else {
insertsOnLines.get(insert.point.point.getLine() + " " + insert.point.point.getCharPositionInLine()).add(insert);
}
}
inserts = insertsOnLines;
logger.info("TYPES ARE:");
insertsOnLines.forEach((key, value) -> value.forEach(type -> logger.info(type.point.getInsertString())));
ArrayList<LSPVariable> variables = new ArrayList<>();
for(var entrySet : insertsOnLines.entrySet()){
ArrayList<Type> typesOfVariable = new ArrayList<>();
for(PlaceholderVariable typeinsert : entrySet.getValue()){
typesOfVariable.add(new Type(typeinsert.getInsertString(), typeinsert.point.isGenericClassInsertPoint()));
}
variables.add(new LSPVariable("test",typesOfVariable , entrySet.getValue().getFirst().point.point.getLine(), entrySet.getValue().getFirst().point.point.getCharPositionInLine(), entrySet.getValue().get(0).point.point.getStopIndex(), entrySet.getValue().get(0).getResultPair().getLeft()));
}
for (var variable : variables) {
HashMap<String, Type> uniqueType = new HashMap<>();
for (var type : variable.getPossibleTypes()) {
if (!uniqueType.containsKey(type.getType().replaceAll(" ", ""))) {
uniqueType.put(type.getType(), type);
}
}
variable.setPossibleTypes(new ArrayList<>(uniqueType.values()));
logger.info(variable.getLine() + ":" + variable.getCharPosition());
for (Type t : variable.getPossibleTypes()) {
logger.info(t.getType());
}
}
return variables;
}
public ArrayList<LSPVariable> infereIncremental(List<ResultSet> newResultSet, List<GenericsResult> generatedGenerics, SourceFile ast) throws IOException, ClassNotFoundException, URISyntaxException {
System.setOut(new PrintStream(OutputStream.nullOutputStream()));
logger.info("HMMMMMMMMMMMMM");
var parsedSource = ast;
var tiResults = newResultSet;
Set<PlaceholderVariable> tips = new HashSet<>();
for (int i = 0; i < tiResults.size(); i++) {
ResultSet tiResult = tiResults.get(i);
tips.addAll(ASTTransformationHelper.createTypeInsertPoints(parsedSource, tiResult, null));
}
logger.info("OKI DOKI ALLES ERSTELLT");
System.setOut(System.out);
HashMap<String, List<PlaceholderVariable>> insertsOnLines = new HashMap<>();
for (PlaceholderVariable insert : tips) {
if (!insertsOnLines.containsKey(insert.point.point.getLine() + " " + insert.point.point.getCharPositionInLine())) {
insertsOnLines.put(insert.point.point.getLine() + " " + insert.point.point.getCharPositionInLine(), new ArrayList<>(List.of(insert)));
} else {
insertsOnLines.get(insert.point.point.getLine() + " " + insert.point.point.getCharPositionInLine()).add(insert);
}
}
inserts = insertsOnLines;
logger.info("TYPES ARE:");
insertsOnLines.forEach((key, value) -> value.forEach(type -> logger.info(type.point.getInsertString())));
ArrayList<LSPVariable> variables = new ArrayList<>();
for(var entrySet : insertsOnLines.entrySet()){
ArrayList<Type> typesOfVariable = new ArrayList<>();
for(PlaceholderVariable typeinsert : entrySet.getValue()){
typesOfVariable.add(new Type(typeinsert.getInsertString(), typeinsert.point.isGenericClassInsertPoint()));
}
variables.add(new LSPVariable("test",typesOfVariable , entrySet.getValue().getFirst().point.point.getLine(), entrySet.getValue().getFirst().point.point.getCharPositionInLine(), entrySet.getValue().get(0).point.point.getStopIndex(), entrySet.getValue().get(0).getResultPair().getLeft()));
}
for (var variable : variables) {
HashMap<String, Type> uniqueType = new HashMap<>();
for (var type : variable.getPossibleTypes()) {
if (!uniqueType.containsKey(type.getType().replaceAll(" ", ""))) {
uniqueType.put(type.getType(), type);
}
}
variable.setPossibleTypes(new ArrayList<>(uniqueType.values()));
logger.info(variable.getLine() + ":" + variable.getCharPosition());
for (Type t : variable.getPossibleTypes()) {
logger.info(t.getType());
}
}
return variables;
}
public void reduceCurrent(HashMap<LineCharPosition, String> combinedList, List<LSPVariable> variables) {
for (var lines : combinedList.entrySet()) {
var contentChange = lines.getValue();
for (LSPVariable variable : variables) {
for (Type typeOfVariable : variable.getPossibleTypes()) {
if (typeOfVariable.getType().equalsIgnoreCase(contentChange.replaceAll(" ", ""))) {
if (variable.getLine() - 1 == lines.getKey().line && variable.getCharPosition() == lines.getKey().charPosition) {
current.getResultSets().removeIf(el -> !el.resolveType(variable.getOriginalTphName()).resolvedType.toString().equalsIgnoreCase(contentChange.replaceAll(" ", "")));
return;
}
}
}
} }
variable.setPossibleTypes(new ArrayList<>(unique.values()));
} }
} }
}
public SourceFile getNewAst(String uri) throws IOException, URISyntaxException, ClassNotFoundException {
LanguageServerInterface languageServerInterface = new LanguageServerInterface();
return languageServerInterface.getAst(uri, RESET_TO_LETTER);
}
public void updateAst(String uri) throws IOException, URISyntaxException, ClassNotFoundException {
logger.info("Old AST:");
logger.info(ASTPrinter.print(this.current.getAst()));
this.current = new LanguageServerTransferObject(current.getResultSets(), getNewAst(uri), "", current.getGeneratedGenerics());
logger.info("NEW AST:");
logger.info(ASTPrinter.print(current.getAst()));
}
public void updateCurrent(List<ResultSet> resultSets){
current.getResultSets().addAll(resultSets);
}
}
@@ -0,0 +1,39 @@
package de.dhbw.helper;
import de.dhbw.model.Type;
import de.dhbwstuttgart.syntaxtree.Method;
import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
import de.dhbwstuttgart.typeinference.result.ResultSet;
import java.util.ArrayList;
import java.util.List;
public class TypeUtils {
public ArrayList<Type> getAvailableTypes(List<ResultSet> resultSets, RefTypeOrTPHOrWildcardOrGeneric parameter) {
ArrayList<String> paramTypes = new ArrayList<>();
resultSets.forEach(conSet -> {
if (parameter.toString().toLowerCase().contains("tph ")) {
paramTypes.add(conSet.resolveType(parameter).resolvedType.toString());
}
});
return new ArrayList<>(paramTypes.stream().filter(el -> !el.contains("TPH ")).map(el -> new Type(el, false)).toList());
}
public ArrayList<Type> getAvailableTypes(List<ResultSet> resultSets, Method method) {
ArrayList<String> normalType = new ArrayList<>();
resultSets.forEach(conSet -> {
if (method.getReturnType().toString().toLowerCase().contains("tph ")) {
normalType.add(conSet.resolveType(method.getReturnType()).resolvedType.toString());
}
});
return new ArrayList<>(normalType.stream().filter(el -> !el.contains("TPH ")).map(el -> new Type(el, false)).toList());
}
}
@@ -5,5 +5,29 @@ import org.eclipse.lsp4j.InlayHint;
import java.util.List; import java.util.List;
public record DiagnosticsAndTypehints(List<Diagnostic> diagnostics, List<InlayHint> inlayHints) { public class DiagnosticsAndTypehints {
private List<Diagnostic> diagnostics;
private List<InlayHint> inlayHints;
public List<Diagnostic> getDiagnostics() {
return diagnostics;
}
public List<InlayHint> getInlayHints() {
return inlayHints;
}
public void setDiagnostics(List<Diagnostic> diagnostics) {
this.diagnostics = diagnostics;
}
public void setInlayHints(List<InlayHint> inlayHints) {
this.inlayHints = inlayHints;
}
public DiagnosticsAndTypehints(List<Diagnostic> diagnostics, List<InlayHint> inlayHints){
this.diagnostics = diagnostics;
this.inlayHints = inlayHints;
}
} }
@@ -0,0 +1,41 @@
package de.dhbw.model;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class DocumentChanges {
HashMap<LineCharPosition, String> preciseChanges = new HashMap<>();
ArrayList<Integer> offsetPerLine = new ArrayList<>();
HashMap<Integer, List<String>> textChanges = new HashMap<>();
public ArrayList<Integer> getOffsetPerLine() {
return offsetPerLine;
}
public HashMap<Integer, List<String>> getTextChanges() {
return textChanges;
}
public HashMap<LineCharPosition, String> getPreciseChanges() {
return preciseChanges;
}
public void setOffsetPerLine(ArrayList<Integer> offsetPerLine) {
this.offsetPerLine = offsetPerLine;
}
public void setPreciseChanges(HashMap<LineCharPosition, String> preciseChanges) {
this.preciseChanges = preciseChanges;
}
public void setTextChanges(HashMap<Integer, List<String>> textChanges) {
this.textChanges = textChanges;
}
public DocumentChanges(HashMap<LineCharPosition, String> preciseChanges, ArrayList<Integer> offsetPerLine, HashMap<Integer, List<String>> textChanges){
this.preciseChanges = preciseChanges;
this.offsetPerLine = offsetPerLine;
this.textChanges = textChanges;
}
}
@@ -0,0 +1,11 @@
package de.dhbw.model;
import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
import java.util.ArrayList;
public class LSPClass extends LSPVariable{
public LSPClass(String name, ArrayList<Type> possibleTypes, int line, int charPosition, int endPosition, RefTypeOrTPHOrWildcardOrGeneric originalTphName) {
super(name, possibleTypes, line, charPosition, endPosition, originalTphName);
}
}
@@ -0,0 +1,11 @@
package de.dhbw.model;
import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
import java.util.ArrayList;
public class LSPMethod extends LSPVariable {
public LSPMethod(String name, ArrayList<Type> type, int line, int charPosition, int endPosition, RefTypeOrTPHOrWildcardOrGeneric originalTphName) {
super(name, type, line, charPosition, endPosition, originalTphName);
}
}
@@ -0,0 +1,12 @@
package de.dhbw.model;
import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
import java.util.ArrayList;
public class LSPParameter extends LSPVariable {
public LSPParameter(String name, ArrayList<Type> type, int line, int charPosition, int endPosition, RefTypeOrTPHOrWildcardOrGeneric originalTphName) {
super(name, type, line, charPosition, endPosition, originalTphName);
}
}
@@ -2,19 +2,18 @@ package de.dhbw.model;
import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric; import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
import java.util.List; import java.util.ArrayList;
public class LSPVariable { public class LSPVariable {
String name;
ArrayList<Type> possibleTypes;
int line;
int charPosition;
int endPosition;
boolean needsInference;
RefTypeOrTPHOrWildcardOrGeneric originalTphName;
private final String name; public LSPVariable(String name, ArrayList<Type> possibleTypes, int line, int charPosition, int endPosition, RefTypeOrTPHOrWildcardOrGeneric originalTphName) {
private final int line;
private final int charPosition;
private final int endPosition;
private final RefTypeOrTPHOrWildcardOrGeneric originalTphName;
private List<Type> possibleTypes;
public LSPVariable(String name, List<Type> possibleTypes, int line, int charPosition,
int endPosition, RefTypeOrTPHOrWildcardOrGeneric originalTphName) {
this.name = name; this.name = name;
this.possibleTypes = possibleTypes; this.possibleTypes = possibleTypes;
this.line = line; this.line = line;
@@ -23,11 +22,51 @@ public class LSPVariable {
this.originalTphName = originalTphName; this.originalTphName = originalTphName;
} }
public String getName() { return name; } public RefTypeOrTPHOrWildcardOrGeneric getOriginalTphName() {
public int getLine() { return line; } return originalTphName;
public int getCharPosition() { return charPosition; } }
public int getEndPosition() { return endPosition; }
public RefTypeOrTPHOrWildcardOrGeneric getOriginalTphName() { return originalTphName; } public void setOriginalTphName(RefTypeOrTPHOrWildcardOrGeneric originalTphName) {
public List<Type> getPossibleTypes() { return possibleTypes; } this.originalTphName = originalTphName;
public void setPossibleTypes(List<Type> possibleTypes) { this.possibleTypes = possibleTypes; } }
public int getEndPosition() {
return endPosition;
}
public void setEndPosition(int endPosition) {
this.endPosition = endPosition;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ArrayList<Type> getPossibleTypes() {
return possibleTypes;
}
public void setPossibleTypes(ArrayList<Type> possibleTypes) {
this.possibleTypes = possibleTypes;
}
public int getLine() {
return line;
}
public void setLine(int line) {
this.line = line;
}
public int getCharPosition() {
return charPosition;
}
public void setCharPosition(int charPosition) {
this.charPosition = charPosition;
}
} }
@@ -0,0 +1,25 @@
package de.dhbw.model;
public class LineCharPosition {
public final int line;
public final int charPosition;
public LineCharPosition(int line, int charPosition) {
this.line = line;
this.charPosition = charPosition;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof LineCharPosition)) return false;
LineCharPosition that = (LineCharPosition) o;
return line == that.line && charPosition == that.charPosition;
}
@Override
public String toString() {
return "Line " + line + ", Char " + charPosition;
}
}
@@ -0,0 +1,55 @@
package de.dhbw.model.ParseError;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.atn.ATNConfigSet;
import org.antlr.v4.runtime.dfa.DFA;
import org.eclipse.lsp4j.*;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;
public class DiagnoseErrorListener implements ANTLRErrorListener {
private final List<Diagnostic> errorMessages = new ArrayList<>();
@Override
public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) {
int endCharPosition = charPositionInLine;
if (offendingSymbol instanceof Token) {
Token offendingToken = (Token) offendingSymbol;
// Berechne die Endposition anhand der Token-Länge
endCharPosition = charPositionInLine + offendingToken.getText().length();
}
// Erstelle den Range mit Start- und Endposition
Range errorRange = new Range(
new Position(line - 1, charPositionInLine), // Startposition
new Position(line - 1, endCharPosition) // Endposition
);
Diagnostic diagnostic = new Diagnostic(
errorRange,
msg,
DiagnosticSeverity.Error,
"JavaTX Language Server"
);
errorMessages.add(diagnostic);
}
@Override
public void reportAmbiguity(Parser parser, DFA dfa, int i, int i1, boolean b, BitSet bitSet, ATNConfigSet atnConfigSet) {
}
@Override
public void reportAttemptingFullContext(Parser parser, DFA dfa, int i, int i1, BitSet bitSet, ATNConfigSet atnConfigSet) {
}
@Override
public void reportContextSensitivity(Parser parser, DFA dfa, int i, int i1, int i2, ATNConfigSet atnConfigSet) {
}
public List<Diagnostic> getErrorMessages() {
return errorMessages;
}
}
@@ -4,58 +4,90 @@ import org.antlr.v4.runtime.Token;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Set;
public class PlaceholderPoint { public class PlaceholderPoint {
public Token point;
public final Token point;
private String insertString; private String insertString;
private int extraOffset = 0; private int extraOffset = 0;
private final PlaceholderType kind; private PlaceholderType kind;
public PlaceholderPoint(Token point, String toInsert, PlaceholderType kind) { public PlaceholderPoint(Token point, String toInsert, PlaceholderType kind){
this.point = point; this.point = point;
this.kind = kind; this.kind = kind;
this.insertString = toInsert.endsWith(" ") ? toInsert : toInsert + " "; this.insertString = (toInsert.endsWith(" ")) ? toInsert : toInsert + " " ;
} }
public boolean isGenericClassInsertPoint() { public boolean isGenericClassInsertPoint() {
return kind == PlaceholderType.GENERIC_CLASS_INSERT; return kind == PlaceholderType.GENERIC_CLASS_INSERT;
} }
public String insert(String intoSource, List<PlaceholderPoint> additionalOffset) { public String insert(String intoSource, List<PlaceholderPoint> additionalOffset){
return new StringBuilder(intoSource) return new StringBuilder(intoSource).insert(point.getStartIndex()+extraOffset, insertString).toString();
.insert(point.getStartIndex() + extraOffset, insertString)
.toString();
} }
public String getInsertString() { return insertString; } public String getInsertString() {
public void setInsertString(String insertString) { this.insertString = insertString; } return insertString;
public PlaceholderType getKind() { return kind; } }
public void addExtraOffset(int toAdd) { this.extraOffset += toAdd; }
public int getPositionInCode() { return point.getStartIndex() + extraOffset; } public void addExtraOffset(int toAdd) {
this.extraOffset += toAdd;
@Override }
public int getPositionInCode() {
return point.getStartIndex() + extraOffset;
}
/* PL 2018-06-19
* Zwei TypeInsertPoint's sind gleich, wenn ihre point's gleich sind
* eingefuegt damit man TypeReplaceMarker vergleichen kann
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj) { public boolean equals(Object obj) {
return this == obj; return this == obj;
/*
if(!(obj instanceof TypeInsertPoint)) {
return false;
}
else {
return
((TypeInsertPoint)obj).getPositionInCode() == this.getPositionInCode() &&
((TypeInsertPoint)obj).insertString.equals(this.insertString);
}
*/
} }
@Override
public int hashCode() { public int hashCode() {
return getPositionInCode() * 11 * insertString.hashCode(); return getPositionInCode() * 11 * insertString.hashCode();
} }
@Override public Set<PlaceholderPoint> getAdditionalPoints() {
return this.getAdditionalPoints();
}
public String toString() { public String toString() {
return point.getLine() + ":" + point.getCharPositionInLine() + ":" + insertString; return point.getLine() + ":" + point.getCharPositionInLine() + ":" + insertString;
} }
public static final class TypeInsertPointPositionComparator implements Comparator<PlaceholderPoint> { public static final class TypeInsertPointPositionComparator implements Comparator<PlaceholderPoint> {
@Override
public int compare(PlaceholderPoint o1, PlaceholderPoint o2) { @Override
if (o1.point == null && o2.point == null) return 0; public int compare(PlaceholderPoint o1, PlaceholderPoint o2) {
if (o2.point == null) return 1; if (o1.point == null && o2.point == null) {
if (o1.point == null) return -1; return 0;
return Integer.compare(o1.getPositionInCode(), o2.getPositionInCode()); } else if (o2.point == null) {
} return 1;
} else if (o1.point == null) {
return -1;
}
if (o1.getPositionInCode() > o2.getPositionInCode()) {
return 1;
} else if (o1.getPositionInCode() < o2.getPositionInCode()) {
return -1;
}
return 0;
}
} }
} }
@@ -1,7 +1,7 @@
package de.dhbw.model; package de.dhbw.model;
public enum PlaceholderType { public enum PlaceholderType {
NORMAL_INSERT, NORMAL_INSERT,
GENERIC_CLASS_INSERT, GENERIC_CLASS_INSERT,
GENERIC_METHOD_INSERT GENERERIC_METHOD_INSERT
} }
@@ -5,61 +5,63 @@ import de.dhbwstuttgart.typeinference.result.ResultPair;
import java.util.*; import java.util.*;
public class PlaceholderVariable { public class PlaceholderVariable {
/**
* point wird hauptsächlich zur Anzeige einer Annotation im Eclipse-plugin benutzt.
*/
public final PlaceholderPoint point; public final PlaceholderPoint point;
private final Set<PlaceholderPoint> inserts; Set<PlaceholderPoint> inserts;
private final ResultPair<?, ?> resultPair; ResultPair<?, ?> resultPair;
public PlaceholderVariable(PlaceholderPoint point, Set<PlaceholderPoint> additionalPoints, public PlaceholderVariable(PlaceholderPoint point, Set<PlaceholderPoint> additionalPoints, ResultPair<?, ?> resultPair) {
ResultPair<?, ?> resultPair) {
this.point = point; this.point = point;
this.inserts = additionalPoints; inserts = additionalPoints;
this.resultPair = resultPair; this.resultPair = resultPair;
} }
public String insert(String intoSource) { public String insert(String intoSource) {
List<PlaceholderPoint> offsets = new ArrayList<>();
String ret = intoSource;
List<PlaceholderPoint> insertsSorted = new ArrayList<>(); List<PlaceholderPoint> insertsSorted = new ArrayList<>();
insertsSorted.add(point); insertsSorted.add(point);
insertsSorted.addAll(inserts);
Collections.sort(insertsSorted, new PlaceholderPoint.TypeInsertPointPositionComparator().reversed());
if (!point.getInsertString().contains("void")) {
insertsSorted.addAll(inserts);
}
insertsSorted.sort(new PlaceholderPoint.TypeInsertPointPositionComparator().reversed());
String result = intoSource;
for (PlaceholderPoint insertPoint : insertsSorted) { for (PlaceholderPoint insertPoint : insertsSorted) {
result = insertPoint.insert(result, new ArrayList<>()); ret = insertPoint.insert(ret, new ArrayList<>());
offsets.add(insertPoint);
} }
return result; return ret;
} }
public String getInsertString() { public String getInsertString() {
return point.getInsertString(); return point.getInsertString();
} }
public void reducePackage() {
point.setInsertString(point.getInsertString()
.replaceAll("java\\.lang\\.", "")
.replaceAll("java\\.util\\.", ""));
}
public ResultPair<?, ?> getResultPair() { public ResultPair<?, ?> getResultPair() {
return resultPair; return this.resultPair;
} }
@Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (this == obj) return true; if (!(obj instanceof PlaceholderVariable)) {
if (!(obj instanceof PlaceholderVariable other)) return false; return false;
return other.point.equals(this.point); } else {
return ((PlaceholderVariable) obj).point.equals(this.point);
}
} }
@Override public Set<PlaceholderPoint> getAdditionalPoints() {
public int hashCode() { PlaceholderPoint.TypeInsertPointPositionComparator comparator = new PlaceholderPoint.TypeInsertPointPositionComparator();
return point.hashCode(); TreeSet<PlaceholderPoint> result = new TreeSet<>(comparator.reversed());
result.addAll(inserts);
return result;
}
public Set<PlaceholderPoint> getAdditionalPointsUnsorted() {
return inserts;
} }
@Override
public String toString() { public String toString() {
return point.toString(); return point.toString();
} }
@@ -1,4 +1,27 @@
package de.dhbw.model; package de.dhbw.model;
public record SnippetWithName(String name, String snippet) { public class SnippetWithName {
private String name;
private String snippet;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSnippet() {
return snippet;
}
public void setSnippet(String snippet) {
this.snippet = snippet;
}
public SnippetWithName(String name, String snippet) {
this.name = name;
this.snippet = snippet;
}
} }
@@ -1,19 +1,34 @@
package de.dhbw.model; package de.dhbw.model;
import java.util.Objects; public class Type {
String type;
boolean generic;
public Type(String type, boolean generic){
this.type = type;
this.generic = generic;
}
public void setGeneric(boolean generic) {
this.generic = generic;
}
public void setType(String type) {
this.type = type;
}
public String getType() {
return type;
}
public boolean isGeneric() {
return generic;
}
public record Type(String type, boolean generic) {
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (this == obj) return true; return type.equals(obj.toString());
if (!(obj instanceof Type other)) return false;
return Objects.equals(type, other.type);
}
@Override
public int hashCode() {
return Objects.hashCode(type);
} }
@Override @Override
@@ -1,35 +1,155 @@
package de.dhbw.service; package de.dhbw.service;
import de.dhbw.helper.CodeSnippetOptions;
import de.dhbw.helper.TextHelper;
import de.dhbw.helper.TypeResolver;
import de.dhbw.model.LSPVariable; import de.dhbw.model.LSPVariable;
import de.dhbwstuttgart.typedeployment.TypeInsert;
import org.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.InlayHint; import org.eclipse.lsp4j.InlayHint;
import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
public class CacheService { public class CacheService {
private HashMap<String, List<InlayHint>> globalInlayHintMap = new HashMap<>();
private HashMap<String, List<InlayHint>> inlayHintsByUri = new HashMap<>();
private HashMap<String, List<Diagnostic>> diagnosticsByUri = new HashMap<>();
private HashMap<String, String> lastSavedFiles = new HashMap<>(); private HashMap<String, String> lastSavedFiles = new HashMap<>();
private Boolean currentlyCalculating = false;
private HashMap<String, List<Diagnostic>> globalDiagnosticsMap = new HashMap<>();
private HashMap<String, String> textDocuments = new HashMap<>();
private CodeSnippetOptions codeSnippetOptions = new CodeSnippetOptions();
private TextHelper textHelper = new TextHelper();
private Boolean dontShowHints = false;
private TypeResolver typeResolver = new TypeResolver();
private Path fileRoot = null;
private Boolean singleFileOpened = false;
private List<LSPVariable> variables = new ArrayList<>(); private List<LSPVariable> variables = new ArrayList<>();
private List<TypeInsert> typeInserts;
public void reset() { public List<TypeInsert> getTypeInserts() {
inlayHintsByUri.clear(); return typeInserts;
diagnosticsByUri.clear();
lastSavedFiles.clear();
variables.clear();
} }
public void updateGlobalMaps(List<Diagnostic> diagnostics, List<InlayHint> hints, String uri) { public void setTypeInserts(List<TypeInsert> typeInserts) {
diagnosticsByUri.put(uri, diagnostics); this.typeInserts = typeInserts;
inlayHintsByUri.put(uri, hints);
} }
public HashMap<String, List<InlayHint>> getInlayHintsByUri() { return inlayHintsByUri; } public void reset(){
public HashMap<String, List<Diagnostic>> getDiagnosticsByUri() { return diagnosticsByUri; } globalInlayHintMap = new HashMap<>();
public HashMap<String, String> getLastSavedFiles() { return lastSavedFiles; } lastSavedFiles = new HashMap<>();
public List<LSPVariable> getVariables() { return variables; } currentlyCalculating = false;
public void setVariables(List<LSPVariable> variables) { this.variables = variables; } globalDiagnosticsMap = new HashMap<>();
textDocuments = new HashMap<>();
codeSnippetOptions = new CodeSnippetOptions();
textHelper = new TextHelper();
dontShowHints = false;
typeResolver = new TypeResolver();
fileRoot = null;
singleFileOpened = false;
typeInserts = null;
}
public void updateGlobalMaps(List<Diagnostic> diagnostics, List<InlayHint> typeHint, String uri) {
globalDiagnosticsMap.put(uri, diagnostics);
globalInlayHintMap.put(uri, typeHint);
}
public Boolean getCurrentlyCalculating() {
return currentlyCalculating;
}
public Boolean getDontShowHints() {
return dontShowHints;
}
public Boolean getSingleFileOpened() {
return singleFileOpened;
}
public HashMap<String, String> getLastSavedFiles() {
return lastSavedFiles;
}
public void setLastSavedFiles(HashMap<String, String> lastSavedFiles) {
this.lastSavedFiles = lastSavedFiles;
}
public CodeSnippetOptions getCodeSnippetOptions() {
return codeSnippetOptions;
}
public HashMap<String, List<Diagnostic>> getGlobalDiagnosticsMap() {
return globalDiagnosticsMap;
}
public HashMap<String, List<InlayHint>> getGlobalInlayHintMap() {
return globalInlayHintMap;
}
public HashMap<String, String> getTextDocuments() {
return textDocuments;
}
public List<LSPVariable> getVariables() {
return variables;
}
public Path getFileRoot() {
return fileRoot;
}
public TextHelper getTextHelper() {
return textHelper;
}
public TypeResolver getTypeResolver() {
return typeResolver;
}
public void setCodeSnippetOptions(CodeSnippetOptions codeSnippetOptions) {
this.codeSnippetOptions = codeSnippetOptions;
}
public void setCurrentlyCalculating(Boolean currentlyCalculating) {
this.currentlyCalculating = currentlyCalculating;
}
public void setDontShowHints(Boolean dontShowHints) {
this.dontShowHints = dontShowHints;
}
public void setFileRoot(Path fileRoot) {
this.fileRoot = fileRoot;
}
public void setGlobalDiagnosticsMap(HashMap<String, List<Diagnostic>> globalDiagnosticsMap) {
this.globalDiagnosticsMap = globalDiagnosticsMap;
}
public void setGlobalInlayHintMap(HashMap<String, List<InlayHint>> globalInlayHintMap) {
this.globalInlayHintMap = globalInlayHintMap;
}
public void setSingleFileOpened(Boolean singleFileOpened) {
this.singleFileOpened = singleFileOpened;
}
public void setTextDocuments(HashMap<String, String> textDocuments) {
this.textDocuments = textDocuments;
}
public void setTextHelper(TextHelper textHelper) {
this.textHelper = textHelper;
}
public void setTypeResolver(TypeResolver typeResolver) {
this.typeResolver = typeResolver;
}
public void setVariables(List<LSPVariable> variables) {
this.variables = variables;
}
} }
@@ -14,32 +14,46 @@ public class ClientService {
this.client = client; this.client = client;
} }
public void setClient(LanguageClient client) { public void publishDiagnostics(String uri, List<Diagnostic> diagnostics){
this.client = client; PublishDiagnosticsParams diagnosticsParams = new PublishDiagnosticsParams(uri, diagnostics);
client.publishDiagnostics(diagnosticsParams);
} }
public LanguageClient getClient() { public void sendClientLog(MessageType type, String message){
return client;
}
public void publishDiagnostics(String uri, List<Diagnostic> diagnostics) {
client.publishDiagnostics(new PublishDiagnosticsParams(uri, diagnostics));
}
public void sendClientLog(MessageType type, String message) {
client.logMessage(new MessageParams(type, message)); client.logMessage(new MessageParams(type, message));
} }
public void sendClientLog(String message){
client.logMessage(new MessageParams(MessageType.Info, message));
}
public void showMessage(MessageType type, String message) { public void showMessage(MessageType type, String message) {
client.showMessage(new MessageParams(type, message)); client.showMessage(new MessageParams(type, message));
} }
public void refreshClient() { public void showMessage(String message) {
client.showMessage(new MessageParams(MessageType.Info, message));
}
public void setClient(LanguageClient client) {
this.client = client;
}
public void updateClient(LanguageClient client) {
client.refreshInlayHints(); client.refreshInlayHints();
client.refreshDiagnostics(); client.refreshDiagnostics();
} }
public void startProgress(String taskName, String title) { public void updateClient() {
client.refreshInlayHints();
client.refreshDiagnostics();
}
public LanguageClient getClient() {
return client;
}
public void startLoading(String taskName, String title, LanguageClient client) {
Either<String, Integer> token = Either.forLeft(taskName); Either<String, Integer> token = Either.forLeft(taskName);
client.createProgress(new WorkDoneProgressCreateParams(token)); client.createProgress(new WorkDoneProgressCreateParams(token));
@@ -47,13 +61,19 @@ public class ClientService {
begin.setTitle(title); begin.setTitle(title);
begin.setCancellable(false); begin.setCancellable(false);
begin.setPercentage(0); begin.setPercentage(0);
client.notifyProgress(new ProgressParams(token, Either.forLeft(begin)));
ProgressParams beginParams = new ProgressParams(token, Either.forLeft(begin));
client.notifyProgress(beginParams);
} }
public void stopProgress(String taskName, String message) { public void stopLoading(String taskName, String title, LanguageClient client) {
Either<String, Integer> token = Either.forLeft(taskName); Either<String, Integer> token = Either.forLeft(taskName);
WorkDoneProgressEnd end = new WorkDoneProgressEnd(); WorkDoneProgressEnd end = new WorkDoneProgressEnd();
end.setMessage(message); end.setMessage(title);
client.notifyProgress(new ProgressParams(token, Either.forLeft(end)));
ProgressParams endParams = new ProgressParams(token, Either.forLeft(end));
client.notifyProgress(endParams);
} }
} }
@@ -1,30 +0,0 @@
package de.dhbw.service;
import org.eclipse.lsp4j.Range;
import java.util.HashMap;
public class DocumentStore {
private final HashMap<String, String> files = new HashMap<>();
public String getFileContent(String uri) {
return files.get(uri);
}
public void saveFile(String uri, String content) {
files.put(uri, content);
}
public void clear() {
files.clear();
}
public boolean rangesOverlap(Range range1, Range range2) {
int start1 = range1.getStart().getLine() * 10000 + range1.getStart().getCharacter();
int end1 = range1.getEnd().getLine() * 10000 + range1.getEnd().getCharacter();
int start2 = range2.getStart().getLine() * 10000 + range2.getStart().getCharacter();
int end2 = range2.getEnd().getLine() * 10000 + range2.getEnd().getCharacter();
return start1 <= end2 && start2 <= end1;
}
}
@@ -2,6 +2,7 @@ package de.dhbw.service;
import de.dhbw.helper.ConversionHelper; import de.dhbw.helper.ConversionHelper;
import de.dhbwstuttgart.languageServerInterface.ParserInterface; import de.dhbwstuttgart.languageServerInterface.ParserInterface;
import de.dhbwstuttgart.languageServerInterface.model.ParserError;
import org.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.Diagnostic;
import java.util.List; import java.util.List;
@@ -9,13 +10,26 @@ import java.util.List;
public class ParserService { public class ParserService {
private final ConversionHelper conversionHelper; private final ConversionHelper conversionHelper;
private final ClientService clientService;
public ParserService(ConversionHelper conversionHelper) {
public ParserService(ConversionHelper conversionHelper, ClientService clientService, CacheService cacheService) {
this.conversionHelper = conversionHelper; this.conversionHelper = conversionHelper;
this.clientService = clientService;
}
public List<ParserError> getParserErrors(String input){
ParserInterface parserInterface = new ParserInterface();
return parserInterface.getParseErrors(input);
}
public void checkParser(String input, String uri) {
List<Diagnostic> diagnosticsList = conversionHelper.parseErrorToDiagnostic(getParserErrors(input));
clientService.publishDiagnostics(uri, diagnosticsList);
} }
public List<Diagnostic> getDiagnosticsOfErrors(String input, String uri) { public List<Diagnostic> getDiagnosticsOfErrors(String input, String uri) {
ParserInterface parserInterface = new ParserInterface(); return conversionHelper.parseErrorToDiagnostic(getParserErrors(input));
return conversionHelper.parseErrorsToDiagnostics(parserInterface.getParseErrors(input));
} }
} }
@@ -0,0 +1,121 @@
package de.dhbw.service;
import de.dhbw.model.DocumentChanges;
import de.dhbw.model.LineCharPosition;
import org.eclipse.lsp4j.Range;
import java.util.*;
public class TextDocumentService {
private final HashMap<String, String> files = new HashMap<>();
public TextDocumentService(){
}
public void reset(){
files.clear();
}
public String getFileOfUri(String uri) {
return files.get(uri);
}
public void saveFileWithUri(String uri, String input) {
files.put(uri, input);
}
public DocumentChanges calculateDifference(String currentText, String newText){
ArrayList<String> currentTextLines = new ArrayList<>(Arrays.stream(currentText.split("\n")).toList());
ArrayList<String> newTextLines = new ArrayList<>(Arrays.stream(newText.split("\n")).toList());
HashMap<LineCharPosition, String> preciseChanges = new HashMap<>();
ArrayList<Integer> offsetPerLine = new ArrayList<>();
HashMap<Integer, List<String>> textChanges = new HashMap<>();
int index = 0;
for (String newTextLine : newTextLines) {
if (!(currentTextLines.size() > index)) {
offsetPerLine.add(0);
} else {
Map<LineCharPosition, String> lineDiffs = differenceLinePos(currentTextLines.get(index), newTextLine, index);
preciseChanges.putAll(lineDiffs);
textChanges.put(index, difference(currentTextLines.get(index), newTextLine).stream().map(el -> el.replaceAll(" ", "")).toList());
offsetPerLine.add(newTextLine.length() - currentTextLines.get(index).length());
}
index++;
}
return new DocumentChanges(preciseChanges, offsetPerLine, textChanges);
}
public Map<LineCharPosition, String> differenceLinePos(String first, String second, int line) {
Map<LineCharPosition, String> result = new HashMap<>();
int i = 0, j = 0;
int startDiff = -1;
while (j < second.length()) {
if (i < first.length() && first.charAt(i) == second.charAt(j)) {
if (startDiff != -1) {
String diff = second.substring(startDiff, j);
result.put(new LineCharPosition(line, startDiff), diff);
startDiff = -1;
}
i++;
j++;
} else {
if (startDiff == -1) {
startDiff = j;
}
j++;
}
}
if (startDiff != -1) {
String diff = second.substring(startDiff);
result.put(new LineCharPosition(line, startDiff), diff);
}
return result;
}
public ArrayList<String> difference(String first, String second) {
ArrayList<String> result = new ArrayList<>();
int i = 0, j = 0;
int startDiff = -1;
while (j < second.length()) {
if (i < first.length() && first.charAt(i) == second.charAt(j)) {
if (startDiff != -1) {
result.add(second.substring(startDiff, j));
startDiff = -1;
}
i++;
j++;
} else {
if (startDiff == -1) {
startDiff = j;
}
j++;
}
}
if (startDiff != -1) {
result.add(second.substring(startDiff));
}
return result;
}
public boolean rangesOverlap(Range range1, Range range2) {
int start1 = range1.getStart().getLine() * 1000 + range1.getStart().getCharacter();
int end1 = range1.getEnd().getLine() * 1000 + range1.getEnd().getCharacter();
int start2 = range2.getStart().getLine() * 1000 + range2.getStart().getCharacter();
int end2 = range2.getEnd().getLine() * 1000 + range2.getEnd().getCharacter();
return start1 <= end2 && start2 <= end1;
}
}
@@ -1,23 +1,14 @@
import de.dhbw.helper.CodeSnippetOptions; import de.dhbw.helper.CodeSnippetOptions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
public class CodeSnippetOptionsTest { public class CodeSnippetOptionsTest {
@Test @Test
public void testSnippetsAreProvided() { public void testForLoop() {
CodeSnippetOptions options = new CodeSnippetOptions(); CodeSnippetOptions options = new CodeSnippetOptions();
assertEquals(3, options.getSnippets().size());
}
@Test assertEquals(options.getForLoopSnippet().getSnippet(),"for(i = 0; i < ${1:listSize}; i++){\n\n}");
public void testForLoopSnippet() {
CodeSnippetOptions options = new CodeSnippetOptions();
var forLoop = options.getSnippets().stream()
.filter(s -> s.name().equals("forLoop"))
.findFirst()
.orElseThrow();
assertEquals("for(i = 0; i < ${1:listSize}; i++){\n\n}", forLoop.snippet());
} }
} }
@@ -0,0 +1,120 @@
import de.dhbw.helper.TextHelper;
import de.dhbw.helper.TypeResolver;
import de.dhbwstuttgart.languageServerInterface.LanguageServerInterface;
import org.junit.Assert;
import org.junit.Test;
import java.io.IOException;
import java.net.URISyntaxException;
public class CompilerInterfaceTest {
@Test
public void testAbstractSyntaxAsString() throws IOException, ClassNotFoundException, URISyntaxException {
//
// ArrayList<String> allTypes = new ArrayList<>();
//
// res.getResultSets().forEach(el -> allTypes.add(el.resolveType(res.getAst().getAllMethods().get(0).getReturnType()).resolvedType.toString()));
// var results = res.getGeneratedGenerics().entrySet().iterator().next();
//
//
// System.out.println(res.getResultSets().toString());
//
//
// System.out.println("\n\n\nPRINTED AST:");
// System.out.println(res.getPrintedAst());
}
@Test
public void testConstraintTypes() throws IOException, ClassNotFoundException, URISyntaxException {
LanguageServerInterface languageServer = new LanguageServerInterface();
TypeResolver typeResolver = new TypeResolver();
var res = languageServer.getResultSetAndAbstractSyntax("/home/ruben/Documents/JavaTXLanguageServer/test/test.jav");
var res2 = languageServer.getResultSetAndAbstractSyntax("/home/ruben/Documents/JavaTXLanguageServer/test/test.jav");
System.out.println("");
}
@Test
public void testTypeFinder() throws IOException, ClassNotFoundException {
// TypeResolver typeResolver = new TypeResolver();
// var inferedMethods = typeResolver.infereInput("import java.lang.Integer;\n import java.lang.String;\n" +
// "public class test{\n" +
// "public main(test){" +
// "if(0>1){" +
// "return \"w\";" +
// "}" +
// "Integer i = 0;" +
// "return i;" +
// "}" +
// "}"
// );
//
// inferedMethods.forEach(el -> System.out.println(el.getName() + ": " + el.getPossibleTypes() + " | " + el.getLine() + " " + el.getCharPosition()));
}
@Test
public void testGenericTypes() throws IOException, ClassNotFoundException {
// TypeResolver typeResolver = new TypeResolver();
// var inferedMethods = typeResolver.infereInput("import java.lang.Integer; public class test{\n" +
// " \n" +
// " public main(testa){\n" +
// " return testa;\n" +
// " }\n" +
// "}"
// );
//
// inferedMethods.forEach(el -> System.out.println(el.getName() + ": " + el.getPossibleTypes() + " | " + el.getLine() + " " + el.getCharPosition()));
}
@Test
public void testTypeFinderParameter() throws IOException, ClassNotFoundException {
// TypeResolver typeResolver = new TypeResolver();
// var inferedMethods = typeResolver.infereInput("import java.lang.Integer;\n" +
// "import java.lang.String; \n" +
// "public class test{\n" +
// " public main(test, test2){\n" +
// " if(1>0){\n" +
// " return test;\n" +
// " }\n" +
// " String i = test;\n" +
// " return 1;\n" +
// "\n" +
// "\n" +
// " }\n" +
// "}"
// );
//
// inferedMethods.forEach(el -> System.out.println(el.getName() + ": " + el.getPossibleTypes()));
}
@Test
public void testCharEnding() throws IOException, ClassNotFoundException {
TextHelper textHelper = new TextHelper();
var endingChar = textHelper.getEndingCharOfStartingChar(3, 13, "import java.lang.Integer;\n" +
"import java.lang.String; \n" +
"public class test{\n" +
" public main(test, test2){\n" +
" if(1>0){\n" +
" return test;\n" +
" }\n" +
" String i = test;\n" +
" return 1;\n" +
"\n" +
"\n" +
" }\n" +
"}"
);
Assert.assertEquals(15, (int) endingChar);
}
}
@@ -0,0 +1,13 @@
import org.junit.Ignore;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class JavaTXLanguageDocumentServiceTest {
@Test
@Ignore
public void testWordExtraction() {
// JavaTXTextDocumentService service = new JavaTXTextDocumentService();
// service.didSave(new DidSaveTextDocumentParams(new TextDocumentIdentifier("file:///c%3A/Users/ruben/Neuer%20Ordner%20%282%29/LSP-Vortrag/images/test.jav")));
}
}
@@ -1,28 +0,0 @@
import de.dhbw.helper.TextHelper;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class TextHelperTest {
@Test
public void testEndingCharOfStartingChar() {
TextHelper textHelper = new TextHelper();
String input = "import java.lang.Integer;\n" +
"import java.lang.String;\n" +
"public class test{\n" +
" public main(test, test2){\n" +
" }\n" +
"}";
int endingChar = textHelper.getEndingCharOfStartingChar(3, 13, input);
assertEquals(17, endingChar);
}
@Test
public void testGetTextOfChars() {
TextHelper textHelper = new TextHelper();
String input = "line zero\nline one\nline two";
assertEquals("line", textHelper.getTextOfChars(input, 1, 0, 4));
}
}
+1 -6
View File
@@ -55,16 +55,11 @@ The Language Server in itself can be used for any Client. The Clients task is to
![Diagram](docs/diagram.png) ![Diagram](docs/diagram.png)
## Additional Information
- Uses the [LSP-Interface](https://gitea.hb.dhbw-stuttgart.de/JavaTX/JavaCompilerCore/src/branch/LSP-Interface) Branch of the Java-TX Compiler Repository
## Update JavaTX Compiler Dependency as Maven Package ## Update JavaTX Compiler Dependency as Maven Package
If you make changes in the Compiler Interface, you have to change the jar and therefore the Dependency in the Java TX Language Server If you make changes in the Compiler Interface, you have to change the jar and therefore the Dependency in the Java TX Language Server
You can follow this steps: You can follow this steps:
1. package the JavaTX Compiler 1. package the JavaTX Compiler
2. create a lib Folder at ./LangaugeServer -> ./LanguageServer/lib 2. take the Jar-File and copy it into the /lib Folder
2. take the Jar-File and copy it into the /lib Folder at
3. execute this Maven command to add the Jar in your local Repository: ```mvn install:install-file -Dfile=lib/JavaTXcompiler-0.1-jar-with-dependencies.jar -DgroupId=de.dhbwstuttgart -DartifactId=JavaTXcompiler -Dversion=0.1 -Dpackaging=jar``` 3. execute this Maven command to add the Jar in your local Repository: ```mvn install:install-file -Dfile=lib/JavaTXcompiler-0.1-jar-with-dependencies.jar -DgroupId=de.dhbwstuttgart -DartifactId=JavaTXcompiler -Dversion=0.1 -Dpackaging=jar```
4. run ```maven clean```, ```validate``` and ```install``` to load the new Dependency 4. run ```maven clean```, ```validate``` and ```install``` to load the new Dependency
5. you can now package the Language Server or change the code accordingly. 5. you can now package the Language Server or change the code accordingly.