Qodana: add property for suppressed plugin ids list.
[idea/community.git] / java / idea-ui / src / com / intellij / openapi / roots / ui / configuration / ErrorPaneConfigurable.java
index 9b9d492a471585640cec9cc08b3ada8a28799b26..c9312dec366c10648c389aa3e12c6835b45192e7 100644 (file)
@@ -11,6 +11,9 @@ import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectCon
 import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
 import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureProblemDescription;
 import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.NlsSafe;
+import com.intellij.openapi.util.text.HtmlBuilder;
+import com.intellij.openapi.util.text.HtmlChunk;
 import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.ui.*;
 import com.intellij.ui.awt.RelativePoint;
@@ -19,7 +22,8 @@ import com.intellij.util.ui.StartupUiUtil;
 import com.intellij.util.ui.UIUtil;
 import com.intellij.util.ui.update.MergingUpdateQueue;
 import com.intellij.util.ui.update.Update;
-import com.intellij.xml.util.XmlStringUtil;
+import one.util.streamex.StreamEx;
+import org.jetbrains.annotations.Contract;
 import org.jetbrains.annotations.Nls;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -32,6 +36,7 @@ import java.awt.event.MouseEvent;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.stream.IntStream;
 
 /**
  * @author Konstantin Bulenkov
@@ -45,6 +50,19 @@ public class ErrorPaneConfigurable extends JPanel implements Configurable, Dispo
   private final MergingUpdateQueue myContentUpdateQueue;
   private final JTextPane myContent = new JTextPane();
   private final Runnable myOnErrorsChanged;
+  private static final @NlsSafe String myStyleText = "body {" +
+                                                     "  color: #" + ColorUtil.toHex(new JBColor(Gray.x33, UIUtil.getLabelForeground())) + ";" +
+                                                     "  font-family: '" + StartupUiUtil.getLabelFont().getName() + ",serif';" +
+                                                     "  font-size: " + StartupUiUtil.getLabelFont().getSize() + ";" +
+                                                     "}" +
+                                                     "li {" +
+                                                     "  margin-bottom: 5;" +
+                                                     "}" +
+                                                     "ol {" +
+                                                     "}" +
+                                                     "a {" +
+                                                     " text-decoration: none;" +
+                                                     "}";
 
   public ErrorPaneConfigurable(final Project project, StructureConfigurableContext context, Runnable onErrorsChanged) {
     super(new BorderLayout());
@@ -114,27 +132,6 @@ public class ErrorPaneConfigurable extends JPanel implements Configurable, Dispo
   public void refresh() {
     myAlarm.cancelAllRequests();
     myAlarm.addRequest(() -> {
-      final String header = "<html>" +
-                            "<header><style type='text/css'>" +
-                            "body {" +
-                            "  color: #" + ColorUtil.toHex(new JBColor(Gray.x33, UIUtil.getLabelForeground())) + ";" +
-                            "  font-family: '" + StartupUiUtil.getLabelFont().getName() + ",serif';" +
-                            "  font-size: " + StartupUiUtil.getLabelFont().getSize() + ";" +
-                            "}" +
-                            "li {" +
-                            "  margin-bottom: 5;" +
-                            "}" +
-                            "ol {" +
-                            "}" +
-                            "a {" +
-                            " text-decoration: none;" +
-                            "}" +
-                            "</style>" +
-                            "</header>" +
-                            "<body>";
-      final StringBuilder html = new StringBuilder(header);
-      int i = 0;
-      html.append("<ol>");
       ConfigurationError[] errors;
       int currentStamp;
       synchronized (myLock) {
@@ -142,44 +139,91 @@ public class ErrorPaneConfigurable extends JPanel implements Configurable, Dispo
         currentStamp = myComputedErrorsStamp;
       }
 
-      for (ConfigurationError error : errors) {
-        i++;
-        if (i > 100) break;
-        html.append("<li>");
-        String description;
-        if (error instanceof ProjectConfigurationProblem) {
-          //todo[nik] pass ProjectStructureProblemDescription directly and get rid of ConfigurationError at all
-          ProjectStructureProblemDescription problemDescription = ((ProjectConfigurationProblem)error).getProblemDescription();
-          description = problemDescription.getDescription();
-          if (description == null) {
-            ProjectStructureElement place = problemDescription.getPlace().getContainingElement();
-            description = XmlStringUtil.convertToHtmlContent(problemDescription.getMessage(false));
-            if (problemDescription.canShowPlace()) {
-              description = place.getTypeName() + " <a href='http://navigate/" + i + "'>"
-                            + XmlStringUtil.convertToHtmlContent(place.getPresentableName()) + "</a>: "
-                            + StringUtil.decapitalize(description);
-            }
-          }
-          else {
-            description = XmlStringUtil.convertToHtmlContent(description);
-          }
-        }
-        else {
-          description = XmlStringUtil.convertToHtmlContent(error.getDescription());
-        }
-        if (error.canBeFixed()) {
-          description += " <a href='http://fix/" + i + "'>[Fix]</a>";
-        }
-        html.append(description).append("</li>");
-      }
-      html.append("</ol></body></html>");
-      myContentUpdateQueue.queue(new ShowErrorsUpdate(currentStamp, html.toString()));
+      final HtmlChunk[] liTags = getErrorDescriptions(errors);
+
+      final HtmlChunk.Element ol = HtmlChunk.tag("ol")
+        .children(liTags);
+
+      final HtmlChunk.Element style = HtmlChunk.tag("style")
+        .attr("type", "text/css")
+        .addText(myStyleText);
+      final HtmlChunk.Element headerTag = new HtmlBuilder()
+        .append(style)
+        .wrapWith("header");
+
+      final HtmlChunk.Element result = new HtmlBuilder()
+        .append(headerTag)
+        .append(HtmlChunk.body().child(ol))
+        .wrapWith(HtmlChunk.html());
+
+      myContentUpdateQueue.queue(new ShowErrorsUpdate(currentStamp, result.toString()));
       if (myOnErrorsChanged != null) {
         myOnErrorsChanged.run();
       }
     }, 100);
   }
 
+  @Contract(pure = true)
+  private static HtmlChunk @NotNull[] getErrorDescriptions(final ConfigurationError @NotNull[] errors) {
+    final int limit = Math.min(errors.length, 100);
+
+    return StreamEx.of(errors)
+      .zipWith(IntStream.range(0, limit), ConfigurationErrorWithIndex::new)
+      .map(ErrorPaneConfigurable::getErrorDescriptionTag)
+      .toArray(HtmlChunk[]::new);
+  }
+
+  private static final class ConfigurationErrorWithIndex {
+    private final @NotNull ConfigurationError myError;
+    private final int myIdx;
+
+    private ConfigurationErrorWithIndex(@NotNull final ConfigurationError error, final int idx) {
+      myError = error;
+      myIdx = idx;
+    }
+  }
+
+  @Contract(pure = true)
+  @NotNull
+  private static HtmlChunk getErrorDescriptionTag(@NotNull final ConfigurationErrorWithIndex errorIndex) {
+    final int index = errorIndex.myIdx;
+    final ConfigurationError error = errorIndex.myError ;
+
+    final HtmlChunk description = getErrorDescription(index, error);
+
+    if (!error.canBeFixed()) return description.wrapWith("li");
+
+    final String text = "[" + JavaUiBundle.message("fix.link.text") + "]";
+
+    return new HtmlBuilder().append(description)
+      .append(HtmlChunk.link("http://fix/" + index, text))
+      .wrapWith("li");
+  }
+
+  @Contract(pure = true)
+  @NotNull
+  private static HtmlChunk getErrorDescription(final int index, @NotNull final ConfigurationError error) {
+    //todo[nik] pass ProjectStructureProblemDescription directly and get rid of ConfigurationError at all
+    if (!(error instanceof ProjectConfigurationProblem)) return error.getDescription();
+
+    final ProjectStructureProblemDescription problemDescription = ((ProjectConfigurationProblem)error).getProblemDescription();
+    if (problemDescription.getDescription() != null) return problemDescription.getDescription();
+
+    if (!problemDescription.canShowPlace()) return HtmlChunk.text(problemDescription.getMessage());
+
+    final String message = StringUtil.decapitalize(problemDescription.getMessage());
+
+    final ProjectStructureElement place = problemDescription.getPlace().getContainingElement();
+    final HtmlChunk link = HtmlChunk.link("http://navigate/" + index, place.getPresentableName());
+
+    return new HtmlBuilder().append(place.getTypeName())
+      .append(" ")
+      .append(link)
+      .append(": ")
+      .append(message)
+      .toFragment();
+  }
+
   @Nls
   @Override
   public String getDisplayName() {
@@ -237,9 +281,9 @@ public class ErrorPaneConfigurable extends JPanel implements Configurable, Dispo
 
   private class ShowErrorsUpdate extends Update {
     private final int myCurrentStamp;
-    private final String myText;
+    private final @Nls(capitalization = Nls.Capitalization.Sentence) String myText;
 
-    ShowErrorsUpdate(int currentStamp, String text) {
+    ShowErrorsUpdate(int currentStamp, @Nls(capitalization = Nls.Capitalization.Sentence) String text) {
       super(currentStamp);
       myCurrentStamp = currentStamp;
       myText = text;