expand all descriptions over problem element (IDEA-80256)
[idea/community.git] / platform / lang-impl / src / com / intellij / codeInsight / daemon / impl / DaemonTooltipRendererProvider.java
1 /*
2  * Copyright 2000-2011 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /*
18  * @author max
19  */
20 package com.intellij.codeInsight.daemon.impl;
21
22 import com.intellij.codeInsight.daemon.DaemonBundle;
23 import com.intellij.codeInsight.daemon.impl.actions.ShowErrorDescriptionAction;
24 import com.intellij.codeInsight.hint.LineTooltipRenderer;
25 import com.intellij.codeInsight.hint.TooltipLinkHandlerEP;
26 import com.intellij.codeInsight.hint.TooltipRenderer;
27 import com.intellij.openapi.editor.Editor;
28 import com.intellij.openapi.editor.ex.ErrorStripTooltipRendererProvider;
29 import com.intellij.openapi.editor.impl.TrafficTooltipRenderer;
30 import com.intellij.openapi.editor.markup.RangeHighlighter;
31 import com.intellij.openapi.project.Project;
32 import com.intellij.openapi.util.text.StringUtil;
33 import com.intellij.util.SmartList;
34 import com.intellij.util.containers.ContainerUtil;
35 import com.intellij.util.ui.UIUtil;
36 import gnu.trove.THashSet;
37 import org.jetbrains.annotations.NonNls;
38 import org.jetbrains.annotations.NotNull;
39 import org.jetbrains.annotations.Nullable;
40
41 import javax.swing.*;
42 import java.util.Collection;
43 import java.util.Comparator;
44 import java.util.List;
45 import java.util.regex.Matcher;
46 import java.util.regex.Pattern;
47
48 public class DaemonTooltipRendererProvider implements ErrorStripTooltipRendererProvider {
49   private final Project myProject;
50
51   public DaemonTooltipRendererProvider(final Project project) {
52     myProject = project;
53   }
54
55   @Override
56   public TooltipRenderer calcTooltipRenderer(@NotNull final Collection<RangeHighlighter> highlighters) {
57     LineTooltipRenderer bigRenderer = null;
58     List<HighlightInfo> infos = new SmartList<HighlightInfo>();
59     Collection<String> tooltips = new THashSet<String>(); //do not show same tooltip twice
60     for (RangeHighlighter marker : highlighters) {
61       final Object tooltipObject = marker.getErrorStripeTooltip();
62       if (tooltipObject == null) continue;
63       if (tooltipObject instanceof HighlightInfo) {
64         HighlightInfo info = (HighlightInfo)tooltipObject;
65         if (info.toolTip != null && tooltips.add(info.toolTip)) {
66           infos.add(info);
67         }
68       }
69       else {
70         final String text = tooltipObject.toString();
71         if (tooltips.add(text)) {
72           if (bigRenderer == null) {
73             bigRenderer = new MyRenderer(text, new Object[] {highlighters});
74           }
75           else {
76             bigRenderer.addBelow(text);
77           }
78         }
79       }
80     }
81     if (!infos.isEmpty()) {
82       // show errors first
83       ContainerUtil.quickSort(infos, new Comparator<HighlightInfo>() {
84         @Override
85         public int compare(final HighlightInfo o1, final HighlightInfo o2) {
86           int i = SeverityRegistrar.getInstance(myProject).compare(o2.getSeverity(), o1.getSeverity());
87           if (i != 0) return i;
88           return o1.toolTip.compareTo(o2.toolTip);
89         }
90       });
91       final HighlightInfoComposite composite = new HighlightInfoComposite(infos);
92       if (bigRenderer == null) {
93         bigRenderer = new MyRenderer(UIUtil.convertSpace2Nbsp(composite.toolTip), new Object[] {highlighters});
94       }
95       else {
96         final LineTooltipRenderer renderer = new MyRenderer(UIUtil.convertSpace2Nbsp(composite.toolTip), new Object[] {highlighters});
97         renderer.addBelow(bigRenderer.getText());
98         bigRenderer = renderer;
99       }
100     }
101     return bigRenderer;
102   }
103
104   @Override
105   public TooltipRenderer calcTooltipRenderer(@NotNull final String text) {
106     return new MyRenderer(text, new Object[] {text});
107   }
108
109   @Override
110   public TooltipRenderer calcTooltipRenderer(@NotNull final String text, final int width) {
111     return new MyRenderer(text, width, new Object[] {text});
112   }
113
114   @Override
115   public TrafficTooltipRenderer createTrafficTooltipRenderer(Runnable onHide, Editor editor) {
116     return new TrafficTooltipRendererImpl(onHide, editor);
117   }
118
119   private static class MyRenderer extends LineTooltipRenderer {
120     public MyRenderer(final String text, Object[] comparable) {
121       super(text, comparable);
122     }
123
124     public MyRenderer(final String text, final int width, Object[] comparable) {
125       super(text, width, comparable);
126     }
127
128     @Override
129     protected void onHide(final JComponent contentComponent) {
130       ShowErrorDescriptionAction.rememberCurrentWidth(contentComponent.getWidth());
131     }
132
133     @Override
134     protected boolean dressDescription(@NotNull final Editor editor) {
135       final String[] problems = UIUtil.getHtmlBody(myText).split(BORDER_LINE);
136       String text = "";
137       for (String problem : problems) {
138         final String ref = getLinkRef(problem);
139         if (ref != null) {
140           String description = TooltipLinkHandlerEP.getDescription(ref, editor);
141           if (description != null) {
142             final Pattern pattern = Pattern.compile(".*Use.*(the (panel|checkbox|checkboxes|field|button|controls).*below).*", Pattern.DOTALL);
143             final Matcher matcher = pattern.matcher(description);
144             int startFindIdx = 0;
145             while (matcher.find(startFindIdx)) {
146               final int end = matcher.end(1);
147               startFindIdx = end;
148               description = description.substring(0, matcher.start(1)) + " inspection settings " + description.substring(end);
149             }
150             text += UIUtil.getHtmlBody(problem).replace(DaemonBundle.message("inspection.extended.description"),
151                                                         DaemonBundle.message("inspection.collapse.description")) +
152                     BORDER_LINE + UIUtil.getHtmlBody(description) + BORDER_LINE;
153           }
154         }
155       }
156       if (text.length() > 0) { //otherwise do not change anything
157         myText = "<html><body>" +  StringUtil.trimEnd(text, BORDER_LINE) + "</body></html>";
158         return true;
159       }
160       return false;
161     }
162
163     @Nullable
164     private static String getLinkRef(@NonNls String text) {
165       final String linkWithRef = "<a href=\"";
166       final int linkStartIdx = text.indexOf(linkWithRef);
167       if (linkStartIdx >= 0) {
168         final String ref = text.substring(linkStartIdx + linkWithRef.length());
169         final int quoteIdx = ref.indexOf('"');
170         if (quoteIdx > 0) {
171           return ref.substring(0, quoteIdx);
172         }
173       }
174       return null;
175     }
176
177     @Override
178     protected void stripDescription() {
179       final String[] problems = UIUtil.getHtmlBody(myText).split(BORDER_LINE);
180       myText = "<html><body>";
181       for (int i = 0; i < problems.length; i++) {
182         final String problem = problems[i];
183         if (i % 2 == 0) {
184           myText += UIUtil.getHtmlBody(problem).replace(DaemonBundle.message("inspection.collapse.description"),
185                                                  DaemonBundle.message("inspection.extended.description")) + BORDER_LINE;
186         }
187       }
188       myText = StringUtil.trimEnd(myText, BORDER_LINE) + "</body></html>";
189     }
190
191     @Override
192     protected LineTooltipRenderer createRenderer(final String text, final int width) {
193       return new MyRenderer(text, width, getEqualityObjects());
194     }
195   }
196 }