From b1d775179667672586440edd7ed9491529b73c5a Mon Sep 17 00:00:00 2001 From: JaDogg Date: Sun, 26 May 2024 19:13:54 +0100 Subject: [PATCH] fix(yakshaintellij): use scratch files for error checking .yaka files --- .../sdk/language/YakshaCompilerAnnotator.java | 157 +++++++++++++----- 1 file changed, 112 insertions(+), 45 deletions(-) diff --git a/editor/intellij/src/main/java/org/intellij/sdk/language/YakshaCompilerAnnotator.java b/editor/intellij/src/main/java/org/intellij/sdk/language/YakshaCompilerAnnotator.java index 88af02a8..2afdb628 100644 --- a/editor/intellij/src/main/java/org/intellij/sdk/language/YakshaCompilerAnnotator.java +++ b/editor/intellij/src/main/java/org/intellij/sdk/language/YakshaCompilerAnnotator.java @@ -2,51 +2,113 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import com.google.gson.JsonSyntaxException; import com.intellij.lang.annotation.AnnotationHolder; import com.intellij.lang.annotation.ExternalAnnotator; import com.intellij.lang.annotation.HighlightSeverity; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; -import com.intellij.openapi.project.DumbAware; -import com.intellij.openapi.project.Project; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiManager; -import org.intellij.sdk.language.psi.YakshaFile; import org.intellij.sdk.language.tw.YakshaToolWindow; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.BufferedWriter; import java.io.File; import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; -public class YakshaCompilerAnnotator extends ExternalAnnotator> implements DumbAware { +public class YakshaCompilerAnnotator extends ExternalAnnotator, List> { + + private static final Logger LOGGER = Logger.getInstance(YakshaCompilerAnnotator.class); + public static final int MAX_DEPTH = 5; + + private static void log(final String s) { +// System.out.println(s); + LOGGER.info(s); + } + @Override - public @Nullable YakshaFile collectInformation(@NotNull PsiFile file) { - if (file instanceof YakshaFile) { - return (YakshaFile) file; - } - return null; + public @Nullable Pair collectInformation(@NotNull PsiFile file, @NotNull Editor editor, boolean hasErrors) { + return Pair.create(editor, file); } @Override - public @NotNull List doAnnotate(@Nullable YakshaFile collectedInfo) { + public @NotNull List doAnnotate(@Nullable Pair collectedInfo) { if (collectedInfo == null) { return List.of(); } - final var project = collectedInfo.getProject(); - final var virtualFile = collectedInfo.getVirtualFile(); - File mainFile = findMainFile(new File(virtualFile.getPath()), 5); - if (mainFile == null) { + final var editor = collectedInfo.first; + final var psiFile = collectedInfo.second; + final var virtualFile = psiFile.getVirtualFile(); + + File f = new File(virtualFile.getPath()); + final File cleanup; + final File directory = f.getParentFile(); + + if (f.getName().startsWith("_.")) { + log("You are editing a scratch file: " + f.getPath()); return List.of(); } + if (f.getName().equals("main.yaka")) { + final var scratch = Paths.get(directory.getAbsolutePath(), "_.main.yaka"); + log("Wrote scratch file:" + scratch); + final var temp = createSyncedFile(editor.getDocument(), scratch); + if (temp == null) { + f = Paths.get(directory.getAbsolutePath(), "_.main.yaka").toFile(); + } else { + f = new File(temp.getPath()); + } + cleanup = f; + } else { + final var name = "_." + f.getName(); + final var scratch = Paths.get(f.getAbsoluteFile().getParent(), name); + cleanup = scratch.toFile(); + createSyncedFile(editor.getDocument(), scratch); + log("Wrote scratch file:" + scratch); + f = findMainFile(new File(directory.getPath())); + if (f == null) { + deleteScratch(cleanup); + return List.of(); + } + } + + final var result = runCompiler(f); + deleteScratch(cleanup); + return result; + } + + private static void deleteScratch(@NotNull File f) { + try { + final var success = f.delete(); + log("Deleted " + f.getPath() + " success = " + success); + } catch (Exception ignored) { + log("Failed to delete " + f.getPath()); + } + } - return runCompiler(mainFile); + private static VirtualFile createSyncedFile(Document doc, Path tmp) { + try { + try (BufferedWriter out = Files.newBufferedWriter(tmp, StandardCharsets.UTF_8)) { + out.write(doc.getText()); + } + File f = tmp.toFile(); + return LocalFileSystem.getInstance().refreshAndFindFileByIoFile(f); + } catch (IOException ex) { + throw new RuntimeException(ex); + } } @Override @@ -59,6 +121,13 @@ public void apply(@NotNull PsiFile file, List annotationResult, @ if (document == null) { return; } + final var f = new File(file.getVirtualFile().getPath()); + if (f.getName().startsWith("_.")) { + return; + } + + final var givenFile = f.getAbsolutePath(); + final var givenFileScratch = Paths.get(Paths.get(givenFile).getParent().toString(), "_." + f.getName()).toFile().getAbsolutePath(); for (CompilerError error : annotationResult) { var line = error.line - 1; @@ -67,25 +136,23 @@ public void apply(@NotNull PsiFile file, List annotationResult, @ var startOffset = document.getLineStartOffset(line) + error.column - 1; var endOffset = startOffset + error.length; - holder.newAnnotation(HighlightSeverity.ERROR, error.message).range(new TextRange(startOffset, endOffset)).create(); - } - } + final var absPath = new File(error.file).getAbsolutePath(); - private @Nullable Document getDoc(Project project, VirtualFile file) { - final var psiFile = PsiManager.getInstance(project).findFile(file); - if (psiFile == null) { - return null; + if (absPath.equals(givenFile) || absPath.equals(givenFileScratch)) { + holder.newAnnotation(HighlightSeverity.ERROR, error.message).range(new TextRange(startOffset, endOffset)).create(); + } } - return PsiDocumentManager.getInstance(project).getDocument(psiFile); } public static class CompilerError { + final String file; final String message; final int line; final int column; final int length; - private CompilerError(final String message, final int line, final int column, final int length) { + private CompilerError(final String file, final String message, final int line, final int column, final int length) { + this.file = file; this.message = message; this.line = line; this.column = column; @@ -93,9 +160,9 @@ private CompilerError(final String message, final int line, final int column, fi } } - private File findMainFile(File dir, int maxDepth) { + private File findMainFile(final File dir) { File currentDir = dir; - for (int i = 0; i <= maxDepth; i++) { + for (int i = 0; i <= YakshaCompilerAnnotator.MAX_DEPTH; i++) { File mainCFile = new File(currentDir, "main.yaka"); if (mainCFile.exists() && mainCFile.isFile()) { return mainCFile; @@ -107,31 +174,28 @@ private File findMainFile(File dir, int maxDepth) { } private List runCompiler(File mainFile) { - List errors = new ArrayList<>(); + final List errors = new ArrayList<>(); final var compiler = YakshaToolWindow.YAKSHA_EXE_PATH; if (compiler.isBlank()) { - System.out.println("------ yaksha compiler path is not set -----"); + log("------ yaksha compiler path is not set -----"); return errors; } - System.out.println("----- yaksha compiler: " + compiler); + log("----- yaksha compiler: " + compiler); - List command = List.of(compiler, "compile", "-d", mainFile.getAbsolutePath()); + final List command = List.of(compiler, "compile", "-d", "-e", mainFile.getAbsolutePath()); try { - Process process = new ProcessBuilder(command) - .directory(mainFile.getParentFile()) - .redirectErrorStream(true) - .start(); + final Process process = new ProcessBuilder(command).directory(mainFile.getParentFile()).redirectErrorStream(true).start(); process.waitFor(60, TimeUnit.SECONDS); String output = new String(process.getInputStream().readAllBytes()); - int exitCode = process.exitValue(); - - errors.addAll(parseCompilerErrors(output, mainFile.getAbsolutePath())); - } catch (IOException | InterruptedException e) { - e.printStackTrace(); + final var result = parseCompilerErrors(output, mainFile.getAbsolutePath()); + log("Found errors " + result.size()); + errors.addAll(result); + } catch (IOException | InterruptedException ignored) { + log("-------------- error "); } return errors; @@ -143,16 +207,19 @@ private List parseCompilerErrors(String output, String mainFilePa for (String line : lines) { if (!line.trim().isEmpty()) { - JsonObject json = JsonParser.parseString(line).getAsJsonObject(); + final JsonObject json; + try { + json = JsonParser.parseString(line).getAsJsonObject(); + } catch (JsonSyntaxException exception) { + continue; + } String file = json.has("file") ? json.get("file").getAsString() : mainFilePath; int lineNumber = json.has("line") ? json.get("line").getAsInt() : 0; int columnNumber = json.has("pos") ? json.get("pos").getAsInt() : 0; String message = json.get("message").getAsString(); int tokenLength = json.has("token") ? json.get("token").getAsString().length() : 1; - if (mainFilePath.equals(file)) { - errors.add(new CompilerError(message, lineNumber, columnNumber, tokenLength)); - } + errors.add(new CompilerError(file, message, lineNumber, columnNumber, tokenLength)); } }