Compare commits

...

21 Commits

Author SHA1 Message Date
dholle 1cc679afb4 Improvements 2026-06-25 15:59:09 +02:00
dholle 40e5549062 Correctly strip off package names 2026-06-24 14:49:11 +02:00
dholle 4e4eb45842 Update plugin 2026-06-24 14:23:50 +02:00
dholle c0a30af6ef Fix old api usage 2026-04-29 10:31:54 +02:00
dholle 68843eac17 delimiter, not sep 2025-11-26 18:15:32 +01:00
dholle 2c4ff6e237 Update version 2025-11-26 18:14:27 +01:00
dholle c9d878cf30 Fix path delimiter for windows 2025-11-26 18:12:53 +01:00
dholle 75943a8d95 Resolve file paths in emacs client 2025-11-12 13:24:26 +01:00
dholle 53e6d94180 README 2025-10-29 13:34:53 +01:00
dholle d6db2d70e7 Move file 2025-10-22 11:41:30 +02:00
dholle 3bc2340a0c Add emacs mode and fix some incompatibilities 2025-10-22 11:40:32 +02:00
dholle b691d6e0e3 Resolve path 2025-10-09 17:48:00 +02:00
dholle 6917c43c33 Specify compiler path in extension settings 2025-10-02 18:10:50 +02:00
Vic Nightfall 1a0596ca71 Version bump 2025-09-28 14:20:59 +02:00
RubenKraft a9bbe834cc Update README.md 2025-09-24 14:43:07 +00:00
Ruben 4fbf9133a5 fix: bump version 2025-09-23 18:38:31 +02:00
Ruben 972d9014ba fix: bump version 2025-09-23 18:38:01 +02:00
Ruben 4f4404d366 fix: cleanup and fixes 2025-09-23 18:37:37 +02:00
Ruben 2dfbef6d7f fix: except VerifyError in Catch Clause 2025-09-23 18:26:58 +02:00
Ruben 7e6e968b0f feat: do not show 100 times the insert 2025-09-22 18:22:05 +02:00
Ruben dded453ba3 feat: update jar and Version 2025-09-22 15:42:25 +02:00
22 changed files with 3059 additions and 973 deletions
+2571 -632
View File
File diff suppressed because it is too large Load Diff
+14 -1
View File
@@ -3,7 +3,7 @@
"name": "java-tx-language-extension", "name": "java-tx-language-extension",
"displayName": "Java-TX Language Extension", "displayName": "Java-TX Language Extension",
"description": "The Language Extension for Java-TX with Typehints and Syntax Checks", "description": "The Language Extension for Java-TX with Typehints and Syntax Checks",
"version": "0.0.9", "version": "0.0.21",
"engines": { "engines": {
"vscode": "^1.94.0" "vscode": "^1.94.0"
}, },
@@ -20,6 +20,18 @@
"command": "tx.restartLanguageServer", "command": "tx.restartLanguageServer",
"title": "TX: Restart Language Server" "title": "TX: Restart Language Server"
} }
],
"configuration": [
{
"title": "JavaTX Language Server Plugin",
"properties": {
"tx.compilerLocation": {
"type": "string",
"format": "file",
"description": "JavaTX Compiler Location"
}
}
}
] ]
}, },
"scripts": { "scripts": {
@@ -42,6 +54,7 @@
"typescript": "^5.6.2" "typescript": "^5.6.2"
}, },
"dependencies": { "dependencies": {
"@vscode/vsce": "^3.6.1",
"vscode-languageclient": "^9.0.1" "vscode-languageclient": "^9.0.1"
} }
} }
+58 -21
View File
@@ -1,28 +1,67 @@
import path from 'path';
import os from "os";
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import { import {
Executable,
LanguageClient, LanguageClient,
LanguageClientOptions, LanguageClientOptions,
ServerOptions ServerOptions
} from 'vscode-languageclient/node'; } from 'vscode-languageclient/node';
let homeDirectory: string;
let currentUser: string;
function untildify(pathWithTilde: string) {
if (homeDirectory === undefined) {
homeDirectory = os.homedir();
}
// Handle regular ~ expansion (current user)
if (homeDirectory && /^~(?=$|\/|\\)/.test(pathWithTilde)) {
return pathWithTilde.replace(/^~/, homeDirectory);
}
// Handle ~username expansion (only for current user)
const userMatch = pathWithTilde.match(/^~([^/\\]+)(.*)/);
if (userMatch) {
if (currentUser === undefined) {
currentUser = os.userInfo().username;
}
if (currentUser) {
const username = userMatch[1];
const rest = userMatch[2];
if (username === currentUser) {
return homeDirectory + rest;
}
}
}
// Return unchanged if no expansion occurred
return pathWithTilde;
}
let client: LanguageClient | undefined; // <— global, damit wir neu starten können let client: LanguageClient | undefined; // <— global, damit wir neu starten können
function createClient(context: vscode.ExtensionContext): LanguageClient { function createClient(context: vscode.ExtensionContext): LanguageClient | null {
const workspaceFolder = context.extensionPath; const workspaceFolder = context.extensionPath;
const config = vscode.workspace.getConfiguration("tx");
let compiler = config.get<string>("compilerLocation");
if (!compiler || compiler.trim() === "") {
vscode.window.showErrorMessage("Bitte konfiguriere den Pfad des Java-TX Compilers in den Einstellungen!");
return null;
}
compiler = path.resolve(untildify(compiler));
const cmd: Executable = {
command: 'java',
args: ['-Xss10m', '-cp', `${compiler}${path.delimiter}${workspaceFolder}/JavaTXLanguageServer-1.0-SNAPSHOT-jar-with-dependencies.jar`, "de.dhbw.JavaTXLanguageServerLauncher"],
};
const serverOptions: ServerOptions = { const serverOptions: ServerOptions = {
run: { run: cmd,
command: 'java', debug: cmd
args: ['-Xss10m', '-jar', workspaceFolder + "/JavaTXLanguageServer-1.0-SNAPSHOT-jar-with-dependencies.jar"],
},
debug: {
command: 'java',
args: [
'-Xss10m',
'-jar',
workspaceFolder + '/JavaTXLanguageServer-1.0-SNAPSHOT-jar-with-dependencies.jar',
],
}
}; };
const clientOptions: LanguageClientOptions = { const clientOptions: LanguageClientOptions = {
@@ -42,7 +81,9 @@ function createClient(context: vscode.ExtensionContext): LanguageClient {
} }
export async function activate(context: vscode.ExtensionContext) { export async function activate(context: vscode.ExtensionContext) {
client = createClient(context); const c = createClient(context);
if (!c) return;
client = c;
client.start() client.start()
.then(() => console.log("Language Client erfolgreich gestartet")) .then(() => console.log("Language Client erfolgreich gestartet"))
@@ -50,12 +91,6 @@ export async function activate(context: vscode.ExtensionContext) {
console.log('Congratulations, your extension "tx" is now active!'); 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 *** // *** NEU: Restart-Command ***
const restart = vscode.commands.registerCommand('tx.restartLanguageServer', async () => { const restart = vscode.commands.registerCommand('tx.restartLanguageServer', async () => {
if (!client) { if (!client) {
@@ -68,7 +103,9 @@ export async function activate(context: vscode.ExtensionContext) {
} catch (e) { } catch (e) {
console.error('Fehler beim Stoppen des Language Clients:', e); console.error('Fehler beim Stoppen des Language Clients:', e);
} }
client = createClient(context); // komplett neu erzeugen const c = createClient(context); // komplett neu erzeugen
if (!c) return;
client = c;
try { try {
await client.start(); await client.start();
vscode.window.showInformationMessage('Java-TX Language Server neu gestartet.'); vscode.window.showInformationMessage('Java-TX Language Server neu gestartet.');
+11
View File
@@ -0,0 +1,11 @@
## Install emacs plugin:
Edit your .emacs file and add the following:
```
(use-package javatx-mode
:custom
(javatx-compiler-path "$PATH_TO_COMPILER$")
(javatx-lsp-server-path "$PATH_TO_LSP$")
:load-path "~/.emacs.d/lisp")
```
+84
View File
@@ -0,0 +1,84 @@
;;; javatx-mode.el --- Major mode for .jav files -*- lexical-binding: t; -*-
(defvar javatx-mode-hook nil
"Hook called when entering `javatx-mode`.")
;; Define the mode
(define-derived-mode javatx-mode java-mode "Javatx"
"Major mode for editing `.jav` files.")
;; Automatically use javatx-mode for .jav files
;;;###autoload
(add-to-list 'auto-mode-alist '("\\.jav\\'" . javatx-mode))
(provide 'javatx-mode)
;;; javatx-mode.el ends here
;; Initialize package sources
(require 'package)
(setq package-archives
'(("melpa" . "https://melpa.org/packages/")
("gnu" . "https://elpa.gnu.org/packages/")))
(package-initialize)
;; Refresh package contents if needed
(unless package-archive-contents
(package-refresh-contents))
;; Install use-package if not installed
(unless (package-installed-p 'use-package)
(package-install 'use-package))
(require 'use-package)
(setq use-package-always-ensure t) ;; automatically install packages if missing
(use-package lsp-mode
:hook (prog-mode . lsp)
:commands lsp
:config
(setq lsp-prefer-flymake nil)) ;; use flycheck instead of flymake
(use-package lsp-ui
:commands lsp-ui-mode
:hook (lsp-mode . lsp-ui-mode)
:config
(setq lsp-ui-sideline-show-hover t
lsp-ui-sideline-show-code-actions t
lsp-ui-sideline-show-diagnostics t))
(defcustom javatx-compiler-path nil
"Path to the JavaTX Compiler jar."
:type 'string
:group 'javatx)
(defcustom javatx-lsp-server-path nil
"Path to the JavaTX Language Server jar."
:type 'string
:group 'javatx)
;;register javatx-mode lsp
(with-eval-after-load 'lsp-mode
(message "Compiler path: %s" javatx-compiler-path)
(message "Server path: %s" javatx-lsp-server-path)
(add-to-list 'lsp-language-id-configuration '(javatx-mode . "Java-TX"))
(lsp-register-client
(make-lsp-client
:new-connection (lsp-stdio-connection (lambda () `("java" "-cp" ,(format "%s:%s" (expand-file-name javatx-compiler-path) (expand-file-name javatx-lsp-server-path)) "de.dhbw.JavaTXLanguageServerLauncher")))
:major-modes '(javatx-mode)
:server-id 'javatx-lsp-proxy)))
(add-hook 'javatx-mode-hook #'lsp) ;; start LSP automatically for .jav files
;; Automatically enable inlay hints for javatx-mode
(add-hook 'javatx-mode-hook
(lambda ()
;; Replace 'lsp-inlay-hints-mode' with whatever inlay hints function you use
(when (fboundp 'lsp-inlay-hints-mode)
(lsp-inlay-hints-mode 1))))
(setq lsp-log-io t) ;; enable logging of LSP messages
(with-eval-after-load 'lsp-mode
(define-key lsp-mode-map (kbd "C-c a") 'lsp-execute-code-action))
+29 -45
View File
@@ -14,61 +14,22 @@
<version>4.11</version> <version>4.11</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/org.antlr/antlr4 -->
<dependency> <dependency>
<groupId>org.antlr</groupId> <groupId>org.antlr</groupId>
<artifactId>antlr4</artifactId> <artifactId>antlr4</artifactId>
<version>4.11.1</version> <version>4.13.2</version>
</dependency> </dependency>
<dependency>
<groupId>io.github.java-diff-utils</groupId>
<artifactId>java-diff-utils</artifactId>
<version>4.12</version>
</dependency>
<dependency> <dependency>
<groupId>log4j</groupId> <groupId>log4j</groupId>
<artifactId>log4j</artifactId> <artifactId>log4j</artifactId>
<version>1.2.17</version> <version>1.2.17</version>
</dependency> </dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.20.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.20.0</version>
</dependency>
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId> <artifactId>junit-jupiter</artifactId>
<version>5.10.0</version> <version>5.14.0</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>io.github.classgraph</groupId>
<artifactId>classgraph</artifactId>
<version>4.8.172</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>33.2.0-jre</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.ow2.asm/asm -->
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>9.5</version>
</dependency>
<dependency> <dependency>
<groupId>org.eclipse.lsp4j</groupId> <groupId>org.eclipse.lsp4j</groupId>
<artifactId>org.eclipse.lsp4j</artifactId> <artifactId>org.eclipse.lsp4j</artifactId>
@@ -78,6 +39,7 @@
<groupId>de.dhbwstuttgart</groupId> <groupId>de.dhbwstuttgart</groupId>
<artifactId>JavaTXcompiler</artifactId> <artifactId>JavaTXcompiler</artifactId>
<version>0.1</version> <version>0.1</version>
<scope>provided</scope>
</dependency> </dependency>
</dependencies> </dependencies>
<properties> <properties>
@@ -110,7 +72,6 @@
<release>21</release> <release>21</release>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId> <artifactId>maven-assembly-plugin</artifactId>
@@ -134,6 +95,29 @@
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>copy-fat-jar</id>
<phase>package</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target>
<mkdir dir="${project.basedir}/../Clients/VisualStudioCode"/>
<copy
file="${project.build.directory}/${project.build.finalName}-jar-with-dependencies.jar"
todir="${project.basedir}/../Clients/VisualStudioCode"
overwrite="true"/>
</target>
</configuration>
</execution>
</executions>
</plugin>
</plugins> </plugins>
</build> </build>
</project> </project>
@@ -17,6 +17,7 @@ import java.util.concurrent.CompletableFuture;
* */ * */
public class JavaTXLanguageServer implements LanguageServer { public class JavaTXLanguageServer implements LanguageServer {
private static final Logger logger = LogManager.getLogger(JavaTXLanguageServer.class); private static final Logger logger = LogManager.getLogger(JavaTXLanguageServer.class);
public static ClientCapabilities capabilities;
private LanguageClient client; private LanguageClient client;
public void connect(LanguageClient client) { public void connect(LanguageClient client) {
@@ -48,6 +49,7 @@ public class JavaTXLanguageServer implements LanguageServer {
if(params.getWorkspaceFolders() != null && !params.getWorkspaceFolders().isEmpty()) { if(params.getWorkspaceFolders() != null && !params.getWorkspaceFolders().isEmpty()) {
textDocumentService.setFileRoot(params.getWorkspaceFolders()); textDocumentService.setFileRoot(params.getWorkspaceFolders());
} }
JavaTXLanguageServer.capabilities = params.getCapabilities();
return CompletableFuture.supplyAsync(() -> new InitializeResult(capabilities)); return CompletableFuture.supplyAsync(() -> new InitializeResult(capabilities));
} }
@@ -113,8 +113,7 @@ public class JavaTXTextDocumentService implements org.eclipse.lsp4j.services.Tex
if (!syntaxErrors.isEmpty()) { if (!syntaxErrors.isEmpty()) {
clientService.publishDiagnostics(params.getTextDocument().getUri(), syntaxErrors); clientService.publishDiagnostics(params.getTextDocument().getUri(), syntaxErrors);
} }
client.refreshDiagnostics(); clientService.updateClient();
client.refreshInlayHints();
textDocuments.put(params.getTextDocument().getUri(), params.getTextDocument().getText()); textDocuments.put(params.getTextDocument().getUri(), params.getTextDocument().getText());
textDocumentService.saveFileWithUri(params.getTextDocument().getUri(), params.getTextDocument().getText()); textDocumentService.saveFileWithUri(params.getTextDocument().getUri(), params.getTextDocument().getText());
} }
@@ -154,10 +153,10 @@ public class JavaTXTextDocumentService implements org.eclipse.lsp4j.services.Tex
@Override @Override
public void didSave(DidSaveTextDocumentParams didSaveTextDocumentParams) { public void didSave(DidSaveTextDocumentParams didSaveTextDocumentParams) {
logService.log("[didSave] Client triggered didSave-Event."); logService.log("[didSave] Client triggered didSave-Event.");
clientService.startLoading("compile-task", "Inferring types...", client); clientService.startLoading("compile-task", "Inferring types...");
saveHandler.handleSave(didSaveTextDocumentParams); saveHandler.handleSave(didSaveTextDocumentParams);
clientService.stopLoading("compile-task", "Types successfully inferred", client); clientService.stopLoading("compile-task", "Types successfully inferred");
clientService.updateClient(client); clientService.updateClient();
} }
@Override @Override
@@ -298,7 +297,9 @@ public class JavaTXTextDocumentService implements org.eclipse.lsp4j.services.Tex
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("[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); var triggerKind = params.getContext().getTriggerKind();
if (triggerKind != null)
logService.log("Code-Action Context was: " + triggerKind.name(), MessageType.Info);
return CompletableFuture.supplyAsync(() -> { return CompletableFuture.supplyAsync(() -> {
return codeActionHandler.handleNewCodeAction(params); return codeActionHandler.handleNewCodeAction(params);
@@ -3,6 +3,7 @@ package de.dhbw.handler;
import com.google.common.base.Stopwatch; import com.google.common.base.Stopwatch;
import de.dhbw.helper.ConversionHelper; import de.dhbw.helper.ConversionHelper;
import de.dhbw.helper.TypeResolver; import de.dhbw.helper.TypeResolver;
import de.dhbw.helper.TypeResolver.InferenceResult;
import de.dhbw.model.*; import de.dhbw.model.*;
import de.dhbw.service.*; import de.dhbw.service.*;
import de.dhbwstuttgart.syntaxtree.factory.NameGenerator; import de.dhbwstuttgart.syntaxtree.factory.NameGenerator;
@@ -80,6 +81,8 @@ public class ChangeHandler {
if (!syntaxErrors.isEmpty()) { if (!syntaxErrors.isEmpty()) {
clientService.publishDiagnostics(params.getTextDocument().getUri(), syntaxErrors); clientService.publishDiagnostics(params.getTextDocument().getUri(), syntaxErrors);
}else {
clientService.updateClient();
} }
logService.log("Found [" + syntaxErrors.size() + "] Syntax Errors in Document."); logService.log("Found [" + syntaxErrors.size() + "] Syntax Errors in Document.");
@@ -105,110 +108,101 @@ public class ChangeHandler {
logService.log("Should Calculate is: " + shouldCalculate); logService.log("Should Calculate is: " + shouldCalculate);
if (false) {
try { /*try {
File tempDir = new File(System.getProperty("java.io.tmpdir")); File tempDir = new File(System.getProperty("java.io.tmpdir"));
File tempFile = File.createTempFile("newText", ".tmp", tempDir); File tempFile = File.createTempFile("newText", ".tmp", tempDir);
FileWriter fileWriter = new FileWriter(tempFile, true); FileWriter fileWriter = new FileWriter(tempFile, true);
System.out.println(tempFile.getAbsolutePath()); System.out.println(tempFile.getAbsolutePath());
BufferedWriter bw = new BufferedWriter(fileWriter); BufferedWriter bw = new BufferedWriter(fileWriter);
bw.write(summedUp.get()); bw.write(summedUp.get());
bw.close(); bw.close();
typeResolver.updateAst(tempFile.toURI().getPath()); typeResolver.updateAst(tempFile.toURI().getPath());
tempFile.delete(); tempFile.delete();
} catch (Exception e) { } catch (Exception e) {
logService.log(e.getMessage()); 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());
} }
var sWatch = Stopwatch.createUnstarted(); clientService.showMessage(MessageType.Error, e.getMessage());
sWatch.start(); 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);*/
logService.log("Calculating again.");
var sWatch = Stopwatch.createUnstarted();
sWatch.start();
try {
try { try {
ArrayList<LSPVariable> typesOfMethodAndParameters = typeResolver.infereChangeInput(); File tempDir2 = Path.of(new URI(params.getTextDocument().getUri())).getParent().toFile();
DiagnosticsAndTypehints diagnosticsAndTypehints = conversionHelper.variablesToDiagnosticsAndTypehints(typesOfMethodAndParameters, params.getTextDocument().getUri()); File tempFile2 = File.createTempFile("jtx_temp", ".tmp", tempDir2);
FileWriter fileWriter = new FileWriter(tempFile2, true);
BufferedWriter bw = new BufferedWriter(fileWriter);
bw.write(input);
bw.close();
List<InlayHint> typeHint = diagnosticsAndTypehints.getInlayHints(); try {
List<Diagnostic> diagnostics = diagnosticsAndTypehints.getDiagnostics(); InferenceResult inferenceResult = typeResolver.infereInput(tempFile2.toURI().toString(), input);
cacheService.setVariables(inferenceResult.variables());
cacheService.updateGlobalMaps(diagnostics, typeHint, params.getTextDocument().getUri()); DiagnosticsAndTypehints diagnosticsAndTypehints = conversionHelper.variablesToDiagnosticsAndTypehintsWithInput(inferenceResult, input);
} catch (Exception e) { List<InlayHint> typeHint = diagnosticsAndTypehints.getInlayHints();
logService.log("[didChange] Error trying to get Inlay-Hints and Diagnostics for Client: " + e.getMessage(), MessageType.Error); List<Diagnostic> diagnostics = diagnosticsAndTypehints.getDiagnostics();
for (var stackTrace : e.getStackTrace()) {
logService.log(stackTrace.toString()); cacheService.updateGlobalMaps(diagnostics, typeHint, params.getTextDocument().getUri());
List<Diagnostic> allDiagnostics = new ArrayList<>(diagnostics);
clientService.publishDiagnostics(params.getTextDocument().getUri(), allDiagnostics);
} catch (Exception | VerifyError f) {
logService.log("[didSave] Error trying to get Inlay-Hints and Diagnostics for Client: " + f.getMessage(), MessageType.Error);
for (StackTraceElement elem : f.getStackTrace()) {
logService.log(elem.toString());
}
clientService.showMessage(MessageType.Error, f.getMessage() == null ? "null" : f.getMessage());
cacheService.updateGlobalMaps(new ArrayList<>(), new ArrayList<>(), params.getTextDocument().getUri());
} }
tempFile2.delete();
clientService.showMessage(MessageType.Error, e.getMessage()); } catch (Exception e) {
logService.log("[didSave] Error trying to get Inlay-Hints and Diagnostics for Client: " + e.getMessage(), MessageType.Error);
clientService.showMessage(MessageType.Error, e.getMessage() == null ? "null" : e.getMessage());
cacheService.updateGlobalMaps(new ArrayList<>(), new ArrayList<>(), params.getTextDocument().getUri()); cacheService.updateGlobalMaps(new ArrayList<>(), new ArrayList<>(), params.getTextDocument().getUri());
} finally { } finally {
sWatch.stop(); sWatch.stop();
logService.log("[didChange] Finished Calculating in [" + sWatch.elapsed().toSeconds() + "s]", MessageType.Info); logService.log("[didSave] 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 = Path.of(new URI(params.getTextDocument().getUri())).getParent().toFile();
File tempFile2 = File.createTempFile("jtx_temp", ".tmp", tempDir2);
FileWriter fileWriter = new FileWriter(tempFile2, true);
BufferedWriter bw = new BufferedWriter(fileWriter);
bw.write(input);
bw.close();
try {
ArrayList<LSPVariable> variables = typeResolver.infereInput(tempFile2.toURI().toString(), input, true);
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);
} catch (Exception f) {
logService.log("[didSave] Error trying to get Inlay-Hints and Diagnostics for Client: " + f.getMessage(), MessageType.Error);
for (StackTraceElement elem : f.getStackTrace()) {
logService.log(elem.toString());
}
clientService.showMessage(MessageType.Error, f.getMessage() == null ? "null" : f.getMessage());
cacheService.updateGlobalMaps(new ArrayList<>(), new ArrayList<>(), params.getTextDocument().getUri());
}
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 e) {
logService.log(e.getMessage());
} }
} }
} }
} }
@@ -1,6 +1,8 @@
package de.dhbw.handler; package de.dhbw.handler;
import de.dhbw.helper.ASTTransformationHelper;
import de.dhbw.helper.ConversionHelper; import de.dhbw.helper.ConversionHelper;
import de.dhbw.helper.InsertPoint;
import de.dhbw.helper.TextHelper; import de.dhbw.helper.TextHelper;
import de.dhbw.helper.TypeResolver; import de.dhbw.helper.TypeResolver;
import de.dhbw.service.CacheService; import de.dhbw.service.CacheService;
@@ -68,8 +70,8 @@ public class CodeActionHandler {
} }
public static <V> Map<String, V> getOverlapping( public static <V> Map<InsertPoint, V> getOverlapping(
Map<String, V> map, Map<InsertPoint, V> map,
int line, int line,
int startChar, int startChar,
int endChar int endChar
@@ -83,20 +85,13 @@ public class CodeActionHandler {
final int s = startChar, e = endChar; final int s = startChar, e = endChar;
return map.entrySet().stream() return map.entrySet().stream()
.filter(entry -> { .filter(entry -> {
String key = entry.getKey(); InsertPoint key = entry.getKey();
String[] parts = key.split("\\s+"); int kLine = key.line();
if (parts.length != 2) return false; int kChar = key.character();
try { return kLine == line && kChar >= s && kChar <= e;
int kLine = Integer.parseInt(parts[0]); })
int kChar = Integer.parseInt(parts[1]); .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
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));
} }
@@ -105,43 +100,53 @@ public class CodeActionHandler {
String documentUri = params.getTextDocument().getUri(); String documentUri = params.getTextDocument().getUri();
Range rangeOfInsert = params.getRange(); 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 if (typeResolver.getInserts() != null) {
Map<String, List<PlaceholderVariable>> typeInsertsOverlapping = getOverlapping(typeResolver.getInserts(), rangeOfInsert.getStart().getLine() + 1, rangeOfInsert.getStart().getCharacter(), rangeOfInsert.getEnd().getCharacter()); //All Diagnostics that are in range of the hover -> All Diagnostics of the selected Variable and thus all Types of the Variable
logService.log("Inserts are:"); Map<InsertPoint, List<PlaceholderVariable>> typeInsertsOverlapping = getOverlapping(typeResolver.getInserts(), rangeOfInsert.getStart().getLine() + 1, rangeOfInsert.getStart().getCharacter(), rangeOfInsert.getEnd().getCharacter());
typeResolver.getInserts().forEach((key, value) -> logService.log(key)); logService.log("Inserts are:");
logService.log("Size is: " + typeInsertsOverlapping.size()); typeResolver.getInserts().forEach((key, value) -> logService.log(key.toString()));
logService.log("Range is: " + rangeOfInsert.getStart().getLine() + " -> " + rangeOfInsert.getStart().getCharacter() + " - " + rangeOfInsert.getEnd().getCharacter()); logService.log("Size is: " + typeInsertsOverlapping.size());
List<Either<Command, CodeAction>> actions = new ArrayList<>(); 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 typeInsertList : typeInsertsOverlapping.values()) {
for (var typeInsert : typeInsertList) { for (var typeInsert : typeInsertList) {
try { try {
logService.log("NEW TEXT OF FILE BEFORE INSERT IS:"); logService.log("NEW TEXT OF FILE BEFORE INSERT IS:");
logService.log(textDocumentService.getFileOfUri(documentUri)); logService.log(textDocumentService.getFileOfUri(documentUri));
String typeWithReplacedVariable = typeInsert.insert(textDocumentService.getFileOfUri(documentUri)); String typeWithReplacedVariable = typeInsert.insert(textDocumentService.getFileOfUri(documentUri));
ArrayList<TextEdit> listOfChanges = new ArrayList<>(); ArrayList<TextEdit> listOfChanges = new ArrayList<>();
listOfChanges.add(new TextEdit(wholeDocumentRange(textDocumentService.getFileOfUri(documentUri)), typeWithReplacedVariable)); listOfChanges.add(new TextEdit(wholeDocumentRange(textDocumentService.getFileOfUri(documentUri)), typeWithReplacedVariable));
var isTypeImported = false; Map<String, List<TextEdit>> changes = new HashMap<>();
changes.put(documentUri, listOfChanges);
Map<String, List<TextEdit>> changes = new HashMap<>(); WorkspaceEdit edit = new WorkspaceEdit();
changes.put(documentUri, listOfChanges); edit.setChanges(changes);
WorkspaceEdit edit = new WorkspaceEdit(); var constraints = typeInsert.getConstraints();
edit.setChanges(changes); var genericsString = "";
if (constraints != null && !constraints.isEmpty()) {
genericsString = ASTTransformationHelper.createConstraintsString(constraints);
}
CodeAction action = new CodeAction("Insert " + ConversionHelper.cleanType(typeInsert.getInsertString()) + genericsString);
action.setKind(CodeActionKind.QuickFix);
action.setEdit(edit);
CodeAction action = new CodeAction("Insert " + conversionHelper.cleanType(typeInsert.getInsertString())); if(!actions.stream().map(el -> el.getRight().getTitle()).toList().contains(action.getTitle())){
action.setKind(CodeActionKind.QuickFix); actions.add(Either.forRight(action));
action.setEdit(edit); }
actions.add(Either.forRight(action)); } catch (Exception e) {
} catch (Exception e) { logService.log("Error creating Actions, returning empty List. The Error was: " + e.getMessage(), MessageType.Error);
logService.log("Error creating Actions, returning empty List. The Error was: " + e.getMessage(), MessageType.Error); }
} }
} }
return actions;
} }
return actions;
return new ArrayList<>();
} }
public List<Either<Command, CodeAction>> handleCodeAction(CodeActionParams params) { public List<Either<Command, CodeAction>> handleCodeAction(CodeActionParams params) {
@@ -1,10 +1,12 @@
package de.dhbw.handler; package de.dhbw.handler;
import com.google.common.base.Stopwatch; import com.google.common.base.Stopwatch;
import com.google.common.base.Verify;
import de.dhbw.model.DiagnosticsAndTypehints; import de.dhbw.model.DiagnosticsAndTypehints;
import de.dhbw.service.*; import de.dhbw.service.*;
import de.dhbw.helper.ConversionHelper; import de.dhbw.helper.ConversionHelper;
import de.dhbw.helper.TypeResolver; import de.dhbw.helper.TypeResolver;
import de.dhbw.helper.TypeResolver.InferenceResult;
import de.dhbw.model.LSPVariable; import de.dhbw.model.LSPVariable;
import de.dhbwstuttgart.languageServerInterface.LanguageServerInterface; import de.dhbwstuttgart.languageServerInterface.LanguageServerInterface;
import de.dhbwstuttgart.languageServerInterface.model.ParserError; import de.dhbwstuttgart.languageServerInterface.model.ParserError;
@@ -51,22 +53,23 @@ public class SaveHandler {
List<Diagnostic> syntaxErrors = parserService.getDiagnosticsOfErrors(fileInput, didSaveTextDocumentParams.getTextDocument().getUri()); List<Diagnostic> syntaxErrors = parserService.getDiagnosticsOfErrors(fileInput, didSaveTextDocumentParams.getTextDocument().getUri());
if (!syntaxErrors.isEmpty()) { if (!syntaxErrors.isEmpty()) {
clientService.publishDiagnostics(didSaveTextDocumentParams.getTextDocument().getUri(), syntaxErrors); clientService.publishDiagnostics(didSaveTextDocumentParams.getTextDocument().getUri(), syntaxErrors);
}else {
clientService.updateClient();
} }
logService.log("Found [" + syntaxErrors.size() + "] Syntax Errors in Document."); logService.log("Found [" + syntaxErrors.size() + "] Syntax Errors in Document.");
if (syntaxErrors.isEmpty()) { if (syntaxErrors.isEmpty()) {
cacheService.getLastSavedFiles().put(didSaveTextDocumentParams.getTextDocument().getUri(), fileInput); cacheService.getLastSavedFiles().put(didSaveTextDocumentParams.getTextDocument().getUri(), fileInput);
//typeResolver.getCompilerInput(didSaveTextDocumentParams.getTextDocument().getUri(), fileInput);
if (fileInput == null) { if (fileInput == null) {
logService.log("[didSave] Input of Text Document is null in TextDocument-Hashmap.", MessageType.Error); logService.log("[didSave] Input of Text Document is null in TextDocument-Hashmap.", MessageType.Error);
} }
ArrayList<LSPVariable> variables = typeResolver.infereInput(didSaveTextDocumentParams.getTextDocument().getUri(), fileInput, false); InferenceResult inferenceResult = typeResolver.infereInput(didSaveTextDocumentParams.getTextDocument().getUri(), fileInput);
cacheService.setVariables(variables); cacheService.setVariables(inferenceResult.variables());
DiagnosticsAndTypehints diagnosticsAndTypehints = conversionHelper.variablesToDiagnosticsAndTypehints(variables, didSaveTextDocumentParams.getTextDocument().getUri()); DiagnosticsAndTypehints diagnosticsAndTypehints = conversionHelper.variablesToDiagnosticsAndTypehints(inferenceResult, didSaveTextDocumentParams.getTextDocument().getUri());
List<InlayHint> typeHint = diagnosticsAndTypehints.getInlayHints(); List<InlayHint> typeHint = diagnosticsAndTypehints.getInlayHints();
List<Diagnostic> diagnostics = diagnosticsAndTypehints.getDiagnostics(); List<Diagnostic> diagnostics = diagnosticsAndTypehints.getDiagnostics();
@@ -79,16 +82,13 @@ public class SaveHandler {
} }
} catch (Exception e) { } catch (Exception | VerifyError e) {
logService.log("[didSave] Error trying to get Inlay-Hints and Diagnostics for Client: " + e.getMessage(), MessageType.Error); 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()); clientService.showMessage(MessageType.Error, e.getMessage() == null ? "null" : e.getMessage());
cacheService.updateGlobalMaps(new ArrayList<>(), new ArrayList<>(), didSaveTextDocumentParams.getTextDocument().getUri()); cacheService.updateGlobalMaps(new ArrayList<>(), new ArrayList<>(), didSaveTextDocumentParams.getTextDocument().getUri());
}
} finally { finally {
sWatch.stop(); sWatch.stop();
logService.log("[didSave] Finished Calculating in [" + sWatch.elapsed().toSeconds() + "s]", MessageType.Info); logService.log("[didSave] Finished Calculating in [" + sWatch.elapsed().toSeconds() + "s]", MessageType.Info);
} }
@@ -15,12 +15,13 @@ import de.dhbwstuttgart.typeinference.result.*;
import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.Token;
import java.util.HashSet; import java.util.HashSet;
import java.util.Optional;
import java.util.Set; import java.util.Set;
public class ASTTransformationHelper { public class ASTTransformationHelper {
public static Set<PlaceholderVariable> createTypeInsertPoints(SourceFile forSourcefile, ResultSet withResults, GenericsResult generics) { public static Set<PlaceholderVariable> createTypeInsertPoints(SourceFile forSourcefile, GenericsResult generics) {
return new PlaceholderPlacer().getTypeInserts(forSourcefile, withResults, generics); return new PlaceholderPlacer(forSourcefile, generics).getTypeInserts();
} }
public static PlaceholderVariable createInsertPoints(RefTypeOrTPHOrWildcardOrGeneric type, Token offset, ClassOrInterface cl, Method m, public static PlaceholderVariable createInsertPoints(RefTypeOrTPHOrWildcardOrGeneric type, Token offset, ClassOrInterface cl, Method m,
@@ -29,27 +30,20 @@ public class ASTTransformationHelper {
ResolvedType resolvedType = resultSet.resolveType(type); ResolvedType resolvedType = resultSet.resolveType(type);
PlaceholderPoint insertPoint = new PlaceholderPoint(offset, PlaceholderPoint insertPoint = new PlaceholderPoint(offset,
new PlaceholderToInsertString(resolvedType.resolvedType, constraints, classConstraints).insert, PlaceholderType.NORMAL_INSERT); new PlaceholderToInsertString(resolvedType.resolvedType, constraints, classConstraints).insert, PlaceholderType.NORMAL_INSERT);
return new PlaceholderVariable(insertPoint, createGenericInsert(constraints, classConstraints, cl, m, resultSet, offset), resolvedType.getResultPair()); return new PlaceholderVariable(insertPoint, createGenericInsert(constraints, classConstraints, cl, m, offset), resolvedType.getResultPair(), constraints);
} }
private static synchronized Set<PlaceholderPoint> createGenericInsert(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, Token mOffset){
Set<PlaceholderPoint> result = createGenericClassInserts(classConstraints, cl); Set<PlaceholderPoint> result = createGenericClassInserts(classConstraints, cl);
if (m != null) { if (m != null) {
result.addAll(createMethodConstraints(methodConstraints, m.getOffset() != null ? m.getOffset() : mOffset)); createMethodConstraints(methodConstraints, m.getOffset() != null ? m.getOffset() : mOffset).ifPresent(result::add);
} }
return result; return result;
} }
private static Set<PlaceholderPoint> createMethodConstraints(GenericsResultSet constraints, Token mOffset) {
Set<PlaceholderPoint> result = new HashSet<>();
Token offset = mOffset;
if (constraints.size() == 0) { public static String createConstraintsString(GenericsResultSet constraints) {
return result;
}
String insert = " <"; String insert = " <";
for (var genericInsertConstraint : constraints) { for (var genericInsertConstraint : constraints) {
@@ -63,9 +57,16 @@ public class ASTTransformationHelper {
insert = insert.substring(0, insert.length() -2); insert = insert.substring(0, insert.length() -2);
insert += ">"; insert += ">";
result.add(new PlaceholderPoint(offset, insert, PlaceholderType.GENERERIC_METHOD_INSERT)); return insert;
return result; }
private static Optional<PlaceholderPoint> createMethodConstraints(GenericsResultSet constraints, Token mOffset) {
Token offset = mOffset;
if (constraints.size() == 0) {
return Optional.empty();
}
return Optional.of(new PlaceholderPoint(offset, createConstraintsString(constraints), PlaceholderType.GENERERIC_METHOD_INSERT));
} }
private static Set<PlaceholderPoint> createGenericClassInserts(GenericsResultSet classConstraints, ClassOrInterface cl) { private static Set<PlaceholderPoint> createGenericClassInserts(GenericsResultSet classConstraints, ClassOrInterface cl) {
@@ -75,23 +76,7 @@ public class ASTTransformationHelper {
if (classConstraints == null || classConstraints.size() == 0) { if (classConstraints == null || classConstraints.size() == 0) {
return result; return result;
} }
result.add(new PlaceholderPoint(offset, createConstraintsString(classConstraints), PlaceholderType.GENERIC_CLASS_INSERT));
String insert = " <";
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 += ", ";
}
insert = insert.substring(0, insert.length() -2);
insert += ">";
result.add(new PlaceholderPoint(offset, insert, PlaceholderType.GENERIC_CLASS_INSERT));
return result; return result;
} }
} }
@@ -1,9 +1,11 @@
package de.dhbw.helper; package de.dhbw.helper;
import de.dhbw.helper.TypeResolver.InferenceResult;
import de.dhbw.model.DiagnosticsAndTypehints; import de.dhbw.model.DiagnosticsAndTypehints;
import de.dhbw.service.TextDocumentService; import de.dhbw.service.TextDocumentService;
import de.dhbw.model.LSPVariable; import de.dhbw.model.LSPVariable;
import de.dhbw.model.Type; import de.dhbw.model.Type;
import de.dhbwstuttgart.exceptions.CompilerWarning;
import de.dhbwstuttgart.languageServerInterface.model.ParserError; import de.dhbwstuttgart.languageServerInterface.model.ParserError;
import org.eclipse.lsp4j.*; import org.eclipse.lsp4j.*;
@@ -20,8 +22,9 @@ public class ConversionHelper {
this.textDocumentService = textDocumentService; this.textDocumentService = textDocumentService;
} }
public String cleanType(String type){ public static String cleanType(String type) {
return type.replaceAll("java.lang.", "").replaceAll("java.util.", ""); // Strip package from type string for better readability in diagnostics and inlay hints
return type.replaceAll("\\b(?:[A-Za-z_]\\w*\\.)+(\\w+)\\b", "$1");
} }
public InlayHint getInlayHint(LSPVariable variable) { public InlayHint getInlayHint(LSPVariable variable) {
@@ -29,7 +32,7 @@ public class ConversionHelper {
String typeDisplay = ""; String typeDisplay = "";
for (Type type : variable.getPossibleTypes()) { for (Type type : variable.getPossibleTypes()) {
typeDisplay += " | " + cleanType(type.getType().replaceAll("GTV ", "")); typeDisplay += "| " + cleanType(type.getType().replaceAll("GTV ", ""));
} }
@@ -90,11 +93,35 @@ public class ConversionHelper {
}).toList(); }).toList();
} }
public DiagnosticsAndTypehints variablesToDiagnosticsAndTypehints(ArrayList<LSPVariable> typesOfMethodAndParameters, String uri) { public DiagnosticsAndTypehints addWarningsToDiagnostics(DiagnosticsAndTypehints diagnosticsAndTypehints, List<CompilerWarning> warnings) {
List<Diagnostic> diagnostics = new ArrayList<>(diagnosticsAndTypehints.getDiagnostics());
for (var warning : warnings) {
var offset = warning.getOffset();
Range warningRange = new Range(
new Position(offset.getLine() - 1, offset.getCharPositionInLine()),
new Position(offset.getLine() - 1, offset.getCharPositionInLine() + offset.getText().length())
);
Diagnostic diagnostic = new Diagnostic(
warningRange,
warning.getMessage(),
DiagnosticSeverity.Warning,
"JavaTX Language Server"
);
diagnostics.add(diagnostic);
}
return new DiagnosticsAndTypehints(diagnostics, diagnosticsAndTypehints.getInlayHints());
}
public DiagnosticsAndTypehints variablesToDiagnosticsAndTypehints(InferenceResult inferenceResult, String uri) {
var diagnosticsAndTypehints = variablesToDiagnosticsAndTypehints(inferenceResult.variables(), uri);
return addWarningsToDiagnostics(diagnosticsAndTypehints, inferenceResult.warnings());
}
public DiagnosticsAndTypehints variablesToDiagnosticsAndTypehints(ArrayList<LSPVariable> variables, String uri) {
List<InlayHint> typeHint = new ArrayList<>(); List<InlayHint> typeHint = new ArrayList<>();
ArrayList<Diagnostic> diagnostics = new ArrayList<>(); ArrayList<Diagnostic> diagnostics = new ArrayList<>();
for (var variable : typesOfMethodAndParameters) { for (var variable : variables) {
InlayHint inlayHint = getInlayHint(variable); InlayHint inlayHint = getInlayHint(variable);
typeHint.add(inlayHint); typeHint.add(inlayHint);
@@ -108,11 +135,11 @@ public class ConversionHelper {
return new DiagnosticsAndTypehints(diagnostics, typeHint); return new DiagnosticsAndTypehints(diagnostics, typeHint);
} }
public DiagnosticsAndTypehints variablesToDiagnosticsAndTypehintsWithInput(ArrayList<LSPVariable> typesOfMethodAndParameters, String input) { public DiagnosticsAndTypehints variablesToDiagnosticsAndTypehintsWithInput(InferenceResult inferenceResult, String input) {
List<InlayHint> typeHint = new ArrayList<>(); List<InlayHint> typeHint = new ArrayList<>();
ArrayList<Diagnostic> diagnostics = new ArrayList<>(); ArrayList<Diagnostic> diagnostics = new ArrayList<>();
for (var variable : typesOfMethodAndParameters) { for (var variable : inferenceResult.variables()) {
InlayHint inlayHint = getInlayHint(variable); InlayHint inlayHint = getInlayHint(variable);
typeHint.add(inlayHint); typeHint.add(inlayHint);
@@ -123,6 +150,6 @@ public class ConversionHelper {
} }
} }
return new DiagnosticsAndTypehints(diagnostics, typeHint); return addWarningsToDiagnostics(new DiagnosticsAndTypehints(diagnostics, typeHint), inferenceResult.warnings());
} }
} }
@@ -0,0 +1,3 @@
package de.dhbw.helper;
public record InsertPoint(int line, int character) {}
@@ -3,29 +3,32 @@ package de.dhbw.helper;
import de.dhbw.model.PlaceholderVariable; import de.dhbw.model.PlaceholderVariable;
import de.dhbwstuttgart.syntaxtree.*; import de.dhbwstuttgart.syntaxtree.*;
import de.dhbwstuttgart.target.generate.GenericsResult; import de.dhbwstuttgart.target.generate.GenericsResult;
import de.dhbwstuttgart.typeinference.result.ResultSet;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
public class PlaceholderPlacer extends AbstractASTWalker { public class PlaceholderPlacer extends AbstractASTWalker {
Set<PlaceholderVariable> inserts = new HashSet<>(); Set<PlaceholderVariable> inserts = new HashSet<>();
private ResultSet withResults;
String pkgName; String pkgName;
private GenericsResult genericsResult; private GenericsResult genericsResult;
private SourceFile forSourceFile;
public Set<PlaceholderVariable> getTypeInserts(SourceFile forSourceFile, ResultSet withResults, GenericsResult genericsResult){ public PlaceholderPlacer(SourceFile forSourceFile, GenericsResult genericsResult) {
this.withResults = withResults; this.forSourceFile = forSourceFile;
this.genericsResult = genericsResult; this.genericsResult = genericsResult;
pkgName = forSourceFile.getPkgName(); this.pkgName = forSourceFile.getPkgName();
forSourceFile.accept(this); }
inserts.forEach(el -> el.reducePackage());
return inserts; public Set<PlaceholderVariable> getTypeInserts() {
this.inserts = new HashSet<>();
this.forSourceFile.accept(this);
this.inserts.forEach(el -> el.reducePackage());
return this.inserts;
} }
@Override @Override
public void visit(ClassOrInterface classOrInterface) { public void visit(ClassOrInterface classOrInterface) {
de.dhbw.helper.PlaceholderPlacerClass cl = new de.dhbw.helper.PlaceholderPlacerClass(classOrInterface, withResults, genericsResult); de.dhbw.helper.PlaceholderPlacerClass cl = new de.dhbw.helper.PlaceholderPlacerClass(classOrInterface, genericsResult);
this.inserts.addAll(cl.inserts); this.inserts.addAll(cl.inserts);
} }
} }
@@ -21,10 +21,10 @@ class PlaceholderPlacerClass extends AbstractASTWalker{
GenericsResultSet constraints; GenericsResultSet constraints;
GenericsResultSet classConstraints; GenericsResultSet classConstraints;
PlaceholderPlacerClass(ClassOrInterface forClass, ResultSet withResults, GenericsResult generatedGenerics){ PlaceholderPlacerClass(ClassOrInterface forClass, GenericsResult generatedGenerics) {
this.cl = forClass; this.cl = forClass;
this.method = null; this.method = null;
this.results = withResults; this.results = generatedGenerics.getGenerics().getResultSet();
this.generatedGenerics = generatedGenerics; this.generatedGenerics = generatedGenerics;
forClass.accept(this); forClass.accept(this);
} }
@@ -32,7 +32,7 @@ class PlaceholderPlacerClass extends AbstractASTWalker{
@Override @Override
public void visit(Method method) { public void visit(Method method) {
this.method = method; this.method = method;
constraints = generatedGenerics.get(method); constraints = generatedGenerics.get(cl, method);
classConstraints = generatedGenerics.get(cl); classConstraints = generatedGenerics.get(cl);
if(method.getReturnType() instanceof TypePlaceholder) if(method.getReturnType() instanceof TypePlaceholder)
inserts.add(ASTTransformationHelper.createInsertPoints( inserts.add(ASTTransformationHelper.createInsertPoints(
@@ -1,6 +1,7 @@
package de.dhbw.helper; package de.dhbw.helper;
import de.dhbwstuttgart.syntaxtree.type.*; import de.dhbwstuttgart.syntaxtree.type.*;
import de.dhbwstuttgart.target.generate.GenerateGenerics;
import de.dhbwstuttgart.target.generate.GenericsResultSet; import de.dhbwstuttgart.target.generate.GenericsResultSet;
import de.dhbwstuttgart.typeinference.result.*; import de.dhbwstuttgart.typeinference.result.*;
@@ -60,14 +61,14 @@ public class PlaceholderToInsertString implements ResultSetVisitor{
@Override @Override
public void visit(TypePlaceholder typePlaceholder) { public void visit(TypePlaceholder typePlaceholder) {
ResultPair<?, ?> resultPair = null; GenerateGenerics.Pair resultPair = null;
if (constraints != null) if (constraints != null)
resultPair = constraints.getResultPairFor(typePlaceholder).orElse(null); resultPair = constraints.getResultPairFor(typePlaceholder).orElse(null);
if (resultPair == null) if (resultPair == null)
resultPair = classConstraints.getResultPairFor(typePlaceholder).orElse(null); resultPair = classConstraints.getResultPairFor(typePlaceholder).orElse(null);
if (resultPair != null) if (resultPair != null)
insert += ((TypePlaceholder)resultPair.getLeft()).getName(); insert += resultPair.left.toString();
} }
@Override @Override
@@ -3,30 +3,21 @@ package de.dhbw.helper;
import de.dhbw.model.*; import de.dhbw.model.*;
import de.dhbw.model.PlaceholderVariable; import de.dhbw.model.PlaceholderVariable;
import de.dhbwstuttgart.core.JavaTXCompiler; import de.dhbwstuttgart.exceptions.CompilerWarning;
import de.dhbwstuttgart.languageServerInterface.LanguageServerInterface; import de.dhbwstuttgart.languageServerInterface.LanguageServerInterface;
import de.dhbwstuttgart.languageServerInterface.model.LanguageServerTransferObject; import de.dhbwstuttgart.languageServerInterface.model.LanguageServerTransferObject;
import de.dhbwstuttgart.parser.scope.JavaClassName;
import de.dhbwstuttgart.syntaxtree.SourceFile; import de.dhbwstuttgart.syntaxtree.SourceFile;
import de.dhbwstuttgart.syntaxtree.factory.NameGenerator;
import de.dhbwstuttgart.syntaxtree.visual.ASTPrinter; import de.dhbwstuttgart.syntaxtree.visual.ASTPrinter;
import de.dhbwstuttgart.target.generate.GenericsResult; import de.dhbwstuttgart.target.generate.GenericsResult;
import de.dhbwstuttgart.typedeployment.TypeInsert;
import de.dhbwstuttgart.typeinference.result.ResultSet;
import org.apache.log4j.LogManager; import org.apache.log4j.LogManager;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.PrintStream; import java.io.PrintStream;
import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*; import java.util.*;
/** /**
* Helper-Class for finding the Type of a selected Word * Helper-Class for finding the Type of a selected Word
*/ */
@@ -37,10 +28,10 @@ public class TypeResolver {
private final DuplicationUtils duplicationUtils; private final DuplicationUtils duplicationUtils;
private final boolean ENABLE_GENERICS = true; private final boolean ENABLE_GENERICS = true;
private List<GenericsResult> generatedGenerics = null; private List<GenericsResult> generatedGenerics = null;
private HashMap<String, List<PlaceholderVariable>> inserts; private HashMap<InsertPoint, List<PlaceholderVariable>> inserts;
public HashMap<String, List<PlaceholderVariable>> getInserts() { public HashMap<InsertPoint, List<PlaceholderVariable>> getInserts() {
return inserts; return inserts;
} }
@@ -50,9 +41,7 @@ public class TypeResolver {
private LanguageServerTransferObject current; private LanguageServerTransferObject current;
public void reset() { public void reset() {
HashMap<String, List<TypeInsert>> inserts = null; this.current = null;
List<GenericsResult> generatedGenerics = null;
current = null;
} }
@@ -82,7 +71,7 @@ public class TypeResolver {
public void getCompilerInput(String path, String input) throws IOException, URISyntaxException, ClassNotFoundException { public void getCompilerInput(String path, String input) throws IOException, URISyntaxException, ClassNotFoundException {
LanguageServerInterface languageServer = new LanguageServerInterface(); LanguageServerInterface languageServer = new LanguageServerInterface();
var transferObj = languageServer.getResultSetAndAbastractSyntax(path, RESET_TO_LETTER); var transferObj = languageServer.getResultSetAndAbstractSyntax(path, RESET_TO_LETTER);
current = transferObj; current = transferObj;
} }
@@ -99,25 +88,37 @@ public class TypeResolver {
Set<PlaceholderVariable> tips = new HashSet<>(); Set<PlaceholderVariable> tips = new HashSet<>();
for (int i = 0; i < current.getResultSets().size(); i++) { for (var results : generatedGenerics) {
ResultSet tiResult = current.getResultSets().get(i); tips.addAll(ASTTransformationHelper.createTypeInsertPoints(current.getAst(), results));
tips.addAll(ASTTransformationHelper.createTypeInsertPoints(current.getAst(), tiResult, generatedGenerics.get(i)));
} }
HashMap<String, List<PlaceholderVariable>> insertsOnLines = new HashMap<>(); HashMap<InsertPoint, List<PlaceholderVariable>> insertsOnLines = new HashMap<>();
for (PlaceholderVariable insert : tips) { for (PlaceholderVariable insert : tips) {
if (!insertsOnLines.containsKey(insert.point.point.getLine() + " " + insert.point.point.getCharPositionInLine())) { var insertPoint = new InsertPoint(insert.point.point.getLine(), insert.point.point.getCharPositionInLine());
insertsOnLines.put(insert.point.point.getLine() + " " + insert.point.point.getCharPositionInLine(), new ArrayList<>(List.of(insert))); if (!insertsOnLines.containsKey(insertPoint)) {
insertsOnLines.put(insertPoint, new ArrayList<>(List.of(insert)));
} else { } else {
insertsOnLines.get(insert.point.point.getLine() + " " + insert.point.point.getCharPositionInLine()).add(insert); insertsOnLines.get(insertPoint).add(insert);
} }
} }
inserts = insertsOnLines; 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()); ArrayList<LSPVariable> variables = new ArrayList<>(
insertsOnLines.entrySet().stream().map(el -> {
var v = el.getValue().getFirst();
return new LSPVariable("test",
new ArrayList<>(el.getValue().stream()
.map(typeinsert -> new Type(typeinsert.getInsertString(), typeinsert.point.isGenericClassInsertPoint()))
.toList()),
v.point.point.getLine(),
v.point.point.getCharPositionInLine(),
v.point.point.getStopIndex(),
v.getResultPair().getLeft());
}
).toList()
);
for (var variable : variables) { for (var variable : variables) {
@@ -140,31 +141,32 @@ public class TypeResolver {
return variables; return variables;
} }
public ArrayList<LSPVariable> infereInput(String pathString, String input, boolean a) throws IOException, ClassNotFoundException, URISyntaxException { public record InferenceResult(ArrayList<LSPVariable> variables, List<CompilerWarning> warnings) {}
public InferenceResult infereInput(String pathString, String input) throws IOException, ClassNotFoundException, URISyntaxException {
System.setOut(new PrintStream(OutputStream.nullOutputStream())); System.setOut(new PrintStream(OutputStream.nullOutputStream()));
LanguageServerInterface languageServerInterface = new LanguageServerInterface(); LanguageServerInterface languageServerInterface = new LanguageServerInterface();
LanguageServerTransferObject lsTransfer = languageServerInterface.getResultSetAndAbstractSyntax(pathString); LanguageServerTransferObject lsTransfer = languageServerInterface.getResultSetAndAbstractSyntax(pathString);
var parsedSource = lsTransfer.getAst(); var parsedSource = lsTransfer.getAst();
var tiResults = lsTransfer.getResultSets();
Set<PlaceholderVariable> tips = new HashSet<>(); Set<PlaceholderVariable> tips = new HashSet<>();
generatedGenerics = lsTransfer.getGeneratedGenerics().get(lsTransfer.getAst()); generatedGenerics = lsTransfer.getGeneratedGenerics().get(lsTransfer.getAst());
for (int i = 0; i < tiResults.size(); i++) { for (var generics : generatedGenerics) {
ResultSet tiResult = tiResults.get(i); tips.addAll(ASTTransformationHelper.createTypeInsertPoints(parsedSource, generics));
tips.addAll(ASTTransformationHelper.createTypeInsertPoints(parsedSource, tiResult, lsTransfer.getGeneratedGenerics().get(lsTransfer.getAst()).get(i)));
} }
System.setOut(System.out); System.setOut(System.out);
this.current = lsTransfer; this.current = lsTransfer;
HashMap<String, List<PlaceholderVariable>> insertsOnLines = new HashMap<>(); HashMap<InsertPoint, List<PlaceholderVariable>> insertsOnLines = new HashMap<>();
for (PlaceholderVariable insert : tips) { for (PlaceholderVariable insert : tips) {
if (!insertsOnLines.containsKey(insert.point.point.getLine() + " " + insert.point.point.getCharPositionInLine())) { var insertPoint = new InsertPoint(insert.point.point.getLine(), insert.point.point.getCharPositionInLine());
insertsOnLines.put(insert.point.point.getLine() + " " + insert.point.point.getCharPositionInLine(), new ArrayList<>(List.of(insert))); if (!insertsOnLines.containsKey(insertPoint)) {
insertsOnLines.put(insertPoint, new ArrayList<>(List.of(insert)));
} else { } else {
insertsOnLines.get(insert.point.point.getLine() + " " + insert.point.point.getCharPositionInLine()).add(insert); insertsOnLines.get(insertPoint).add(insert);
} }
} }
@@ -181,8 +183,13 @@ public class TypeResolver {
for(PlaceholderVariable typeinsert : entrySet.getValue()){ for(PlaceholderVariable typeinsert : entrySet.getValue()){
typesOfVariable.add(new Type(typeinsert.getInsertString(), typeinsert.point.isGenericClassInsertPoint())); 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())); var variable = entrySet.getValue().getFirst();
variables.add(new LSPVariable("test", typesOfVariable,
variable.point.point.getLine(),
variable.point.point.getCharPositionInLine(),
variable.point.point.getStopIndex(),
variable.getResultPair().getLeft()));
} }
@@ -203,26 +210,7 @@ public class TypeResolver {
} }
return variables; return new InferenceResult(variables, lsTransfer.getWarnings());
}
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;
}
}
}
}
}
} }
public SourceFile getNewAst(String uri) throws IOException, URISyntaxException, ClassNotFoundException { public SourceFile getNewAst(String uri) throws IOException, URISyntaxException, ClassNotFoundException {
@@ -233,7 +221,7 @@ public class TypeResolver {
public void updateAst(String uri) throws IOException, URISyntaxException, ClassNotFoundException { public void updateAst(String uri) throws IOException, URISyntaxException, ClassNotFoundException {
logger.info("Old AST:"); logger.info("Old AST:");
logger.info(ASTPrinter.print(this.current.getAst())); logger.info(ASTPrinter.print(this.current.getAst()));
this.current = new LanguageServerTransferObject(current.getResultSets(), getNewAst(uri), "", current.getGeneratedGenerics()); this.current = new LanguageServerTransferObject(getNewAst(uri), "", current.getGeneratedGenerics(), current.getWarnings());
logger.info("NEW AST:"); logger.info("NEW AST:");
logger.info(ASTPrinter.print(current.getAst())); logger.info(ASTPrinter.print(current.getAst()));
@@ -1,5 +1,7 @@
package de.dhbw.model; package de.dhbw.model;
import de.dhbw.helper.ConversionHelper;
import de.dhbwstuttgart.target.generate.GenericsResultSet;
import de.dhbwstuttgart.typeinference.result.ResultPair; import de.dhbwstuttgart.typeinference.result.ResultPair;
import java.util.*; import java.util.*;
@@ -11,10 +13,11 @@ public class PlaceholderVariable {
public final PlaceholderPoint point; public final PlaceholderPoint point;
Set<PlaceholderPoint> inserts; Set<PlaceholderPoint> inserts;
ResultPair<?, ?> resultPair; ResultPair<?, ?> resultPair;
private final GenericsResultSet constraints;
public PlaceholderVariable(PlaceholderPoint point, Set<PlaceholderPoint> additionalPoints, ResultPair<?, ?> resultPair, GenericsResultSet constraints) {
public PlaceholderVariable(PlaceholderPoint point, Set<PlaceholderPoint> additionalPoints, ResultPair<?, ?> resultPair) {
this.point = point; this.point = point;
this.constraints = constraints;
inserts = additionalPoints; inserts = additionalPoints;
this.resultPair = resultPair; this.resultPair = resultPair;
} }
@@ -43,7 +46,7 @@ public class PlaceholderVariable {
} }
public void reducePackage() { public void reducePackage() {
point.setInsertString(point.getInsertString().replaceAll("java\\.lang\\.", "").replaceAll("java\\.util\\.", "")); point.setInsertString(ConversionHelper.cleanType(point.getInsertString()));
} }
public ResultPair<?, ?> getResultPair() { public ResultPair<?, ?> getResultPair() {
@@ -73,4 +76,8 @@ public class PlaceholderVariable {
public String toString() { public String toString() {
return point.toString(); return point.toString();
} }
public GenericsResultSet getConstraints() {
return constraints;
}
} }
@@ -3,8 +3,12 @@ package de.dhbw.service;
import org.eclipse.lsp4j.*; import org.eclipse.lsp4j.*;
import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.eclipse.lsp4j.services.LanguageClient; import org.eclipse.lsp4j.services.LanguageClient;
import org.eclipse.lsp4j.services.LanguageServer;
import de.dhbw.JavaTXLanguageServer;
import java.util.List; import java.util.List;
import java.util.Map;
public class ClientService { public class ClientService {
@@ -39,21 +43,18 @@ public class ClientService {
this.client = client; this.client = client;
} }
public void updateClient(LanguageClient client) {
client.refreshInlayHints();
client.refreshDiagnostics();
}
public void updateClient() { public void updateClient() {
client.refreshInlayHints(); if (JavaTXLanguageServer.capabilities.getWorkspace().getInlayHint() != null)
client.refreshDiagnostics(); client.refreshInlayHints();
if (JavaTXLanguageServer.capabilities.getWorkspace().getDiagnostics() != null)
client.refreshDiagnostics();
} }
public LanguageClient getClient() { public LanguageClient getClient() {
return client; return client;
} }
public void startLoading(String taskName, String title, LanguageClient client) { public void startLoading(String taskName, String title) {
Either<String, Integer> token = Either.forLeft(taskName); Either<String, Integer> token = Either.forLeft(taskName);
client.createProgress(new WorkDoneProgressCreateParams(token)); client.createProgress(new WorkDoneProgressCreateParams(token));
@@ -67,7 +68,7 @@ public class ClientService {
} }
public void stopLoading(String taskName, String title, LanguageClient client) { public void stopLoading(String taskName, String title) {
Either<String, Integer> token = Either.forLeft(taskName); Either<String, Integer> token = Either.forLeft(taskName);
WorkDoneProgressEnd end = new WorkDoneProgressEnd(); WorkDoneProgressEnd end = new WorkDoneProgressEnd();
end.setMessage(title); end.setMessage(title);
+2 -1
View File
@@ -63,7 +63,8 @@ The Language Server in itself can be used for any Client. The Clients task is to
If you make changes in the Compiler Interface, you have to change the jar and therefore the Dependency in the Java TX Language Server If you make changes in the Compiler Interface, you have to change the jar and therefore the Dependency in the Java TX Language Server
You can follow this steps: You can follow this steps:
1. package the JavaTX Compiler 1. package the JavaTX Compiler
2. take the Jar-File and copy it into the /lib Folder 2. create a lib Folder at ./LangaugeServer -> ./LanguageServer/lib
2. take the Jar-File and copy it into the /lib Folder at
3. execute this Maven command to add the Jar in your local Repository: ```mvn install:install-file -Dfile=lib/JavaTXcompiler-0.1-jar-with-dependencies.jar -DgroupId=de.dhbwstuttgart -DartifactId=JavaTXcompiler -Dversion=0.1 -Dpackaging=jar``` 3. execute this Maven command to add the Jar in your local Repository: ```mvn install:install-file -Dfile=lib/JavaTXcompiler-0.1-jar-with-dependencies.jar -DgroupId=de.dhbwstuttgart -DartifactId=JavaTXcompiler -Dversion=0.1 -Dpackaging=jar```
4. run ```maven clean```, ```validate``` and ```install``` to load the new Dependency 4. run ```maven clean```, ```validate``` and ```install``` to load the new Dependency
5. you can now package the Language Server or change the code accordingly. 5. you can now package the Language Server or change the code accordingly.