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

View File

@@ -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);
}
}
}

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

View File

@@ -3,7 +3,7 @@
"name": "java-tx-language-extension",
"displayName": "Java-TX Language Extension",
"description": "The Language Extension for Java-TX with Typehints and Syntax Checks",
"version": "0.0.16",
"version": "0.0.5",
"engines": {
"vscode": "^1.94.0"
},
@@ -42,7 +42,6 @@
"typescript": "^5.6.2"
},
"dependencies": {
"@vscode/vsce": "^3.6.2",
"vscode-languageclient": "^9.0.1"
}
}

View File

@@ -5,19 +5,28 @@ import {
ServerOptions
} 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 {
const extensionPath = context.extensionPath;
const jarPath = extensionPath + '/JavaTXLanguageServer-1.0-SNAPSHOT-jar-with-dependencies.jar';
const workspaceFolder = context.extensionPath;
const serverOptions: ServerOptions = {
run: { command: 'java', args: ['-Xss10m', '-jar', jarPath] },
debug: { command: 'java', args: ['-Xss10m', '-jar', jarPath] }
run: {
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 = {
documentSelector: [{ scheme: 'file', pattern: '**/*.jav' }],
documentSelector: [{ scheme: 'file', language: 'java' }],
synchronize: {
fileEvents: vscode.workspace.createFileSystemWatcher('**/*.jav')
},
@@ -35,31 +44,37 @@ function createClient(context: vscode.ExtensionContext): LanguageClient {
export async function activate(context: vscode.ExtensionContext) {
client = createClient(context);
try {
await client.start();
console.log('Java-TX Language Server started successfully.');
} catch (err) {
console.error('Failed to start Language Client:', err);
}
client.start()
.then(() => console.log("Language Client erfolgreich gestartet"))
.catch(err => console.error("Fehler beim Starten des Language Clients:", 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 () => {
if (!client) {
vscode.window.showWarningMessage('Language Client is not initialized.');
vscode.window.showWarningMessage('Language Client ist nicht initialisiert.');
return;
}
try {
await client.stop();
client.dispose();
await client.stop(); // stoppt den Serverprozess
client.dispose(); // räumt Ressourcen auf
} 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 {
await client.start();
vscode.window.showInformationMessage('Java-TX Language Server restarted.');
vscode.window.showInformationMessage('Java-TX Language Server neu gestartet.');
} catch (e) {
console.error('Error restarting Language Client:', e);
vscode.window.showErrorMessage('Failed to restart Language Server.');
console.error('Fehler beim Neustarten des Language Clients:', e);
vscode.window.showErrorMessage('Neustart des Language Servers fehlgeschlagen Details in der Konsole.');
}
});
context.subscriptions.push(restart);

View File

@@ -1,49 +1,80 @@
package de.dhbw;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.eclipse.lsp4j.*;
import org.eclipse.lsp4j.services.LanguageClient;
import org.eclipse.lsp4j.services.LanguageServer;
import org.eclipse.lsp4j.services.TextDocumentService;
import org.eclipse.lsp4j.services.WorkspaceService;
import org.eclipse.lsp4j.services.LanguageServer;
import java.util.List;
import java.util.concurrent.CompletableFuture;
/**
*
* Configuration of the Language Server
*
* */
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 JavaTXWorkspaceService workspaceService = new JavaTXWorkspaceService();
public void connect(LanguageClient client) {
textDocumentService.setClient(client);
@Override
public void setTrace(SetTraceParams params) {
}
/**
* Configure the Features of the LanguageServer
* */
@Override
public CompletableFuture<InitializeResult> initialize(InitializeParams params) {
ServerCapabilities capabilities = new ServerCapabilities();
capabilities.setDocumentFormattingProvider(true);
capabilities.setTextDocumentSync(TextDocumentSyncKind.Full);
capabilities.setHoverProvider(false);
capabilities.setInlayHintProvider(true);
capabilities.setCodeActionProvider(true);
capabilities.setTextDocumentSync(TextDocumentSyncKind.Full);
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());
}
return CompletableFuture.completedFuture(new InitializeResult(capabilities));
return CompletableFuture.supplyAsync(() -> new InitializeResult(capabilities));
}
/**
* @return the TextDocumentService
* */
@Override
public TextDocumentService getTextDocumentService() {
return textDocumentService;
}
/**
* @return the WorkspaceService
* */
@Override
public WorkspaceService getWorkspaceService() {
return workspaceService;
}
/**
* @return the Client
* */
public LanguageClient getClient() {
return client;
}
@Override
public CompletableFuture<Object> shutdown() {
return CompletableFuture.completedFuture(null);

View File

@@ -1,15 +1,30 @@
package de.dhbw;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.eclipse.lsp4j.launch.LSPLauncher;
import org.eclipse.lsp4j.services.LanguageClient;
/**
*
* Start the JavaTX language Server and use System In and System Out for Communication
*
* */
public class JavaTXLanguageServerLauncher {
public static void main(String[] args) throws Exception {
JavaTXLanguageServer server = new JavaTXLanguageServer();
var launcher = LSPLauncher.createServerLauncher(server, System.in, System.out);
LanguageClient client = launcher.getRemoteProxy();
server.connect(client);
launcher.startListening().get();
private static final Logger logger = LogManager.getLogger(JavaTXLanguageServerLauncher.class);
public static void main(String[] args) {
try {
JavaTXLanguageServer server = new JavaTXLanguageServer();
var launcher = LSPLauncher.createServerLauncher(server, System.in, System.out);
LanguageClient client = launcher.getRemoteProxy();
server.connect(client);
launcher.startListening().get();
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@@ -1,18 +1,14 @@
package de.dhbw;
import de.dhbw.handler.ChangeHandler;
import de.dhbw.handler.CodeActionHandler;
import de.dhbw.handler.FormattingHandler;
import de.dhbw.handler.SaveHandler;
import de.dhbw.handler.*;
import de.dhbw.service.CacheService;
import de.dhbw.service.ClientService;
import de.dhbw.service.LogService;
import de.dhbw.helper.CodeSnippetOptions;
import de.dhbw.helper.ConversionHelper;
import de.dhbw.helper.TextHelper;
import de.dhbw.helper.TypeResolver;
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 org.eclipse.lsp4j.*;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
@@ -21,95 +17,130 @@ import org.eclipse.lsp4j.services.TextDocumentService;
import java.net.URI;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.*;
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 SaveHandler saveHandler;
private final ChangeHandler changeHandler;
private final CodeActionHandler codeActionHandler;
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 ClientService clientService;
private final CacheService cacheService;
private final CodeSnippetOptions codeSnippetOptions;
private final TypeResolver typeResolver;
private final ConversionHelper conversionHelper;
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() {
TextHelper textHelper = new TextHelper();
this.textHelper = new TextHelper();
this.cacheService = new CacheService();
this.clientService = new ClientService(null);
this.typeResolver = new TypeResolver();
this.documentStore = new DocumentStore();
this.conversionHelper = new ConversionHelper(textHelper, documentStore);
this.textDocumentService = new de.dhbw.service.TextDocumentService();
this.conversionHelper = new ConversionHelper(textHelper, textDocumentService);
this.logService = new LogService(clientService);
this.formattingHandler = new FormattingHandler(documentStore);
this.parserService = new ParserService(conversionHelper);
this.codeActionHandler = new CodeActionHandler(documentStore, typeResolver, logService, conversionHelper);
this.saveHandler = new SaveHandler(typeResolver, documentStore, logService, cacheService, conversionHelper, clientService, parserService);
this.changeHandler = new ChangeHandler(documentStore, parserService, conversionHelper, clientService, typeResolver, cacheService, logService);
this.codeSnippetOptions = new CodeSnippetOptions();
this.formattingHandler = new FormattingHandler(textDocumentService);
this.parserService = new ParserService(conversionHelper, clientService, cacheService);
this.codeActionHandler = new CodeActionHandler(textHelper, textDocumentService, cacheService, typeResolver, logService);
this.saveHandler = new SaveHandler(typeResolver, textDocumentService, logService, cacheService, conversionHelper, clientService, parserService);
this.changeHandler = new ChangeHandler(textDocumentService, parserService, conversionHelper, clientService, typeResolver, cacheService, logService);
}
private final ConversionHelper conversionHelper;
public void setClient(LanguageClient client) {
this.client = client;
clientService.setClient(client);
}
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
public CompletableFuture<Either<List<CompletionItem>, CompletionList>> completion(CompletionParams params) {
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.setInsertText(snippet.snippet());
item.setInsertText(elem.getSnippet());
item.setInsertTextFormat(InsertTextFormat.Snippet);
completions.add(item);
}
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
public void didOpen(DidOpenTextDocumentParams params) {
cacheService.reset();
typeResolver.reset();
String uri = params.getTextDocument().getUri();
String text = params.getTextDocument().getText();
List<Diagnostic> syntaxErrors = parserService.getDiagnosticsOfErrors(text, uri);
List<Diagnostic> syntaxErrors = parserService.getDiagnosticsOfErrors(params.getTextDocument().getText(), params.getTextDocument().getUri());
if (!syntaxErrors.isEmpty()) {
clientService.publishDiagnostics(uri, syntaxErrors);
clientService.publishDiagnostics(params.getTextDocument().getUri(), syntaxErrors);
}
client.refreshDiagnostics();
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
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();
String uri = params.getTextDocument().getUri();
String fileContent = documentStore.getFileContent(uri);
documentStore.saveFile(uri, fileContent);
String fileInput = textDocumentService.getFileOfUri(params.getTextDocument().getUri());
textDocuments.put(params.getTextDocument().getUri(), fileInput);
textDocumentService.saveFileWithUri(params.getTextDocument().getUri(), fileInput);
changeHandler.didChange(params);
}
/**
* Handles a Formatting-Event
*
* @param params the Context of the Formatting
*/
@Override
public CompletableFuture<List<? extends TextEdit>> formatting(DocumentFormattingParams params) {
logService.log("[formatting] Client requested formatting.", MessageType.Info);
@@ -121,24 +152,191 @@ public class JavaTXTextDocumentService implements TextDocumentService {
}
@Override
public void didSave(DidSaveTextDocumentParams params) {
logService.log("[didSave] Client triggered didSave event.");
clientService.startProgress("compile-task", "Inferring types...");
saveHandler.handleSave(params);
clientService.stopProgress("compile-task", "Types successfully inferred");
clientService.refreshClient();
public void didSave(DidSaveTextDocumentParams didSaveTextDocumentParams) {
logService.log("[didSave] Client triggered didSave-Event.");
clientService.startLoading("compile-task", "Inferring types...", client);
saveHandler.asyncHandle(didSaveTextDocumentParams);
clientService.stopLoading("compile-task", "Types successfully inferred", client);
clientService.updateClient(client);
}
@Override
public CompletableFuture<List<InlayHint>> inlayHint(InlayHintParams params) {
String uri = params.getTextDocument().getUri();
List<InlayHint> hints = cacheService.getInlayHintsByUri().get(uri);
return CompletableFuture.completedFuture(hints != null ? hints : Collections.emptyList());
logService.log("[inlayHint] The Client requested Inlay-Hints.", MessageType.Info);
return CompletableFuture.supplyAsync(() -> dontShowHints ? Collections.emptyList() : cacheService.getGlobalInlayHintMap().get(params.getTextDocument().getUri()) == null ? Collections.emptyList() : cacheService.getGlobalInlayHintMap().get(params.getTextDocument().getUri()));
}
@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
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());
}
}

View File

@@ -1,16 +1,25 @@
package de.dhbw;
import org.eclipse.lsp4j.DidChangeConfigurationParams;
import org.eclipse.lsp4j.DidChangeWatchedFilesParams;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.eclipse.lsp4j.*;
import org.eclipse.lsp4j.services.WorkspaceService;
/**
*
* Handles Actions in Workspace
*
* */
public class JavaTXWorkspaceService implements WorkspaceService {
private static final Logger logger = LogManager.getLogger(JavaTXWorkspaceService.class);
@Override
public void didChangeConfiguration(DidChangeConfigurationParams params) {
public void didChangeConfiguration(DidChangeConfigurationParams didChangeConfigurationParams) {
}
@Override
public void didChangeWatchedFiles(DidChangeWatchedFilesParams params) {
}
}

View File

@@ -3,26 +3,24 @@ package de.dhbw.handler;
import com.google.common.base.Stopwatch;
import de.dhbw.helper.ConversionHelper;
import de.dhbw.helper.TypeResolver;
import de.dhbw.model.DiagnosticsAndTypehints;
import de.dhbw.model.LSPVariable;
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.model.*;
import de.dhbw.service.*;
import de.dhbwstuttgart.syntaxtree.factory.NameGenerator;
import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
import de.dhbwstuttgart.typeinference.result.PairTPHequalRefTypeOrWildcardType;
import org.eclipse.lsp4j.*;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.net.URI;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
public class ChangeHandler {
private final DocumentStore documentStore;
private final TextDocumentService textDocumentService;
private final ParserService parserService;
private final ConversionHelper conversionHelper;
private final ClientService clientService;
@@ -30,10 +28,8 @@ public class ChangeHandler {
private final CacheService cacheService;
private final LogService logService;
public ChangeHandler(DocumentStore documentStore, ParserService parserService,
ConversionHelper conversionHelper, ClientService clientService,
TypeResolver typeResolver, CacheService cacheService, LogService logService) {
this.documentStore = documentStore;
public ChangeHandler(TextDocumentService textDocumentService, ParserService parserService, ConversionHelper conversionHelper, ClientService clientService, TypeResolver typeResolver, CacheService cacheService, LogService logService) {
this.textDocumentService = textDocumentService;
this.parserService = parserService;
this.conversionHelper = conversionHelper;
this.clientService = clientService;
@@ -42,54 +38,166 @@ public class ChangeHandler {
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) {
String uri = params.getTextDocument().getUri();
String currentText = textDocumentService.getFileOfUri(params.getTextDocument().getUri());
StringBuilder content = new StringBuilder();
params.getContentChanges().forEach(change -> content.append(change.getText()));
String input = content.toString();
AtomicReference<String> summedUp = new AtomicReference<>("");
params.getContentChanges().forEach(el -> summedUp.set(summedUp.get() + el.getText()));
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()) {
clientService.publishDiagnostics(uri, syntaxErrors);
return;
clientService.publishDiagnostics(params.getTextDocument().getUri(), syntaxErrors);
}
clientService.refreshClient();
logService.log("Found [" + syntaxErrors.size() + "] Syntax Errors in Document.");
inferAndPublish(uri, input);
}
if (syntaxErrors.isEmpty()) {
private void inferAndPublish(String uri, String input) {
Stopwatch stopwatch = Stopwatch.createStarted();
String type = findAddedWord(currentText, summedUp.get());
String input = summedUp.get();
File tempFile = null;
try {
File tempDir = Path.of(new URI(uri)).getParent().toFile();
tempFile = File.createTempFile("jtx_temp", ".tmp", tempDir);
logService.log("Variables are: " + cacheService.getVariables().size());
try (BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile))) {
writer.write(input);
boolean shouldCalculate = false;
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);
cacheService.setVariables(variables);
logService.log("Should Calculate is: " + shouldCalculate);
DiagnosticsAndTypehints result = conversionHelper.variablesToDiagnosticsAndTypehints(variables, input);
cacheService.updateGlobalMaps(result.diagnostics(), result.inlayHints(), uri);
clientService.publishDiagnostics(uri, new ArrayList<>(result.diagnostics()));
if (false) {
try {
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);
}
}
}

View File

@@ -1,9 +1,10 @@
package de.dhbw.handler;
import de.dhbw.helper.ConversionHelper;
import de.dhbw.helper.TextHelper;
import de.dhbw.helper.TypeResolver;
import de.dhbw.service.DocumentStore;
import de.dhbw.service.CacheService;
import de.dhbw.service.LogService;
import de.dhbw.service.TextDocumentService;
import de.dhbw.model.PlaceholderVariable;
import org.eclipse.lsp4j.*;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
@@ -16,111 +17,201 @@ import java.util.stream.Collectors;
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 LogService logService;
private final ConversionHelper conversionHelper;
public CodeActionHandler(DocumentStore documentStore, TypeResolver typeResolver,
LogService logService, ConversionHelper conversionHelper) {
this.documentStore = documentStore;
public CodeActionHandler(TextHelper textHelper, TextDocumentService textDocumentService, CacheService cacheService, TypeResolver typeResolver, LogService logService) {
this.textHelper = textHelper;
this.textDocumentService = textDocumentService;
this.cacheService = cacheService;
this.typeResolver = typeResolver;
this.logService = logService;
this.conversionHelper = conversionHelper;
}
public List<Either<Command, CodeAction>> handleCodeAction(CodeActionParams params) {
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) {
public static Range wholeDocumentRange(String text) {
if (text == null || text.isEmpty()) {
return new Range(new Position(0, 0), new Position(0, 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);
if (c == '\n') {
lastLine++;
lastBreak = i;
} else if (c == '\r') {
lastLine++;
if (i + 1 < text.length() && text.charAt(i + 1) == '\n') {
// Unterscheide \r\n von alleine stehendem \r
if (i + 1 < n && text.charAt(i + 1) == '\n') {
lastLine++;
lastBreak = i + 1;
i++;
i++; // \n überspringen
} else {
lastLine++;
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));
}
private static <V> Map<String, V> getOverlapping(Map<String, V> map, int line,
int startChar, int endChar) {
int s = Math.min(startChar, endChar);
int e = Math.max(startChar, endChar);
public static <V> Map<String, V> getOverlapping(
Map<String, V> map,
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()
.filter(entry -> {
String[] parts = entry.getKey().split("\\s+");
String key = entry.getKey();
String[] parts = key.split("\\s+");
if (parts.length != 2) return false;
try {
int kLine = Integer.parseInt(parts[0]);
int kChar = Integer.parseInt(parts[1]);
return kLine == line && kChar >= s && kChar <= e;
} catch (NumberFormatException ex) {
// Key nicht im erwarteten Format
return false;
}
})
.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;
}
}

View File

@@ -1,34 +1,38 @@
package de.dhbw.handler;
import de.dhbw.service.DocumentStore;
import de.dhbw.service.TextDocumentService;
import org.eclipse.lsp4j.DocumentFormattingParams;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.TextEdit;
import java.util.ArrayList;
import java.util.List;
public class FormattingHandler {
private final DocumentStore documentStore;
private final TextDocumentService textDocumentService;
public FormattingHandler(DocumentStore documentStore) {
this.documentStore = documentStore;
public FormattingHandler(TextDocumentService textDocumentService) {
this.textDocumentService = textDocumentService;
}
public List<TextEdit> handleFormat(DocumentFormattingParams params) {
String content = documentStore.getFileContent(params.getTextDocument().getUri());
String[] lines = content.split("\n");
public List<TextEdit> handleFormat(DocumentFormattingParams params){
List<TextEdit> edits = new ArrayList<>();
String[] lines = textDocumentService.getFileOfUri(params.getTextDocument().getUri()).split("\n");
StringBuilder formattedText = new StringBuilder();
StringBuilder formatted = new StringBuilder();
for (String line : lines) {
formatted.append(line.stripTrailing()).append("\n");
formattedText.append(line.stripTrailing()).append("\n");
}
TextEdit edit = new TextEdit(
new Range(new Position(0, 0), new Position(lines.length, 0)),
formatted.toString().stripTrailing()
);
return List.of(edit);
TextEdit edit = new TextEdit();
edit.setRange(new Range(new Position(0, 0), new Position(lines.length, 0)));
edit.setNewText(formattedText.toString().trim());
edits.add(edit);
return edits;
}
}

View File

@@ -1,35 +1,45 @@
package de.dhbw.handler;
import com.google.common.base.Stopwatch;
import de.dhbw.helper.ResultListener;
import de.dhbw.model.DiagnosticsAndTypehints;
import de.dhbw.model.LSPVariable;
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.*;
import de.dhbw.helper.ConversionHelper;
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 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.List;
public class SaveHandler {
private final TypeResolver typeResolver;
private final DocumentStore documentStore;
private final TextDocumentService textDocumentService;
private final LogService logService;
private final CacheService cacheService;
private final ConversionHelper conversionHelper;
private final ClientService clientService;
private final ParserService parserService;
public SaveHandler(TypeResolver typeResolver, DocumentStore documentStore, LogService logService,
CacheService cacheService, ConversionHelper conversionHelper,
ClientService clientService, ParserService parserService) {
public SaveHandler(TypeResolver typeResolver, TextDocumentService textDocumentService, LogService logService, CacheService cacheService, ConversionHelper conversionHelper, ClientService clientService, ParserService parserService) {
this.typeResolver = typeResolver;
this.documentStore = documentStore;
this.textDocumentService = textDocumentService;
this.logService = logService;
this.cacheService = cacheService;
this.conversionHelper = conversionHelper;
@@ -37,44 +47,85 @@ public class SaveHandler {
this.parserService = parserService;
}
public void handleSave(DidSaveTextDocumentParams params) {
public void handleSave(DidSaveTextDocumentParams didSaveTextDocumentParams) {
cacheService.reset();
typeResolver.reset();
Stopwatch stopwatch = Stopwatch.createStarted();
var sWatch = Stopwatch.createUnstarted();
sWatch.start();
String uri = params.getTextDocument().getUri();
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()) {
clientService.publishDiagnostics(uri, syntaxErrors);
return;
clientService.publishDiagnostics(didSaveTextDocumentParams.getTextDocument().getUri(), syntaxErrors);
}
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);
cacheService.setVariables(variables);
} catch (Exception e) {
logService.log("[didSave] Error trying to get Inlay-Hints and Diagnostics for Client: " + e.getMessage(), MessageType.Error);
DiagnosticsAndTypehints result = conversionHelper.variablesToDiagnosticsAndTypehints(variables, fileContent);
cacheService.updateGlobalMaps(result.diagnostics(), result.inlayHints(), uri);
clientService.publishDiagnostics(uri, new ArrayList<>(result.diagnostics()));
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());
} 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 {
stopwatch.stop();
logService.log("[didSave] Completed in " + stopwatch.elapsed().toSeconds() + "s", MessageType.Info);
sWatch.stop();
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());
}
}
}

View File

@@ -6,85 +6,93 @@ import de.dhbw.model.PlaceholderVariable;
import de.dhbwstuttgart.syntaxtree.ClassOrInterface;
import de.dhbwstuttgart.syntaxtree.Method;
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.GenericsResult;
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 java.util.HashSet;
import java.util.Set;
import java.util.StringJoiner;
public class ASTTransformationHelper {
private ASTTransformationHelper() {}
public static Set<PlaceholderVariable> createTypeInsertPoints(SourceFile source, ResultSet results,
GenericsResult generics) {
return new PlaceholderPlacer().getTypeInserts(source, results, generics);
public static Set<PlaceholderVariable> createTypeInsertPoints(SourceFile forSourcefile, ResultSet withResults, GenericsResult generics) {
return new PlaceholderPlacer().getTypeInserts(forSourcefile, withResults, generics);
}
public static PlaceholderVariable createInsertPoints(RefTypeOrTPHOrWildcardOrGeneric type, Token offset,
ClassOrInterface cl, Method m, ResultSet resultSet,
GenericsResultSet constraints,
GenericsResultSet classConstraints) {
ResolvedType resolvedType = resultSet.resolveType(type);
PlaceholderPoint insertPoint = new PlaceholderPoint(
offset,
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());
public static PlaceholderVariable createInsertPoints(RefTypeOrTPHOrWildcardOrGeneric type, Token offset, ClassOrInterface cl, Method m,
ResultSet resultSet, GenericsResultSet constraints, GenericsResultSet classConstraints) {
ResolvedType resolvedType = resultSet.resolveType(type);
PlaceholderPoint insertPoint = new PlaceholderPoint(offset,
new PlaceholderToInsertString(resolvedType.resolvedType, constraints, classConstraints).insert, PlaceholderType.NORMAL_INSERT);
return new PlaceholderVariable(insertPoint, createGenericInsert(constraints, classConstraints, cl, m, resultSet, offset), resolvedType.getResultPair());
}
private static synchronized Set<PlaceholderPoint> createGenericInserts(GenericsResultSet methodConstraints,
GenericsResultSet classConstraints,
ClassOrInterface cl, Method m,
ResultSet resultSet, Token mOffset) {
private static synchronized Set<PlaceholderPoint> createGenericInsert(GenericsResultSet methodConstraints, GenericsResultSet classConstraints, ClassOrInterface cl, Method m, ResultSet resultSet, Token mOffset){
Set<PlaceholderPoint> result = createGenericClassInserts(classConstraints, cl);
if (m != null) {
Token offset = m.getOffset() != null ? m.getOffset() : mOffset;
result.addAll(createMethodConstraints(methodConstraints, offset));
}
if (m != null) {
result.addAll(createMethodConstraints(methodConstraints, m.getOffset() != null ? m.getOffset() : mOffset));
}
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 == null || constraints.size() == 0) {
return new HashSet<>();
if (constraints.size() == 0) {
return result;
}
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);
Set<PlaceholderPoint> result = new HashSet<>();
result.add(new PlaceholderPoint(offset, insert, PlaceholderType.GENERIC_METHOD_INSERT));
return result;
result.add(new PlaceholderPoint(offset, insert, PlaceholderType.GENERERIC_METHOD_INSERT));
return result;
}
private static Set<PlaceholderPoint> createGenericClassInserts(GenericsResultSet classConstraints,
ClassOrInterface cl) {
if (classConstraints == null || classConstraints.size() == 0) {
return new HashSet<>();
}
private static Set<PlaceholderPoint> createGenericClassInserts(GenericsResultSet classConstraints, ClassOrInterface cl) {
Set<PlaceholderPoint> result = new HashSet<>();
Token offset = cl.getGenerics().getOffset();
String insert = buildGenericString(classConstraints);
Set<PlaceholderPoint> result = new HashSet<>();
result.add(new PlaceholderPoint(cl.getGenerics().getOffset(), insert, PlaceholderType.GENERIC_CLASS_INSERT));
return result;
}
if (classConstraints == null || classConstraints.size() == 0) {
return result;
}
String insert = " <";
private static String buildGenericString(GenericsResultSet constraints) {
StringJoiner joiner = new StringJoiner(", ", " <", ">");
for (var constraint : constraints) {
if (constraint instanceof GenerateGenerics.PairEQ peq) {
joiner.add(peq.left.resolve().getName());
} else if (constraint instanceof GenerateGenerics.PairLT psm) {
joiner.add(psm.left.resolve().getName() + " extends " + psm.right.resolve().getName());
for (var genericInsertConstraint : classConstraints) {
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 += ", ";
}
return joiner.toString();
}
insert = insert.substring(0, insert.length() -2);
insert += ">";
result.add(new PlaceholderPoint(offset, insert, PlaceholderType.GENERIC_CLASS_INSERT));
return result;
}
}

View File

@@ -2,17 +2,34 @@ package de.dhbw.helper;
import de.dhbw.model.SnippetWithName;
import java.util.List;
import java.util.ArrayList;
/**
* Helper-Class containing all Snippets
*
* */
public class CodeSnippetOptions {
private ArrayList<SnippetWithName> snippets = new ArrayList<>();
private static final List<SnippetWithName> SNIPPETS = List.of(
new SnippetWithName("main", "public main(args){\n ${1:}\n}\n"),
new SnippetWithName("forLoop", "for(i = 0; i < ${1:listSize}; i++){\n\n}"),
new SnippetWithName("forEachLoop", "for(el : ${1:listSize}){\n\n}")
);
public CodeSnippetOptions() {
snippets.add(getMainSnippet());
snippets.add(getForLoopSnippet());
snippets.add(getForEachSnippet());
}
public List<SnippetWithName> getSnippets() {
return SNIPPETS;
public SnippetWithName getMainSnippet(){
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;
}
}

View File

@@ -1,7 +1,7 @@
package de.dhbw.helper;
import de.dhbw.model.DiagnosticsAndTypehints;
import de.dhbw.service.DocumentStore;
import de.dhbw.service.TextDocumentService;
import de.dhbw.model.LSPVariable;
import de.dhbw.model.Type;
import de.dhbwstuttgart.languageServerInterface.model.ParserError;
@@ -13,70 +13,111 @@ import java.util.List;
public class ConversionHelper {
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.documentStore = documentStore;
this.textDocumentService = textDocumentService;
}
public String cleanType(String type) {
return type.replace("java.lang.", "").replace("java.util.", "");
}
public InlayHint getInlayHint(LSPVariable variable) {
InlayHint inlayHint = new InlayHint();
public DiagnosticsAndTypehints variablesToDiagnosticsAndTypehints(List<LSPVariable> variables, String input) {
List<InlayHint> hints = new ArrayList<>();
List<Diagnostic> diagnostics = new ArrayList<>();
for (LSPVariable variable : variables) {
hints.add(createInlayHint(variable));
for (Type type : variable.getPossibleTypes()) {
diagnostics.add(createDiagnostic(variable, input, type));
}
String typeDisplay = "";
for (Type type : variable.getPossibleTypes()) {
typeDisplay += " | " + type.getType().replaceAll("GTV ", "");
}
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) {
return parserErrors.stream().map(error -> new Diagnostic(
new Range(
new Position(error.getLine() - 1, error.getCharPositionInLine()),
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))
public Diagnostic getDiagnostic(LSPVariable variable, String fileUri, 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(), textDocumentService.getFileOfUri(fileUri))) // Endposition
);
Diagnostic diagnostic = new Diagnostic(
range,
cleanType(type.type().replaceAll("GTV ", "")),
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.generic() ? "GENERIC" : "TYPE");
diagnostic.setCode(type.isGeneric() ? "GENERIC" : "TYPE");
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);
}
}

View File

@@ -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());
}
}

View File

@@ -9,23 +9,23 @@ import java.util.HashSet;
import java.util.Set;
public class PlaceholderPlacer extends AbstractASTWalker {
private final Set<PlaceholderVariable> inserts = new HashSet<>();
Set<PlaceholderVariable> inserts = new HashSet<>();
private ResultSet withResults;
String pkgName;
private GenericsResult genericsResult;
public Set<PlaceholderVariable> getTypeInserts(SourceFile sourceFile, ResultSet withResults,
GenericsResult genericsResult) {
public Set<PlaceholderVariable> getTypeInserts(SourceFile forSourceFile, ResultSet withResults, GenericsResult genericsResult){
this.withResults = withResults;
this.genericsResult = genericsResult;
sourceFile.accept(this);
inserts.forEach(PlaceholderVariable::reducePackage);
pkgName = forSourceFile.getPkgName();
forSourceFile.accept(this);
return inserts;
}
@Override
public void visit(ClassOrInterface classOrInterface) {
PlaceholderPlacerClass cl = new PlaceholderPlacerClass(classOrInterface, withResults, genericsResult);
inserts.addAll(cl.inserts);
de.dhbw.helper.PlaceholderPlacerClass cl = new de.dhbw.helper.PlaceholderPlacerClass(classOrInterface, withResults, genericsResult);
this.inserts.addAll(cl.inserts);
}
}

View File

@@ -60,6 +60,6 @@ class PlaceholderPlacerClass extends AbstractASTWalker{
@Override
public void visit(LambdaExpression lambdaExpression) {
// Lambda expressions do not need type inserts
//Lambda-Ausdrücke brauchen keine Typeinsetzungen
}
}

View File

@@ -0,0 +1,6 @@
package de.dhbw.helper;
public class Recalculator {
}

View File

@@ -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);
}
}
}

View File

@@ -1,27 +1,85 @@
package de.dhbw.helper;
import java.util.Set;
import org.apache.log4j.Logger;
import java.util.List;
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");
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;
}
String lineText = lines[line];
for (int i = startChar; i < lineText.length(); i++) {
if (WORD_DELIMITERS.contains(String.valueOf(lineText.charAt(i)))) {
String[] linesInChar = lines[line].split("");
var index = startChar;
for (int i = startChar; i < linesInChar.length; i++) {
index++;
if(endingChars.contains(linesInChar[i])){
return i;
}
}
return lineText.length();
return index-1;
}
public String getTextOfChars(String textDocument, int line, int charStart, int charEnd) {
return textDocument.split("\n")[line].substring(charStart, charEnd);
public String getTextOfChars(String textDocument, Integer line, Integer charStart, Integer charEnd){
String[] splittedText = textDocument.split("\n");
return splittedText[line].substring(charStart, charEnd);
}
}

View File

@@ -1,111 +1,328 @@
package de.dhbw.helper;
import de.dhbw.model.LSPVariable;
import de.dhbw.model.*;
import de.dhbw.model.PlaceholderVariable;
import de.dhbw.model.Type;
import de.dhbwstuttgart.core.JavaTXCompiler;
import de.dhbwstuttgart.languageServerInterface.LanguageServerInterface;
import de.dhbwstuttgart.languageServerInterface.model.LanguageServerTransferObject;
import de.dhbwstuttgart.parser.scope.JavaClassName;
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.typedeployment.TypeInsert;
import de.dhbwstuttgart.typeinference.result.ResultSet;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
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.*;
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 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 LanguageServerTransferObject current;
public HashMap<String, List<PlaceholderVariable>> getInserts() {
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() {
inserts = null;
generatedGenerics = null;
HashMap<String, List<TypeInsert>> inserts = null;
List<GenericsResult> generatedGenerics = 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();
List<ResultSet> resultSets = transfer.getResultSets();
generatedGenerics = transfer.getGeneratedGenerics().get(ast);
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 TypeResolver() {
this.typeUtils = new TypeUtils();
this.duplicationUtils = new DuplicationUtils();
}
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) {
// 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;
}
private HashMap<String, List<PlaceholderVariable>> groupInsertsByPosition(Set<PlaceholderVariable> insertPoints) {
HashMap<String, List<PlaceholderVariable>> grouped = new HashMap<>();
for (PlaceholderVariable insert : insertPoints) {
String key = insert.point.point.getLine() + " " + insert.point.point.getCharPositionInLine();
grouped.computeIfAbsent(key, k -> new ArrayList<>()).add(insert);
}
return grouped;
public void getCompilerInput(String path, String input) throws IOException, URISyntaxException, ClassNotFoundException {
LanguageServerInterface languageServer = new LanguageServerInterface();
var transferObj = languageServer.getResultSetAndAbastractSyntax(path, RESET_TO_LETTER);
current = transferObj;
}
private List<LSPVariable> createVariablesFromInserts(HashMap<String, List<PlaceholderVariable>> insertsMap) {
List<LSPVariable> variables = new ArrayList<>();
for (var entry : insertsMap.entrySet()) {
List<PlaceholderVariable> placeholders = entry.getValue();
List<Type> types = placeholders.stream()
.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()
));
public LanguageServerTransferObject updateIfNotPresent(String path, String input) throws IOException, URISyntaxException, ClassNotFoundException {
if (current == null) {
LanguageServerInterface languageServer = new LanguageServerInterface();
LanguageServerTransferObject transferObj = languageServer.getResultSetAndAbstractSyntax(path);
return transferObj;
}
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;
}
private void deduplicateTypes(List<LSPVariable> variables) {
for (LSPVariable variable : variables) {
LinkedHashMap<String, Type> unique = new LinkedHashMap<>();
for (Type type : variable.getPossibleTypes()) {
unique.putIfAbsent(type.type().replaceAll(" ", ""), type);
public ArrayList<LSPVariable> infereInput(String pathString, String input, boolean a) throws IOException, ClassNotFoundException, URISyntaxException {
System.setOut(new PrintStream(OutputStream.nullOutputStream()));
var uri = new URI(pathString);
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);
}
}

View File

@@ -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());
}
}

View File

@@ -5,5 +5,29 @@ import org.eclipse.lsp4j.InlayHint;
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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -2,19 +2,18 @@ package de.dhbw.model;
import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
import java.util.List;
import java.util.ArrayList;
public class LSPVariable {
String name;
ArrayList<Type> possibleTypes;
int line;
int charPosition;
int endPosition;
boolean needsInference;
RefTypeOrTPHOrWildcardOrGeneric originalTphName;
private final String name;
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) {
public LSPVariable(String name, ArrayList<Type> possibleTypes, int line, int charPosition, int endPosition, RefTypeOrTPHOrWildcardOrGeneric originalTphName) {
this.name = name;
this.possibleTypes = possibleTypes;
this.line = line;
@@ -23,11 +22,51 @@ public class LSPVariable {
this.originalTphName = originalTphName;
}
public String getName() { return name; }
public int getLine() { return line; }
public int getCharPosition() { return charPosition; }
public int getEndPosition() { return endPosition; }
public RefTypeOrTPHOrWildcardOrGeneric getOriginalTphName() { return originalTphName; }
public List<Type> getPossibleTypes() { return possibleTypes; }
public void setPossibleTypes(List<Type> possibleTypes) { this.possibleTypes = possibleTypes; }
public RefTypeOrTPHOrWildcardOrGeneric getOriginalTphName() {
return originalTphName;
}
public void setOriginalTphName(RefTypeOrTPHOrWildcardOrGeneric originalTphName) {
this.originalTphName = originalTphName;
}
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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -4,58 +4,90 @@ import org.antlr.v4.runtime.Token;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
public class PlaceholderPoint {
public final Token point;
public Token point;
private String insertString;
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.kind = kind;
this.insertString = toInsert.endsWith(" ") ? toInsert : toInsert + " ";
this.insertString = (toInsert.endsWith(" ")) ? toInsert : toInsert + " " ;
}
public boolean isGenericClassInsertPoint() {
return kind == PlaceholderType.GENERIC_CLASS_INSERT;
return kind == PlaceholderType.GENERIC_CLASS_INSERT;
}
public String insert(String intoSource, List<PlaceholderPoint> additionalOffset) {
return new StringBuilder(intoSource)
.insert(point.getStartIndex() + extraOffset, insertString)
.toString();
public String insert(String intoSource, List<PlaceholderPoint> additionalOffset){
return new StringBuilder(intoSource).insert(point.getStartIndex()+extraOffset, insertString).toString();
}
public String getInsertString() { return insertString; }
public void setInsertString(String insertString) { this.insertString = insertString; }
public PlaceholderType getKind() { return kind; }
public void addExtraOffset(int toAdd) { this.extraOffset += toAdd; }
public int getPositionInCode() { return point.getStartIndex() + extraOffset; }
@Override
public String getInsertString() {
return insertString;
}
public void addExtraOffset(int toAdd) {
this.extraOffset += toAdd;
}
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) {
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() {
return getPositionInCode() * 11 * insertString.hashCode();
return getPositionInCode() * 11 * insertString.hashCode();
}
@Override
public Set<PlaceholderPoint> getAdditionalPoints() {
return this.getAdditionalPoints();
}
public String toString() {
return point.getLine() + ":" + point.getCharPositionInLine() + ":" + insertString;
return point.getLine() + ":" + point.getCharPositionInLine() + ":" + insertString;
}
public static final class TypeInsertPointPositionComparator implements Comparator<PlaceholderPoint> {
@Override
public int compare(PlaceholderPoint o1, PlaceholderPoint o2) {
if (o1.point == null && o2.point == null) return 0;
if (o2.point == null) return 1;
if (o1.point == null) return -1;
return Integer.compare(o1.getPositionInCode(), o2.getPositionInCode());
}
@Override
public int compare(PlaceholderPoint o1, PlaceholderPoint o2) {
if (o1.point == null && o2.point == null) {
return 0;
} 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;
}
}
}

View File

@@ -1,7 +1,7 @@
package de.dhbw.model;
public enum PlaceholderType {
NORMAL_INSERT,
GENERIC_CLASS_INSERT,
GENERIC_METHOD_INSERT
NORMAL_INSERT,
GENERIC_CLASS_INSERT,
GENERERIC_METHOD_INSERT
}

View File

@@ -5,61 +5,63 @@ import de.dhbwstuttgart.typeinference.result.ResultPair;
import java.util.*;
public class PlaceholderVariable {
/**
* point wird hauptsächlich zur Anzeige einer Annotation im Eclipse-plugin benutzt.
*/
public final PlaceholderPoint point;
private final Set<PlaceholderPoint> inserts;
private final ResultPair<?, ?> resultPair;
Set<PlaceholderPoint> inserts;
ResultPair<?, ?> resultPair;
public PlaceholderVariable(PlaceholderPoint point, Set<PlaceholderPoint> additionalPoints,
ResultPair<?, ?> resultPair) {
public PlaceholderVariable(PlaceholderPoint point, Set<PlaceholderPoint> additionalPoints, ResultPair<?, ?> resultPair) {
this.point = point;
this.inserts = additionalPoints;
inserts = additionalPoints;
this.resultPair = resultPair;
}
public String insert(String intoSource) {
List<PlaceholderPoint> offsets = new ArrayList<>();
String ret = intoSource;
List<PlaceholderPoint> insertsSorted = new ArrayList<>();
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) {
result = insertPoint.insert(result, new ArrayList<>());
ret = insertPoint.insert(ret, new ArrayList<>());
offsets.add(insertPoint);
}
return result;
return ret;
}
public String getInsertString() {
return point.getInsertString();
}
public void reducePackage() {
point.setInsertString(point.getInsertString()
.replaceAll("java\\.lang\\.", "")
.replaceAll("java\\.util\\.", ""));
}
public ResultPair<?, ?> getResultPair() {
return resultPair;
return this.resultPair;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof PlaceholderVariable other)) return false;
return other.point.equals(this.point);
if (!(obj instanceof PlaceholderVariable)) {
return false;
} else {
return ((PlaceholderVariable) obj).point.equals(this.point);
}
}
@Override
public int hashCode() {
return point.hashCode();
public Set<PlaceholderPoint> getAdditionalPoints() {
PlaceholderPoint.TypeInsertPointPositionComparator comparator = new PlaceholderPoint.TypeInsertPointPositionComparator();
TreeSet<PlaceholderPoint> result = new TreeSet<>(comparator.reversed());
result.addAll(inserts);
return result;
}
public Set<PlaceholderPoint> getAdditionalPointsUnsorted() {
return inserts;
}
@Override
public String toString() {
return point.toString();
}

View File

@@ -1,4 +1,27 @@
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;
}
}

View File

@@ -1,19 +1,34 @@
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
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof Type other)) return false;
return Objects.equals(type, other.type);
}
@Override
public int hashCode() {
return Objects.hashCode(type);
return type.equals(obj.toString());
}
@Override

View File

@@ -1,35 +1,155 @@
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.dhbwstuttgart.typedeployment.TypeInsert;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.InlayHint;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class CacheService {
private HashMap<String, List<InlayHint>> inlayHintsByUri = new HashMap<>();
private HashMap<String, List<Diagnostic>> diagnosticsByUri = new HashMap<>();
private HashMap<String, List<InlayHint>> globalInlayHintMap = 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<TypeInsert> typeInserts;
public void reset() {
inlayHintsByUri.clear();
diagnosticsByUri.clear();
lastSavedFiles.clear();
variables.clear();
public List<TypeInsert> getTypeInserts() {
return typeInserts;
}
public void updateGlobalMaps(List<Diagnostic> diagnostics, List<InlayHint> hints, String uri) {
diagnosticsByUri.put(uri, diagnostics);
inlayHintsByUri.put(uri, hints);
public void setTypeInserts(List<TypeInsert> typeInserts) {
this.typeInserts = typeInserts;
}
public HashMap<String, List<InlayHint>> getInlayHintsByUri() { return inlayHintsByUri; }
public HashMap<String, List<Diagnostic>> getDiagnosticsByUri() { return diagnosticsByUri; }
public HashMap<String, String> getLastSavedFiles() { return lastSavedFiles; }
public List<LSPVariable> getVariables() { return variables; }
public void setVariables(List<LSPVariable> variables) { this.variables = variables; }
public void reset(){
globalInlayHintMap = new HashMap<>();
lastSavedFiles = new HashMap<>();
currentlyCalculating = false;
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;
}
}

View File

@@ -14,32 +14,46 @@ public class ClientService {
this.client = client;
}
public void setClient(LanguageClient client) {
this.client = client;
public void publishDiagnostics(String uri, List<Diagnostic> diagnostics){
PublishDiagnosticsParams diagnosticsParams = new PublishDiagnosticsParams(uri, diagnostics);
client.publishDiagnostics(diagnosticsParams);
}
public LanguageClient getClient() {
return client;
}
public void publishDiagnostics(String uri, List<Diagnostic> diagnostics) {
client.publishDiagnostics(new PublishDiagnosticsParams(uri, diagnostics));
}
public void sendClientLog(MessageType type, String message) {
public void sendClientLog(MessageType type, String 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) {
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.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);
client.createProgress(new WorkDoneProgressCreateParams(token));
@@ -47,13 +61,19 @@ public class ClientService {
begin.setTitle(title);
begin.setCancellable(false);
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);
WorkDoneProgressEnd end = new WorkDoneProgressEnd();
end.setMessage(message);
client.notifyProgress(new ProgressParams(token, Either.forLeft(end)));
end.setMessage(title);
ProgressParams endParams = new ProgressParams(token, Either.forLeft(end));
client.notifyProgress(endParams);
}
}

View File

@@ -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;
}
}

View File

@@ -2,6 +2,7 @@ package de.dhbw.service;
import de.dhbw.helper.ConversionHelper;
import de.dhbwstuttgart.languageServerInterface.ParserInterface;
import de.dhbwstuttgart.languageServerInterface.model.ParserError;
import org.eclipse.lsp4j.Diagnostic;
import java.util.List;
@@ -9,13 +10,26 @@ import java.util.List;
public class ParserService {
private final ConversionHelper conversionHelper;
private final ClientService clientService;
public ParserService(ConversionHelper conversionHelper) {
public ParserService(ConversionHelper conversionHelper, ClientService clientService, CacheService cacheService) {
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) {
ParserInterface parserInterface = new ParserInterface();
return conversionHelper.parseErrorsToDiagnostics(parserInterface.getParseErrors(input));
return conversionHelper.parseErrorToDiagnostic(getParserErrors(input));
}
}

View File

@@ -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;
}
}

View File

@@ -1,23 +1,14 @@
import de.dhbw.helper.CodeSnippetOptions;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class CodeSnippetOptionsTest {
@Test
public void testSnippetsAreProvided() {
public void testForLoop() {
CodeSnippetOptions options = new CodeSnippetOptions();
assertEquals(3, options.getSnippets().size());
}
@Test
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());
assertEquals(options.getForLoopSnippet().getSnippet(),"for(i = 0; i < ${1:listSize}; i++){\n\n}");
}
}

View File

@@ -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);
}
}

View File

@@ -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")));
}
}

View File

@@ -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));
}
}

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)
## 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
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:
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 at
2. take the Jar-File and copy it into the /lib Folder
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
5. you can now package the Language Server or change the code accordingly.