Skip to content

Commit

Permalink
Merge pull request #7 from jlagerweij/bugfix/issue-6
Browse files Browse the repository at this point in the history
Fixes issue 6: Mix of Elm 0.19 and 0.18 code throws NullPointerExceptions
  • Loading branch information
jlagerweij authored Sep 17, 2018
2 parents 07c4150 + f5cd6ee commit 72ccbba
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 36 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ repositories {
}

group 'org.elm.tools.external'
version '2.0.0'
version '2.0.1'
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ private Optional<String> findElmPackageDirectory(PsiFile file) {
}

private boolean isIssueForCurrentFile(String basePath, String canonicalPath, Problems res) {
if (res.file == null) {
return true;
}
return res.file.replace("./", basePath + "/").equals(canonicalPath);
}

Expand Down Expand Up @@ -152,6 +155,9 @@ private void annotateForIssue(@NotNull AnnotationHolder holder, Document documen
@NotNull
private Optional<TextRange> findAnnotationLocation(Document document, Problems issue) {
Region region = issue.subregion != null ? issue.subregion : issue.region;
if (region == null) {
return Optional.empty();
}

int offsetStart = StringUtil.lineColToOffset(document.getText(), region.start.line - 1, region.start.column - 1);
int offsetEnd = StringUtil.lineColToOffset(document.getText(), region.end.line - 1, region.end.column - 1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,7 @@
public class CompileErrors {
public String type;
public List<CompileError> errors;
public String path;
public String title;
public List<Object> message;
}
127 changes: 93 additions & 34 deletions src/main/java/org/elm/tools/external/elmmake/ElmMake.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
package org.elm.tools.external.elmmake;

import com.google.common.io.CharStreams;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.execution.process.ProcessNotCreatedException;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationDisplayType;
import com.intellij.notification.NotificationGroup;
import com.intellij.notification.NotificationType;
import com.intellij.notification.Notifications;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;

import org.elm.tools.external.ElmExternalToolsComponent;
import org.jetbrains.annotations.NotNull;
Expand All @@ -21,13 +25,21 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;

public class ElmMake {
private static final Logger LOG = Logger.getInstance(ElmExternalToolsComponent.class);

private static final NotificationGroup GROUP_DISPLAY_ID_BALLOON =
new NotificationGroup("org.elmlang.intellijplugin.elmmake",
NotificationDisplayType.BALLOON, false);
private static final String ELM_EXTERNAL_TOOLS = "Elm External Tools";

private ElmMake() {
// Private constructor to hide the implicit public one
}
Expand All @@ -41,36 +53,31 @@ public static List<Problems> execute(String workDirectory, String nodePath, Stri
commandLine.addParameter(file);

try {
LOG.info(commandLine.getCommandLineString(elmMakeExePath));
LOG.info(workDirectory + ": " + commandLine.getCommandLineString(elmMakeExePath));

Process process = commandLine.createProcess();

process.waitFor();
LOG.debug("Elm exit value: " + process.exitValue());
if (process.exitValue() == 1) {
return parseElmMakeOutput(process.getInputStream(), process.getErrorStream());
}
return parseElmMakeOutput(process.getInputStream(), process.getErrorStream());

} catch (ExecutionException | InterruptedException e) {
LOG.error(e.getMessage(), e);
}
return new ArrayList<>();
}

public static List<Problems> parseElmMakeOutput(InputStream inputStream, InputStream errorStream) {
private static List<Problems> parseElmMakeOutput(InputStream inputStream, InputStream errorStream) {
List<Problems> allProblems = new ArrayList<>();
String output = null;
try {
List<String> lines = CharStreams.readLines(new InputStreamReader(inputStream));
List<String> lines = readLines(new InputStreamReader(inputStream));
if (lines.isEmpty()) {
List<String> errorLines = CharStreams.readLines(new InputStreamReader(errorStream));
List<String> errorLines = readLines(new InputStreamReader(errorStream));
if (notValidJson(errorLines)) {
String errorResult = String.join("\n", errorLines);
if (!errorResult.isEmpty()) {
String groupDisplayId = "org.elmlang.intellijplugin.elmmake";
String title = "Problem found performing Elm make";
Notification notification = new Notification(groupDisplayId, title, errorResult, NotificationType.ERROR, null);
Notifications.Bus.notify(notification);
showNotification("Problem found performing Elm make", errorResult);
}
} else {
for (String line : errorLines) {
Expand Down Expand Up @@ -98,38 +105,90 @@ public static List<Problems> parseElmMakeOutput(InputStream inputStream, InputSt
return allProblems;
}

private static List<String> readLines(Reader r) throws IOException {
List<String> result = new ArrayList<>();
BufferedReader lineReader = new BufferedReader(r);

String line;
while ((line = lineReader.readLine()) != null) {
result.add(line);
}

return result;
}

private static void showNotification(String title, String message) {
ApplicationManager.getApplication().invokeLater(() -> {
Notification notification = GROUP_DISPLAY_ID_BALLOON.createNotification(ELM_EXTERNAL_TOOLS, title, message, NotificationType.ERROR);
Optional<Project> projects = Arrays.stream(ProjectManager.getInstance().getOpenProjects()).findFirst();
projects.ifPresent(project -> Notifications.Bus.notify(notification, project));
});
}

private static List<Problems> parseCompileErrors(final String line) {
List<Problems> problems = new ArrayList<>();

final CompileErrors compileErrors = new Gson().fromJson(line, CompileErrors.class);
for (final CompileError error : compileErrors.errors) {
for (CompileProblem compileProblem : error.problems) {
StringBuilder details = new StringBuilder();
for (Object message : compileProblem.message) {
if (message instanceof String) {
details.append(message);
} else if (message instanceof Map) {
final Map messageMap = (Map) message;
messageMap.get("bold");
messageMap.get("underline");
messageMap.get("color");
final String string = (String) messageMap.get("string");
details.append(string);
}
if (compileErrors.errors != null) {
for (final CompileError error : compileErrors.errors) {
for (CompileProblem compileProblem : error.problems) {
String detailsMessage = messagesToString(compileProblem.message);

final Problems problem = new Problems();
problem.file = error.path;
problem.type = compileErrors.type;
problem.region = compileProblem.region;
problem.overview = compileProblem.title;
problem.details = detailsMessage;
problems.add(problem);
}

final Problems problem = new Problems();
problem.file = error.path;
problem.type = compileErrors.type;
problem.region = compileProblem.region;
problem.overview = compileProblem.title;
problem.details = details.toString();
problems.add(problem);
}
}
if ("error".equals(compileErrors.type) && compileErrors.message != null) {
showNotification(compileErrors.title, messagesToString(compileErrors.message));
}
return problems;
}

@NotNull
private static String messagesToString(final List<Object> messages) {
StringBuilder details = new StringBuilder();
for (Object message : messages) {
if (message instanceof String) {
details.append(message);
} else if (message instanceof Map) {
final Map messageMap = (Map) message;
final boolean bold = Boolean.TRUE.equals(messageMap.get("bold"));
final boolean underline = Boolean.TRUE.equals(messageMap.get("underline"));
final String color = (String) messageMap.get("color");
final String string = (String) messageMap.get("string");

details.append(createStyledMessage(bold, underline, color, string));
}
}
return details.toString();
}

@NotNull
private static String createStyledMessage(final boolean bold, final boolean underline, final String color, final String string) {
String style = "<font ";
if (color != null) {
style += "color=\"" + color + "\" style=\"background-color:black; ";
} else {
style += "style=\"";
}
if (bold) {
style += "font-weight:bolder; ";
}
if (underline) {
style += "text-decoration: underline; ";
}
style += "\">";
style += string;
style += "</font>";
return style;
}

private static List<Problems> parseProblemJsonElm018(String output) {
return new Gson().fromJson(output, new TypeToken<List<Problems>>() {
}.getType());
Expand Down
3 changes: 2 additions & 1 deletion src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<idea-plugin>
<id>org.elm.tools.external</id>
<name>Elm External Tools</name>
<version>2.0.0</version>
<version>2.0.1</version>
<vendor url="http://www.github.com/jlagerweij">
Jos Lagerweij
</vendor>
Expand All @@ -19,6 +19,7 @@

<change-notes><![CDATA[
<ul>
<li><b>2.0.1</b> <em>(2018-09-17)</em> - Fix issue when Elm version does not match code throwing NullPointerExceptions.</li>
<li><b>2.0.0</b> <em>(2018-08-31)</em> - Add support for Elm 0.19. Configure the elm binary to run 'elm make'.</li>
<li><b>1.3.0</b> <em>(2018-04-14)</em> - Add check for correct TextRange</li>
<li><b>1.2.0</b> <em>(2018-04-10)</em> - Fix issue with saving preferences.</li>
Expand Down

0 comments on commit 72ccbba

Please sign in to comment.