correct rendering of traffic light

more informative tooltip
This commit is contained in:
Alexey Kudravtsev
2010-09-10 13:10:23 +04:00
parent 6b24aea83b
commit d90bf971ce
26 changed files with 634 additions and 270 deletions
@@ -27,7 +27,6 @@ import com.intellij.util.EventDispatcher;
import org.jetbrains.annotations.Nls;
import java.awt.*;
import java.util.ArrayList;
public class FontEditorPreview implements PreviewPanel{
private final EditorEx myEditor;
@@ -60,7 +59,6 @@ public class FontEditorPreview implements PreviewPanel{
protected DaemonCodeAnalyzerStatus getDaemonCodeAnalyzerStatus(boolean fillErrorsCount) {
DaemonCodeAnalyzerStatus status = new DaemonCodeAnalyzerStatus();
status.errorAnalyzingFinished = true;
status.passStati = new ArrayList<DaemonCodeAnalyzerStatus.PassStatus>();
status.errorCount = new int[]{1, 2};
return status;
}
@@ -28,7 +28,6 @@ import com.intellij.codeInsight.hint.HintManager;
import com.intellij.codeInsight.intention.impl.IntentionHintComponent;
import com.intellij.concurrency.Job;
import com.intellij.ide.PowerSaveMode;
import com.intellij.injected.editor.EditorWindow;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
@@ -43,7 +42,6 @@ import com.intellij.openapi.editor.markup.RangeHighlighter;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.TextEditor;
import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.progress.ProgressIndicator;
@@ -89,8 +87,6 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzer implements JDOMEx
private final EditorTracker myEditorTracker;
private DaemonProgressIndicator myUpdateProgress = new DaemonProgressIndicator(); //guarded by this
private DaemonProgressIndicator myUpdateVisibleProgress = new DaemonProgressIndicator(); //guarded by this
private final Runnable myUpdateRunnable = createUpdateRunnable();
private final Alarm myAlarm = new Alarm();
@@ -311,37 +307,7 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzer implements JDOMEx
public void updateVisibleHighlighters(@NotNull Editor editor) {
ApplicationManager.getApplication().assertIsDispatchThread();
if (true) return; // no need, will not work anyway
if (!myUpdateByTimerEnabled) return;
if (editor instanceof EditorWindow) editor = ((EditorWindow)editor).getDelegate();
final TextEditor textEditor = TextEditorProvider.getInstance().getTextEditor(editor);
BackgroundEditorHighlighter highlighter = textEditor.getBackgroundHighlighter();
if (highlighter == null) return;
final HighlightingPass[] highlightingPasses = highlighter.createPassesForVisibleArea();
DaemonProgressIndicator progress;
synchronized (this) {
recreateVisibleProgress();
progress = myUpdateVisibleProgress;
}
myPassExecutorService.renewVisiblePasses(textEditor, highlightingPasses, progress);
}
private synchronized void cancelVisibleProgress() {
if (myUpdateVisibleProgress != null) {
myUpdateVisibleProgress.cancel();
myUpdateVisibleProgress = null;
}
}
private synchronized void recreateVisibleProgress() {
if (myUpdateVisibleProgress == null) {
myUpdateVisibleProgress = new DaemonProgressIndicator();
myUpdateVisibleProgress.start();
}
// no need, will not work anyway
}
public void setUpdateByTimerEnabled(boolean value) {
@@ -404,8 +370,7 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzer implements JDOMEx
stopProcess(true);
}
public List<TextEditorHighlightingPass> getPassesToShowProgressFor(PsiFile file) {
Document document = PsiDocumentManager.getInstance(myProject).getDocument(file);
public List<TextEditorHighlightingPass> getPassesToShowProgressFor(Document document) {
List<TextEditorHighlightingPass> allPasses = myPassExecutorService.getAllSubmittedPasses();
List<TextEditorHighlightingPass> result = new ArrayList<TextEditorHighlightingPass>(allPasses.size());
for (TextEditorHighlightingPass pass : allPasses) {
@@ -416,7 +381,7 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzer implements JDOMEx
return result;
}
public boolean isAllAnalysisFinished(PsiFile file) {
public boolean isAllAnalysisFinished(@NotNull PsiFile file) {
if (myDisposed) return false;
Document document = PsiDocumentManager.getInstance(myProject).getCachedDocument(file);
return document != null &&
@@ -462,17 +427,10 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzer implements JDOMEx
if (myUpdateProgress != null) {
myUpdateProgress.cancel();
myPassExecutorService.cancelAll(false);
cancelVisibleProgress();
myUpdateProgress = null;
}
}
private static DaemonProgressIndicator recreateProgress() {
DaemonProgressIndicator myUpdateProgress = new DaemonProgressIndicator();
myUpdateProgress.start();
return myUpdateProgress;
}
public static boolean processHighlights(@NotNull Document document,
@NotNull Project project,
@Nullable("null means all") final HighlightSeverity minSeverity,
@@ -748,7 +706,9 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzer implements JDOMEx
myAlarm.cancelAllRequests();
DaemonProgressIndicator progress;
synchronized (DaemonCodeAnalyzerImpl.this) {
myUpdateProgress = progress = recreateProgress();
DaemonProgressIndicator indicator = new DaemonProgressIndicator();
indicator.start();
myUpdateProgress = progress = indicator;
}
myPassExecutorService.submitPasses(passes, progress, Job.DEFAULT_PRIORITY);
}
@@ -27,6 +27,7 @@ import com.intellij.codeInsight.hint.TooltipRenderer;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ex.ErrorStripTooltipRendererProvider;
import com.intellij.openapi.editor.impl.TrafficTooltipRenderer;
import com.intellij.openapi.editor.markup.RangeHighlighter;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.keymap.KeymapManager;
@@ -107,6 +108,11 @@ public class DaemonTooltipRendererProvider implements ErrorStripTooltipRendererP
return new MyRenderer(text, width);
}
@Override
public TrafficTooltipRenderer createTrafficTooltipRenderer(Runnable onHide) {
return new TrafficTooltipRendererImpl(onHide);
}
private static class MyRenderer extends LineTooltipRenderer {
public MyRenderer(final String text) {
super(text);
@@ -61,7 +61,6 @@ import com.intellij.util.ui.UIUtil;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
import java.util.*;
import java.util.List;
@@ -69,7 +68,6 @@ import java.util.concurrent.atomic.AtomicInteger;
public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingPass implements DumbAware {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.GeneralHighlightingPass");
static final Icon IN_PROGRESS_ICON = IconLoader.getIcon("/general/errorsInProgress.png");
static final String PRESENTABLE_NAME = DaemonBundle.message("pass.syntax");
private static final Key<Boolean> HAS_ERROR_ELEMENT = Key.create("HAS_ERROR_ELEMENT");
@@ -105,7 +103,7 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
int endOffset,
boolean updateAll,
@NotNull TextRange priorityRange) {
super(project, document, IN_PROGRESS_ICON, PRESENTABLE_NAME, file, true);
super(project, document, PRESENTABLE_NAME, file, true);
myStartOffset = startOffset;
myEndOffset = endOffset;
myUpdateAll = updateAll;
@@ -49,8 +49,8 @@ public class GeneralHighlightingPassFactory extends AbstractProjectComponent imp
@Nullable
public TextEditorHighlightingPass createHighlightingPass(@NotNull PsiFile file, @NotNull final Editor editor) {
TextRange textRange = FileStatusMap.getDirtyTextRange(editor, Pass.UPDATE_ALL);
if (textRange == null) return new ProgressableTextEditorHighlightingPass.EmptyPass(myProject, editor.getDocument(), GeneralHighlightingPass.IN_PROGRESS_ICON,
GeneralHighlightingPass.PRESENTABLE_NAME);
if (textRange == null) return new ProgressableTextEditorHighlightingPass.EmptyPass(myProject, editor.getDocument()
);
TextRange visibleRange = VisibleHighlightingPassFactory.calculateVisibleRange(editor);
return new GeneralHighlightingPass(myProject, file, editor.getDocument(), textRange.getStartOffset(), textRange.getEndOffset(), true, visibleRange);
}
@@ -64,7 +64,7 @@ public class LineMarkersPass extends ProgressableTextEditorHighlightingPass impl
int startOffset,
int endOffset,
boolean updateAll) {
super(project, document, GeneralHighlightingPass.IN_PROGRESS_ICON, GeneralHighlightingPass.PRESENTABLE_NAME, file,
super(project, document, GeneralHighlightingPass.PRESENTABLE_NAME, file,
false);
myStartOffset = startOffset;
myEndOffset = endOffset;
@@ -47,8 +47,8 @@ public class LineMarkersPassFactory extends AbstractProjectComponent implements
@Nullable
public TextEditorHighlightingPass createHighlightingPass(@NotNull PsiFile file, @NotNull final Editor editor) {
TextRange textRange = calculateRangeToProcessForSyntaxPass(editor);
if (textRange == null) return new ProgressableTextEditorHighlightingPass.EmptyPass(myProject, editor.getDocument(), GeneralHighlightingPass.IN_PROGRESS_ICON,
GeneralHighlightingPass.PRESENTABLE_NAME);
if (textRange == null) return new ProgressableTextEditorHighlightingPass.EmptyPass(myProject, editor.getDocument()
);
return new LineMarkersPass(myProject, file, editor.getDocument(), textRange.getStartOffset(), textRange.getEndOffset(), true);
}
@@ -45,7 +45,6 @@ import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.util.ProgressWrapper;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.util.IconLoader;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.Trinity;
@@ -64,7 +63,6 @@ import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ConcurrentMap;
@@ -81,7 +79,6 @@ public class LocalInspectionsPass extends ProgressableTextEditorHighlightingPass
private final ConcurrentMap<PsiFile, List<InspectionResult>> result = new ConcurrentHashMap<PsiFile, List<InspectionResult>>();
static final String PRESENTABLE_NAME = DaemonBundle.message("pass.inspection");
private volatile List<HighlightInfo> myInfos = Collections.emptyList();
static final Icon IN_PROGRESS_ICON = IconLoader.getIcon("/general/inspectionInProgress.png");
private final String myShortcutText;
private final SeverityRegistrar mySeverityRegistrar;
private final InspectionProfileWrapper myProfileWrapper;
@@ -91,7 +88,7 @@ public class LocalInspectionsPass extends ProgressableTextEditorHighlightingPass
this(file, document, startOffset, endOffset, new TextRange(0, 0));
}
public LocalInspectionsPass(@NotNull PsiFile file, @Nullable Document document, int startOffset, int endOffset, @NotNull TextRange priorityRange) {
super(file.getProject(), document, IN_PROGRESS_ICON, PRESENTABLE_NAME, file, true);
super(file.getProject(), document, PRESENTABLE_NAME, file, true);
myStartOffset = startOffset;
myEndOffset = endOffset;
myPriorityRange = priorityRange;
@@ -52,8 +52,8 @@ public class LocalInspectionsPassFactory extends AbstractProjectComponent implem
@Nullable
public TextEditorHighlightingPass createHighlightingPass(@NotNull PsiFile file, @NotNull final Editor editor) {
TextRange textRange = calculateRangeToProcess(editor);
if (textRange == null) return new ProgressableTextEditorHighlightingPass.EmptyPass(myProject, editor.getDocument(), LocalInspectionsPass.IN_PROGRESS_ICON,
LocalInspectionsPass.PRESENTABLE_NAME);
if (textRange == null) return new ProgressableTextEditorHighlightingPass.EmptyPass(myProject, editor.getDocument()
);
TextRange visibleRange = VisibleHighlightingPassFactory.calculateVisibleRange(editor);
return new LocalInspectionsPass(file, editor.getDocument(), textRange.getStartOffset(), textRange.getEndOffset(), visibleRange){
List<LocalInspectionTool> getInspectionTools(InspectionProfileWrapper profile) {
@@ -163,7 +163,7 @@ public abstract class PassExecutorService implements Disposable {
List<TextEditorHighlightingPass> passes = textPasses.get(fileEditors.get(0));
threadsToStartCountdown.addAndGet(passes.size());
// create one scheduled pass per unique id (possibly for multiple fileeditors. they all will be applied at the pass finish)
// create one scheduled pass per unique id (possibly for multiple file editors. they all will be applied at the pass finish)
ContainerUtil.quickSort(passes, new Comparator<TextEditorHighlightingPass>() {
public int compare(final TextEditorHighlightingPass o1, final TextEditorHighlightingPass o2) {
return o1.getId() - o2.getId();
@@ -209,7 +209,7 @@ public abstract class PassExecutorService implements Disposable {
toBeSubmitted.put(key, scheduledPass);
for (int predecessorId : pass.getCompletionPredecessorIds()) {
ScheduledPass predecessor = findOrCreatePredecessorPass(fileEditors, document, toBeSubmitted, textEditorHighlightingPasses, freePasses,
updateProgress, threadsToStartCountdown, jobPriority, predecessorId, passId);
updateProgress, threadsToStartCountdown, jobPriority, predecessorId);
if (predecessor != null) {
predecessor.mySuccessorsOnCompletion.add(scheduledPass);
scheduledPass.myRunningPredecessorsCount.incrementAndGet();
@@ -217,7 +217,7 @@ public abstract class PassExecutorService implements Disposable {
}
for (int predecessorId : pass.getStartingPredecessorIds()) {
ScheduledPass predecessor = findOrCreatePredecessorPass(fileEditors, document, toBeSubmitted, textEditorHighlightingPasses, freePasses,
updateProgress, threadsToStartCountdown, jobPriority, predecessorId, passId);
updateProgress, threadsToStartCountdown, jobPriority, predecessorId);
if (predecessor != null) {
predecessor.mySuccessorsOnSubmit.add(scheduledPass);
scheduledPass.myRunningPredecessorsCount.incrementAndGet();
@@ -237,9 +237,9 @@ public abstract class PassExecutorService implements Disposable {
final DaemonProgressIndicator updateProgress,
final AtomicInteger myThreadsToStartCountdown,
final int jobPriority,
final int predecessorId, int passId) {
Pair<Document, Integer> predkey = Pair.create(document, predecessorId);
ScheduledPass predecessor = toBeSubmitted.get(predkey);
final int predecessorId) {
Pair<Document, Integer> predKey = Pair.create(document, predecessorId);
ScheduledPass predecessor = toBeSubmitted.get(predKey);
if (predecessor == null) {
TextEditorHighlightingPass textEditorPass = findPassById(predecessorId, textEditorHighlightingPasses);
predecessor = textEditorPass == null ? null : createScheduledPass(fileEditors, textEditorPass, toBeSubmitted, textEditorHighlightingPasses, freePasses,
@@ -264,7 +264,6 @@ public abstract class PassExecutorService implements Disposable {
Job<Void> job = JobUtil.submitToJobThread(pass, pass.myJobPriority, new Consumer<Future>() {
@Override
public void consume(Future future) {
mySubmittedPasses.remove(pass);
try {
future.get();
}
@@ -281,18 +280,6 @@ public abstract class PassExecutorService implements Disposable {
}
}
public void renewVisiblePasses(final TextEditor textEditor, final HighlightingPass[] highlightingPasses,
final DaemonProgressIndicator visibleProgress) {
for (ScheduledPass pass : mySubmittedPasses.keySet()) {
if (pass.myUpdateProgress == visibleProgress) {
return; //already scheduled. whenever something changes, it will be rescheduled anyway
}
}
Map<FileEditor, HighlightingPass[]> passes = Collections.<FileEditor, HighlightingPass[]>singletonMap(textEditor, highlightingPasses);
// higher priority
submitPasses(passes, visibleProgress, Job.DEFAULT_PRIORITY-10);
}
private class ScheduledPass implements Runnable {
private final List<FileEditor> myFileEditors;
private final TextEditorHighlightingPass myPass;
@@ -451,12 +438,18 @@ public abstract class PassExecutorService implements Disposable {
protected abstract void afterApplyInformationToEditor(TextEditorHighlightingPass pass, FileEditor fileEditor, ProgressIndicator updateProgress);
public List<TextEditorHighlightingPass> getAllSubmittedPasses() {
ArrayList<TextEditorHighlightingPass> result = new ArrayList<TextEditorHighlightingPass>(mySubmittedPasses.size());
List<TextEditorHighlightingPass> result = new ArrayList<TextEditorHighlightingPass>(mySubmittedPasses.size());
for (ScheduledPass scheduledPass : mySubmittedPasses.keySet()) {
if (!scheduledPass.myUpdateProgress.isCanceled()) {
result.add(scheduledPass.myPass);
}
}
ContainerUtil.quickSort(result, new Comparator<TextEditorHighlightingPass>() {
@Override
public int compare(TextEditorHighlightingPass o1, TextEditorHighlightingPass o2) {
return o1.getId() - o2.getId();
}
});
return result;
}
@@ -19,13 +19,19 @@ package com.intellij.codeInsight.daemon.impl;
import com.intellij.codeHighlighting.TextEditorHighlightingPass;
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.impl.EditorMarkupModelImpl;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.TextEditor;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.util.Alarm;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.concurrent.atomic.AtomicLong;
/**
@@ -35,14 +41,15 @@ public abstract class ProgressableTextEditorHighlightingPass extends TextEditorH
private volatile boolean myFinished;
private volatile long myProgessLimit = 0;
private final AtomicLong myProgressCount = new AtomicLong();
private final Icon myInProgressIcon;
private final String myPresentableName;
protected final PsiFile myFile;
protected ProgressableTextEditorHighlightingPass(final Project project, @Nullable final Document document, final Icon inProgressIcon,
final String presentableName, @NotNull PsiFile file, boolean runIntentionPassAfter) {
protected ProgressableTextEditorHighlightingPass(@NotNull Project project,
@Nullable final Document document,
@NotNull String presentableName,
@NotNull PsiFile file,
boolean runIntentionPassAfter) {
super(project, document, runIntentionPassAfter);
myInProgressIcon = inProgressIcon;
myPresentableName = presentableName;
myFile = file;
}
@@ -50,6 +57,7 @@ public abstract class ProgressableTextEditorHighlightingPass extends TextEditorH
public final void doCollectInformation(final ProgressIndicator progress) {
myFinished = false;
collectInformationWithProgress(progress);
repaintTrafficIcon();
}
protected abstract void collectInformationWithProgress(final ProgressIndicator progress);
@@ -59,6 +67,7 @@ public abstract class ProgressableTextEditorHighlightingPass extends TextEditorH
applyInformationWithProgress();
DaemonCodeAnalyzer daemonCodeAnalyzer = DaemonCodeAnalyzer.getInstance(myProject);
((DaemonCodeAnalyzerImpl)daemonCodeAnalyzer).getFileStatusMap().markFileUpToDate(myDocument, myFile, getId());
repaintTrafficIcon();
}
protected abstract void applyInformationWithProgress();
@@ -69,18 +78,22 @@ public abstract class ProgressableTextEditorHighlightingPass extends TextEditorH
*/
public double getProgress() {
if (myProgessLimit == 0) return -1;
return (double)myProgressCount.get() / myProgessLimit;
return ((double)myProgressCount.get()) / myProgessLimit;
}
public long getProgressLimit() {
return myProgessLimit;
}
public long getProgressCount() {
return myProgressCount.get();
}
public boolean isFinished() {
return myFinished;
}
protected final Icon getInProgressIcon() {
return myInProgressIcon;
}
protected final String getPresentableName() {
protected String getPresentableName() {
return myPresentableName;
}
@@ -88,12 +101,44 @@ public abstract class ProgressableTextEditorHighlightingPass extends TextEditorH
myProgessLimit = limit;
}
public void advanceProgress(int delta) {
myProgressCount.addAndGet(delta);
private final Alarm repaintIconAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD);
public void advanceProgress(long delta) {
long l = myProgressCount.addAndGet(delta);
repaintTrafficIcon();
}
private void repaintTrafficIcon() {
if (repaintIconAlarm.getActiveRequestCount() == 0 || getProgressCount() >= getProgressLimit()) {
repaintIconAlarm.addRequest(new Runnable() {
public void run() {
if (myProject.isDisposed()) return;
Editor editor = getEditorForFile(myFile);
if (editor == null || editor.isDisposed()) return;
EditorMarkupModelImpl markup = (EditorMarkupModelImpl)editor.getMarkupModel();
markup.repaintTrafficLightIcon();
}
}, 50, null);
}
}
private static Editor getEditorForFile(@NotNull final PsiFile psiFile) {
VirtualFile virtualFile = psiFile.getVirtualFile();
if (virtualFile == null) {
PsiFile originalFile = psiFile.getOriginalFile();
virtualFile = originalFile.getVirtualFile();
if (virtualFile == null) return null;
}
final FileEditor[] editors = FileEditorManager.getInstance(psiFile.getProject()).getEditors(virtualFile);
for (FileEditor editor : editors) {
if (editor instanceof TextEditor) {
return ((TextEditor)editor).getEditor();
}
}
return null;
}
public static class EmptyPass extends TextEditorHighlightingPass {
public EmptyPass(final Project project, @Nullable final Document document, Icon icon, String text) {
public EmptyPass(final Project project, @Nullable final Document document) {
super(project, document, false);
}
@@ -46,6 +46,7 @@ public class TrafficLightRenderer implements ErrorStripeRenderer {
private static final Icon IN_PROGRESS_ICON = IconLoader.getIcon("/general/errorsInProgress.png");
private static final Icon NO_ANALYSIS_ICON = IconLoader.getIcon("/general/noAnalysis.png");
private static final Icon NO_ICON = new EmptyIcon(IN_PROGRESS_ICON.getIconWidth(), IN_PROGRESS_ICON.getIconHeight());
private static final Icon STARING_EYE_ICON = IconLoader.getIcon("/general/inspectionInProgress.png");
private final Project myProject;
private final Document myDocument;
@@ -57,34 +58,26 @@ public class TrafficLightRenderer implements ErrorStripeRenderer {
@NonNls private static final String BR = "<br>";
@NonNls private static final String NO_PASS_FOR_MESSAGE_KEY_SUFFIX = ".for";
public TrafficLightRenderer(Project project, DaemonCodeAnalyzerImpl highlighter, Document document, PsiFile file) {
myProject = project;
myDaemonCodeAnalyzer = highlighter;
myDocument = document;
myFile = file;
}
public static class DaemonCodeAnalyzerStatus {
public boolean errorAnalyzingFinished; // all passes done
public static class PassStatus {
private final String name;
private final double progress;
private final boolean finished;
public Icon inProgressIcon;
public PassStatus(final String name, final double progress, final boolean finished, Icon inProgressIcon) {
this.name = name;
this.progress = progress;
this.finished = finished;
this.inProgressIcon = inProgressIcon;
}
}
public List<PassStatus> passStati = new ArrayList<PassStatus>();
public final List<ProgressableTextEditorHighlightingPass> passStati = new ArrayList<ProgressableTextEditorHighlightingPass>();
public String[/*rootsNumber*/] noHighlightingRoots;
public String[/*rootsNumber*/] noInspectionRoots;
public int[] errorCount;
public int rootsNumber;
public int[] errorCount = ArrayUtil.EMPTY_INT_ARRAY;
public int rootsNumber;
public String toString() {
@NonNls String s = "DS: finished=" + errorAnalyzingFinished;
s += "; pass statuses: " + passStati.size() + "; ";
for (PassStatus passStatus : passStati) {
s += String.format("(%s %2.0f%% %b)", passStatus.name, passStatus.progress*100, passStatus.finished);
for (ProgressableTextEditorHighlightingPass passStatus : passStati) {
s += String.format("(%s %2.0f%% %b)", passStatus.getPresentableName(), passStatus.getProgress() *100, passStatus.isFinished());
}
return s;
}
@@ -94,8 +87,8 @@ public class TrafficLightRenderer implements ErrorStripeRenderer {
protected DaemonCodeAnalyzerStatus getDaemonCodeAnalyzerStatus(boolean fillErrorsCount) {
if (myFile == null || myProject.isDisposed() || !myDaemonCodeAnalyzer.isHighlightingAvailable(myFile)) return null;
ArrayList<String> noInspectionRoots = new ArrayList<String>();
ArrayList<String> noHighlightingRoots = new ArrayList<String>();
List<String> noInspectionRoots = new ArrayList<String>();
List<String> noHighlightingRoots = new ArrayList<String>();
final PsiFile[] roots = myFile.getPsiRoots();
for (PsiFile file : roots) {
if (!HighlightLevelUtil.shouldHighlight(file)) {
@@ -112,29 +105,26 @@ public class TrafficLightRenderer implements ErrorStripeRenderer {
final SeverityRegistrar severityRegistrar = SeverityRegistrar.getInstance(myProject);
status.errorCount = new int[severityRegistrar.getSeveritiesCount()];
status.rootsNumber = roots.length;
fillDaemonCodeAnalyzerErrorsStatus(status, fillErrorsCount);
List<TextEditorHighlightingPass> passes = myDaemonCodeAnalyzer.getPassesToShowProgressFor(myFile);
ArrayList<DaemonCodeAnalyzerStatus.PassStatus> passStati = new ArrayList<DaemonCodeAnalyzerStatus.PassStatus>();
fillDaemonCodeAnalyzerErrorsStatus(status, fillErrorsCount, severityRegistrar);
List<TextEditorHighlightingPass> passes = myDaemonCodeAnalyzer.getPassesToShowProgressFor(myDocument);
for (TextEditorHighlightingPass tepass : passes) {
if (!(tepass instanceof ProgressableTextEditorHighlightingPass)) continue;
ProgressableTextEditorHighlightingPass pass = (ProgressableTextEditorHighlightingPass)tepass;
double progress = pass.getProgress();
if (progress < 0) continue;
DaemonCodeAnalyzerStatus.PassStatus passStatus = new DaemonCodeAnalyzerStatus.PassStatus(pass.getPresentableName(), progress, pass.isFinished(), pass.getInProgressIcon());
passStati.add(passStatus);
if (pass.getProgress() < 0) continue;
status.passStati.add(pass);
}
status.passStati = passStati;
status.errorAnalyzingFinished = myDaemonCodeAnalyzer.isAllAnalysisFinished(myFile);
return status;
}
protected void fillDaemonCodeAnalyzerErrorsStatus(final DaemonCodeAnalyzerStatus status, final boolean fillErrorsCount) {
final SeverityRegistrar severityRegistrar = SeverityRegistrar.getInstance(myProject);
protected void fillDaemonCodeAnalyzerErrorsStatus(final DaemonCodeAnalyzerStatus status,
final boolean fillErrorsCount,
final SeverityRegistrar severityRegistrar) {
if (fillErrorsCount) Arrays.fill(status.errorCount, 0);
final int count = severityRegistrar.getSeveritiesCount() - 1;
final HighlightSeverity maxSeverity = severityRegistrar.getSeverityByIndex(count);
final HighlightSeverity maxPossibleSeverity = severityRegistrar.getSeverityByIndex(count);
final HighlightSeverity[] maxFoundSeverity = {null};
DaemonCodeAnalyzerImpl.processHighlights(myDocument, myProject, null, 0, myDocument.getTextLength(), new Processor<HighlightInfo>() {
@@ -147,7 +137,7 @@ public class TrafficLightRenderer implements ErrorStripeRenderer {
}
}
else {
if (infoSeverity == maxSeverity) {
if (infoSeverity == maxPossibleSeverity) {
status.errorCount[count] = 1;
return false;
}
@@ -164,57 +154,12 @@ public class TrafficLightRenderer implements ErrorStripeRenderer {
status.errorCount[severityIdx] = 1;
}
}
//if (highlights == null) highlights = Arrays.asList(HighlightInfo.EMPTY_ARRAY);
//if (fillErrorsCount) {
// highlights = new ArrayList<HighlightInfo>(highlights);
// Collections.sort(highlights, new Comparator<HighlightInfo>() {
// public int compare(HighlightInfo o1, HighlightInfo o2) {
// return o1.getSeverity().myVal - o2.getSeverity().myVal;
// }
// });
// Arrays.fill(status.errorCount, 0);
// for (HighlightInfo highlight : highlights) {
// final HighlightSeverity severity = highlight.getSeverity();
// final int severityIdx = severityRegistrar.getSeverityIdx(severity);
// if (severityIdx != -1) {
// status.errorCount[severityIdx] ++;
// }
// }
//}
//else {
// HighlightSeverity severity = null;
// final int count = severityRegistrar.getSeveritiesCount() - 1;
// HighlightSeverity maxSeverity = severityRegistrar.getSeverityByIndex(count);
// for (HighlightInfo info : highlights) {
// if (info.getSeverity() == maxSeverity) {
// status.errorCount[count] = 1;
// return;
// }
// if (severity == null || severityRegistrar.compare(severity, info.getSeverity()) <= 0) {
// severity = info.getSeverity();
// }
// }
// if (severity != null) {
// final int severityIdx = severityRegistrar.getSeverityIdx(severity);
// if (severityIdx != -1) {
// status.errorCount[severityIdx] = 1;
// }
// }
//}
}
public final Project getProject() {
return myProject;
}
public TrafficLightRenderer(Project project, DaemonCodeAnalyzerImpl highlighter, Document document, PsiFile file) {
myProject = project;
myDaemonCodeAnalyzer = highlighter;
myDocument = document;
myFile = file;
}
public String getTooltipMessage() {
DaemonCodeAnalyzerStatus status = getDaemonCodeAnalyzerStatus(true);
@@ -237,9 +182,9 @@ public class TrafficLightRenderer implements ErrorStripeRenderer {
else {
text += DaemonBundle.message("performing.code.analysis");
text += "<table>";
for (DaemonCodeAnalyzerStatus.PassStatus passStatus : status.passStati) {
if (passStatus.finished) continue;
text += "<tr><td>" + passStatus.name + ":</td><td>" + renderProgressHtml(passStatus.progress) + "</td></tr>";
for (ProgressableTextEditorHighlightingPass passStatus : status.passStati) {
if (passStatus.isFinished()) continue;
text += "<tr><td>" + passStatus.getPresentableName() + ":</td><td>" + renderProgressHtml(passStatus.getProgress()) + "</td></tr>";
}
text += "</table>";
}
@@ -290,13 +235,10 @@ public class TrafficLightRenderer implements ErrorStripeRenderer {
}
private static String getMessageByRoots(String [] roots, int rootsNumber, @NonNls String prefix){
if (roots != null) {
final int length = roots.length;
if (length > 0){
return BR + (rootsNumber > 1
? DaemonBundle.message(prefix + NO_PASS_FOR_MESSAGE_KEY_SUFFIX, StringUtil.join(roots, ", "))
: DaemonBundle.message(prefix));
}
if (roots != null && roots.length > 0) {
return BR + (rootsNumber > 1
? DaemonBundle.message(prefix + NO_PASS_FOR_MESSAGE_KEY_SUFFIX, StringUtil.join(roots, ", "))
: DaemonBundle.message(prefix));
}
return "";
}
@@ -318,18 +260,12 @@ public class TrafficLightRenderer implements ErrorStripeRenderer {
if (status.noHighlightingRoots != null && status.noHighlightingRoots.length == status.rootsNumber) {
return NO_ANALYSIS_ICON;
}
Icon inProgressMask = null;
boolean atLeastOnePassFinished = status.errorAnalyzingFinished;
for (DaemonCodeAnalyzerStatus.PassStatus passStatus : status.passStati) {
atLeastOnePassFinished |= passStatus.finished;
if (!passStatus.finished) {
Icon progressIcon = passStatus.inProgressIcon;
if (progressIcon != null) {
inProgressMask = inProgressMask == null ? progressIcon : LayeredIcon.create(inProgressMask, progressIcon);
}
}
for (ProgressableTextEditorHighlightingPass passStatus : status.passStati) {
atLeastOnePassFinished |= passStatus.isFinished();
}
Icon icon = status.errorAnalyzingFinished ? HighlightDisplayLevel.DO_NOT_SHOW.getIcon() : IN_PROGRESS_ICON;
Icon icon = HighlightDisplayLevel.DO_NOT_SHOW.getIcon();
if (atLeastOnePassFinished) {
SeverityRegistrar severityRegistrar = SeverityRegistrar.getInstance(myProject);
for (int i = status.errorCount.length - 1; i >= 0; i--) {
@@ -340,9 +276,20 @@ public class TrafficLightRenderer implements ErrorStripeRenderer {
}
}
if (inProgressMask != null) {
icon = LayeredIcon.create(icon, inProgressMask);
if (status.errorAnalyzingFinished) return icon;
//icon = HighlightDisplayLevel.createIconByMask(Color.blue);
double progress = getOverallProgress(status);
TruncatingIcon trunc = new TruncatingIcon(icon, (int)(icon.getIconWidth() * progress), (int)(icon.getIconHeight() * progress));
return LayeredIcon.create(trunc, STARING_EYE_ICON);
}
private static double getOverallProgress(DaemonCodeAnalyzerStatus status) {
long advancement = 0;
long limit = 0;
for (ProgressableTextEditorHighlightingPass ps : status.passStati) {
advancement += ps.getProgressCount();
limit += ps.getProgressLimit();
}
return icon;
return limit == 0 ? status.errorAnalyzingFinished ? 1 : 0 : advancement * 1.0 / limit;
}
}
@@ -0,0 +1,161 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.daemon.impl;
import com.intellij.codeInsight.daemon.DaemonBundle;
import com.intellij.codeInsight.hint.LineTooltipRenderer;
import com.intellij.ide.PowerSaveMode;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.VerticalFlowLayout;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.uiDesigner.core.GridConstraints;
import com.intellij.uiDesigner.core.GridLayoutManager;
import com.intellij.util.containers.hash.LinkedHashMap;
import javax.swing.*;
import java.util.ArrayList;
import java.util.Map;
/**
* User: cdr
*/
public class TrafficProgressPanel extends JPanel {
private final JLabel statistics = new JLabel();
private final Map<ProgressableTextEditorHighlightingPass, Pair<JProgressBar, JLabel>> passes = new LinkedHashMap<ProgressableTextEditorHighlightingPass, Pair<JProgressBar, JLabel>>();
private final JLabel statusLabel = new JLabel();
private final TrafficLightRenderer myTrafficLightRenderer;
private final JPanel passStatuses = new JPanel();
public TrafficProgressPanel(TrafficLightRenderer trafficLightRenderer, Editor editor) {
super(new VerticalFlowLayout());
myTrafficLightRenderer = trafficLightRenderer;
LineTooltipRenderer.setColors(this);
LineTooltipRenderer.setBorder(this);
LineTooltipRenderer.setColors(passStatuses);
add(statusLabel);
TrafficLightRenderer.DaemonCodeAnalyzerStatus fakeStatusLargeEnough = new TrafficLightRenderer.DaemonCodeAnalyzerStatus();
fakeStatusLargeEnough.errorCount = new int[]{1,1,1,1};
Project project = trafficLightRenderer.getProject();
PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
for (int i=0; i<3;i++) {
fakeStatusLargeEnough.passStati.add(new ProgressableTextEditorHighlightingPass(project, null, DaemonBundle.message("pass.wolf"), psiFile, false) {
@Override
protected void collectInformationWithProgress(ProgressIndicator progress) {
}
@Override
protected void applyInformationWithProgress() {
}
});
}
rebuildPassesPanel(fakeStatusLargeEnough);
for (Pair<JProgressBar, JLabel> pair : passes.values()) {
JProgressBar bar = pair.first;
bar.setMaximum(100);
JLabel label = pair.second;
label.setText("100%");
}
add(passStatuses);
add(statistics);
updatePanel(fakeStatusLargeEnough);
}
private void rebuildPassesPanel(TrafficLightRenderer.DaemonCodeAnalyzerStatus status) {
passStatuses.removeAll();
passStatuses.setLayout(new GridLayoutManager(Math.max(status.passStati.size(),1), 3));
passes.clear();
GridConstraints constraints = new GridConstraints();
for (ProgressableTextEditorHighlightingPass pass : status.passStati) {
JLabel label = new JLabel(pass.getPresentableName() + ":");
JProgressBar progressBar = new JProgressBar(0,100);
JLabel percLabel = new JLabel();
passes.put(pass, Pair.create(progressBar, percLabel));
constraints.setColumn(0); passStatuses.add(label, constraints);
constraints.setColumn(1); passStatuses.add(progressBar, constraints);
constraints.setColumn(2); passStatuses.add(percLabel, constraints);
constraints.setRow(constraints.getRow()+1);
}
}
public void updatePanel(TrafficLightRenderer.DaemonCodeAnalyzerStatus status) {
if (status == null) return;
if (PowerSaveMode.isEnabled()) {
statusLabel.setText("Code analysis is disabled in power save mode.");
passStatuses.setVisible(false);
statistics.setText("");
return;
}
if (status.noHighlightingRoots != null && status.noHighlightingRoots.length == status.rootsNumber) {
statusLabel.setText(DaemonBundle.message("analysis.hasnot.been.run"));
passStatuses.setVisible(false);
statistics.setText("");
return;
}
if (status.errorAnalyzingFinished) {
statusLabel.setText(DaemonBundle.message("analysis.completed"));
passStatuses.setVisible(false);
}
else {
statusLabel.setText(DaemonBundle.message("performing.code.analysis"));
passStatuses.setVisible(true);
if (!status.passStati.equals(new ArrayList<ProgressableTextEditorHighlightingPass>(passes.keySet()))) {
// passes set has changed
rebuildPassesPanel(status);
}
for (ProgressableTextEditorHighlightingPass pass : status.passStati) {
double progress = pass.getProgress();
Pair<JProgressBar, JLabel> pair = passes.get(pass);
JProgressBar progressBar = pair.first;
int percent = (int)Math.round(progress * 100);
progressBar.setValue(percent);
JLabel percentage = pair.second;
percentage.setText(percent + "%");
}
}
int currentSeverityErrors = 0;
String text = "<html><body>";
for (int i = status.errorCount.length - 1; i >= 0; i--) {
if (status.errorCount[i] > 0) {
final HighlightSeverity severity = SeverityRegistrar.getInstance(myTrafficLightRenderer.getProject()).getSeverityByIndex(i);
String name = status.errorCount[i] > 1 ? StringUtil.pluralize(severity.toString().toLowerCase()) : severity.toString().toLowerCase();
text += status.errorAnalyzingFinished
? DaemonBundle.message("errors.found", status.errorCount[i], name)
: DaemonBundle.message("errors.found.so.far", status.errorCount[i], name);
text += "<br>";
currentSeverityErrors += status.errorCount[i];
}
}
if (currentSeverityErrors == 0) {
text += status.errorAnalyzingFinished
? DaemonBundle.message("no.errors.or.warnings.found")
: DaemonBundle.message("no.errors.or.warnings.found.so.far") + "<br>";
}
statistics.setText(text);
}
}
@@ -0,0 +1,74 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.daemon.impl;
import com.intellij.codeInsight.hint.HintManager;
import com.intellij.codeInsight.hint.HintManagerImpl;
import com.intellij.codeInsight.hint.LineTooltipRenderer;
import com.intellij.codeInsight.hint.TooltipGroup;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.impl.EditorMarkupModelImpl;
import com.intellij.openapi.editor.impl.TrafficTooltipRenderer;
import com.intellij.ui.LightweightHint;
import java.awt.*;
/**
* User: cdr
*/
public class TrafficTooltipRendererImpl implements TrafficTooltipRenderer {
private TrafficProgressPanel myPanel;
private final Runnable onHide;
private TrafficLightRenderer myTrafficLightRenderer;
public TrafficTooltipRendererImpl(Runnable onHide) {
this.onHide = onHide;
}
@Override
public void repaintTooltipWindow() {
if (myPanel != null) {
myPanel.updatePanel(myTrafficLightRenderer.getDaemonCodeAnalyzerStatus(true));
}
}
@Override
public LightweightHint show(Editor editor, Point p, boolean alignToRight, TooltipGroup group) {
myTrafficLightRenderer = (TrafficLightRenderer)((EditorMarkupModelImpl)editor.getMarkupModel()).getErrorStripeRenderer();
myPanel = new TrafficProgressPanel(myTrafficLightRenderer, editor);
LineTooltipRenderer.correctLocation(editor, myPanel, p, alignToRight, false, -1);
LightweightHint hint = new LightweightHint(myPanel) {
@Override
public void hide() {
if (myPanel == null) return; //double hide?
super.hide();
myPanel = null;
onHide.run();
}
};
HintManagerImpl hintManager = (HintManagerImpl)HintManager.getInstance();
hintManager.showEditorHint(hint, editor, p,
HintManager.HIDE_BY_ANY_KEY | HintManager.HIDE_BY_TEXT_CHANGE | HintManager.HIDE_BY_OTHER_HINT |
HintManager.HIDE_BY_SCROLLING, 0, false);
repaintTooltipWindow();
return hint;
}
@Override
public boolean equals(Object obj) {
return obj.getClass() == getClass() && myTrafficLightRenderer == ((TrafficTooltipRendererImpl)obj).myTrafficLightRenderer;
}
}
@@ -0,0 +1,50 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.daemon.impl;
import javax.swing.*;
import java.awt.*;
/**
* User: cdr
*/
public class TruncatingIcon implements Icon {
private final int myWidth;
private final int myHeight;
private final Icon myDelegate;
public TruncatingIcon(Icon delegate, int width, int height) {
myDelegate = delegate;
myWidth = width;
myHeight = height;
}
public void paintIcon(Component c, Graphics g, int x, int y) {
Shape old = g.getClip();
g.clipRect(x, y, myWidth, myHeight);
myDelegate.paintIcon(c, g, x, y);
g.setClip(old);
}
public int getIconWidth() {
return myWidth;
}
public int getIconHeight() {
return myHeight;
}
}
@@ -20,6 +20,7 @@ import com.intellij.codeHighlighting.Pass;
import com.intellij.codeHighlighting.TextEditorHighlightingPass;
import com.intellij.codeHighlighting.TextEditorHighlightingPassFactory;
import com.intellij.codeHighlighting.TextEditorHighlightingPassRegistrar;
import com.intellij.codeInsight.daemon.DaemonBundle;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.ex.InspectionProfileWrapper;
import com.intellij.openapi.Disposable;
@@ -100,6 +101,11 @@ public class WholeFileLocalInspectionsPassFactory extends AbstractProjectCompone
return result;
}
@Override
protected String getPresentableName() {
return DaemonBundle.message("pass.whole.inspections");
}
void inspectInjectedPsi(PsiElement[] elements, List<LocalInspectionTool> tools) {
// inspected in LIP already
}
@@ -31,7 +31,7 @@ import org.jetbrains.annotations.NotNull;
*/
class WolfHighlightingPass extends ProgressableTextEditorHighlightingPass implements DumbAware {
WolfHighlightingPass(@NotNull Project project, @NotNull Document document, @NotNull PsiFile file) {
super(project, document, null, DaemonBundle.message("pass.wolf"), file, false);
super(project, document, DaemonBundle.message("pass.wolf"), file, false);
}
protected void collectInformationWithProgress(final ProgressIndicator progress) {
@@ -229,13 +229,15 @@ public class WolfTheProblemSolverImpl extends WolfTheProblemSolver {
public void startCheckingIfVincentSolvedProblemsYet(final ProgressIndicator progress, ProgressableTextEditorHighlightingPass pass) throws ProcessCanceledException{
if (!myProject.isOpen()) return;
int size;
List<VirtualFile> files;
synchronized (myCheckingQueue) {
files = new ArrayList<VirtualFile>(myCheckingQueue);
size = files.size();
}
pass.setProgressLimit(size);
long progressLimit = 0;
for (VirtualFile file : files) {
progressLimit += file.getLength(); // (rough approx number of PSI elements = file length/2) * (visitor count = 2 usually)
}
pass.setProgressLimit(progressLimit);
final StatusBar statusBar = WindowManager.getInstance().getStatusBar(myProject);
String oldInfo = saveStatusBarInfo(statusBar);
try {
@@ -244,13 +246,14 @@ public class WolfTheProblemSolverImpl extends WolfTheProblemSolver {
if (virtualFile == null) break;
ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {
if (progress.isCanceled()) return;
statusBar.setInfo("Checking '" + virtualFile.getPresentableUrl() + "'");
}
});
if (!virtualFile.isValid() || orderVincentToCleanTheCar(virtualFile, progress)) {
doRemove(virtualFile);
}
pass.advanceProgress(1);
pass.advanceProgress(virtualFile.getLength());
}
}
finally {
@@ -23,6 +23,7 @@ import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.keymap.KeymapManager;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.LightweightHint;
@@ -63,57 +64,14 @@ public class LineTooltipRenderer implements TooltipRenderer {
myText = myText.replaceAll(String.valueOf(UIUtil.MNEMONIC), "");
final boolean expanded = myCurrentWidth > 0 && dressDescription(editor);
//pane
final JEditorPane pane = initPane(myText);
pane.setCaretPosition(0);
final HintManagerImpl hintManager = HintManagerImpl.getInstanceImpl();
final JComponent contentComponent = editor.getContentComponent();
final JComponent editorComponent = editor.getComponent();
final JLayeredPane layeredPane = editorComponent.getRootPane().getLayeredPane();
int widthLimit = layeredPane.getWidth() - 10;
int heightLimit = layeredPane.getHeight() - 5;
final JEditorPane pane = initPane(myText);
correctLocation(editor, pane, p, alignToRight, expanded, myCurrentWidth);
final JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(pane);
scrollPane.setBorder(null);
int width = expanded ? 3 * myCurrentWidth / 2 : pane.getPreferredSize().width;
int height = expanded ? Math.max(pane.getPreferredSize().height, 150) : pane.getPreferredSize().height;
if (alignToRight) {
p.x = Math.max(0, p.x - width);
}
// try to make cursor outside tooltip. SCR 15038
p.x += 3;
p.y += 3;
if (p.x >= widthLimit - width) {
p.x = widthLimit - width;
width = Math.min(width, widthLimit);
height += 20;
}
if (p.x < 3) {
p.x = 3;
}
if (p.y > heightLimit - height) {
p.y = heightLimit - height;
height = Math.min(heightLimit, height);
}
if (p.y < 3) {
p.y = 3;
}
locateOutsideMouseCursor(editor, layeredPane, p, width, height, heightLimit);
// in order to restrict tooltip size
pane.setSize(width, height);
pane.setMaximumSize(new Dimension(width, height));
pane.setMinimumSize(new Dimension(width, height));
pane.setPreferredSize(new Dimension(width, height));
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
@@ -193,11 +151,79 @@ public class LineTooltipRenderer implements TooltipRenderer {
});
hintManager.showEditorHint(hint, editor, p,
HintManagerImpl.HIDE_BY_ANY_KEY | HintManagerImpl.HIDE_BY_TEXT_CHANGE | HintManagerImpl.HIDE_BY_OTHER_HINT |
HintManagerImpl.HIDE_BY_SCROLLING, 0, false);
HintManager.HIDE_BY_ANY_KEY | HintManager.HIDE_BY_TEXT_CHANGE | HintManager.HIDE_BY_OTHER_HINT |
HintManager.HIDE_BY_SCROLLING, 0, false);
return hint;
}
public static void correctLocation(Editor editor,
JComponent tooltipComponent,
Point p,
boolean alignToRight,
boolean expanded,
int currentWidth) {
final JComponent editorComponent = editor.getComponent();
final JLayeredPane layeredPane = editorComponent.getRootPane().getLayeredPane();
int widthLimit = layeredPane.getWidth() - 10;
int heightLimit = layeredPane.getHeight() - 5;
Dimension dimension = correctLocation(editor, p, alignToRight, expanded, tooltipComponent, layeredPane, widthLimit, heightLimit, currentWidth);
// in order to restrict tooltip size
tooltipComponent.setSize(dimension);
tooltipComponent.setMaximumSize(dimension);
tooltipComponent.setMinimumSize(dimension);
tooltipComponent.setPreferredSize(dimension);
}
private static Dimension correctLocation(Editor editor,
Point p,
boolean alignToRight,
boolean expanded,
JComponent tooltipComponent,
JLayeredPane layeredPane,
int widthLimit,
int heightLimit,
int currentWidth) {
Dimension preferredSize = tooltipComponent.getPreferredSize();
int width = expanded ? 3 * currentWidth / 2 : preferredSize.width;
int height = expanded ? Math.max(preferredSize.height, 150) : preferredSize.height;
Dimension dimension = new Dimension(width, height);
if (alignToRight) {
p.x = Math.max(0, p.x - width);
}
// try to make cursor outside tooltip. SCR 15038
p.x += 3;
p.y += 3;
if (p.x >= widthLimit - width) {
p.x = widthLimit - width;
width = Math.min(width, widthLimit);
height += 20;
dimension = new Dimension(width, height);
}
if (p.x < 3) {
p.x = 3;
}
if (p.y > heightLimit - height) {
p.y = heightLimit - height;
height = Math.min(heightLimit, height);
dimension = new Dimension(width, height);
}
if (p.y < 3) {
p.y = 3;
}
locateOutsideMouseCursor(editor, layeredPane, p, width, height, heightLimit);
return dimension;
}
private static void locateOutsideMouseCursor(Editor editor,
JComponent editorComponent,
Point p,
@@ -237,16 +263,26 @@ public class LineTooltipRenderer implements TooltipRenderer {
text = "<html><head>" + UIUtil.getCssFontDeclaration(UIUtil.getLabelFont()) + "</head><body>" + getHtmlBody(text) + "</body></html>";
final JEditorPane pane = new JEditorPane(UIUtil.HTML_MIME, text);
pane.setEditable(false);
setColors(pane);
setBorder(pane);
pane.setCaretPosition(0);
return pane;
}
public static void setColors(JComponent pane) {
pane.setForeground(Color.black);
pane.setBackground(HintUtil.INFORMATION_COLOR);
pane.setOpaque(true);
}
public static void setBorder(JComponent pane) {
pane.setBorder(
BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(Color.black),
BorderFactory.createEmptyBorder(0, 5, 0, 5)
)
);
pane.setForeground(Color.black);
pane.setBackground(HintUtil.INFORMATION_COLOR);
pane.setOpaque(true);
return pane;
}
public void addBelow(String text) {
@@ -284,11 +320,11 @@ public class LineTooltipRenderer implements TooltipRenderer {
final LineTooltipRenderer lineTooltipRenderer = (LineTooltipRenderer)o;
return myText == null ? lineTooltipRenderer.myText == null : myText.equals(lineTooltipRenderer.myText);
return Comparing.strEqual(myText, lineTooltipRenderer.myText);
}
public int hashCode() {
return myText != null ? myText.hashCode() : 0;
return myText == null ? 0 : myText.hashCode();
}
public String getText() {
@@ -20,6 +20,7 @@
package com.intellij.openapi.editor.ex;
import com.intellij.codeInsight.hint.TooltipRenderer;
import com.intellij.openapi.editor.impl.TrafficTooltipRenderer;
import com.intellij.openapi.editor.markup.RangeHighlighter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -31,4 +32,6 @@ public interface ErrorStripTooltipRendererProvider {
TooltipRenderer calcTooltipRenderer(@NotNull Collection<RangeHighlighter> highlighters);
TooltipRenderer calcTooltipRenderer(@NotNull String text);
TooltipRenderer calcTooltipRenderer(@NotNull String text, int width);
TrafficTooltipRenderer createTrafficTooltipRenderer(Runnable onHide);
}
@@ -41,6 +41,7 @@ import com.intellij.openapi.editor.markup.ErrorStripeRenderer;
import com.intellij.openapi.editor.markup.RangeHighlighter;
import com.intellij.openapi.util.IconLoader;
import com.intellij.ui.ColorUtil;
import com.intellij.ui.LightweightHint;
import com.intellij.ui.PopupHandler;
import com.intellij.util.Processor;
import com.intellij.util.ui.ButtonlessScrollBarUI;
@@ -49,6 +50,7 @@ import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.plaf.ScrollBarUI;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
@@ -110,6 +112,14 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark
myEditorSourceHeight = myEditor.getPreferredHeight();
}
public void repaintTrafficLightIcon() {
MyErrorPanel errorPanel = getErrorPanel();
if (errorPanel != null) {
errorPanel.myErrorStripeButton.repaint();
errorPanel.repaintTrafficTooltip();
}
}
private static class PositionedStripe {
private final Color color;
private final int yStart;
@@ -126,7 +136,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark
}
}
public boolean showToolTipByMouseMove(final MouseEvent e, final double width) {
private boolean showToolTipByMouseMove(final MouseEvent e, final double width) {
Set<RangeHighlighter> highlighters = new THashSet<RangeHighlighter>();
getNearestHighlighters(this, e, width, highlighters);
@@ -199,10 +209,15 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark
myEditor.getVerticalScrollBar().setPersistentUI(ButtonlessScrollBarUI.createNormal());
}
}
private MyErrorPanel getErrorPanel() {
ScrollBarUI ui = myEditor.getVerticalScrollBar().getUI();
return ui instanceof MyErrorPanel ? (MyErrorPanel)ui : null;
}
public void setErrorPanelPopupHandler(@NotNull PopupHandler handler) {
if (myEditor.getVerticalScrollBar().getUI() instanceof MyErrorPanel) {
((MyErrorPanel)myEditor.getVerticalScrollBar().getUI()).setPopupHandler(handler);
MyErrorPanel errorPanel = getErrorPanel();
if (errorPanel != null) {
errorPanel.setPopupHandler(handler);
}
}
@@ -553,10 +568,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark
}
if (e.getY() < buttonHeight && myErrorStripeRenderer != null) {
String tooltipMessage = myErrorStripeRenderer.getTooltipMessage();
if (tooltipMessage != null) {
showTooltip(e, myTooltipRendererProvider.calcTooltipRenderer(tooltipMessage));
}
showTrafficLightTooltip(e);
return;
}
@@ -572,6 +584,27 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark
}
}
private TrafficTooltipRenderer myTrafficTooltipRenderer;
private void showTrafficLightTooltip(MouseEvent e) {
//final String tooltipMessage = myErrorStripeRenderer.getTooltipMessage();
//if (tooltipMessage == null) return;
if (myTrafficTooltipRenderer == null) {
myTrafficTooltipRenderer = myTooltipRendererProvider.createTrafficTooltipRenderer(new Runnable() {
@Override
public void run() {
myTrafficTooltipRenderer = null;
}
});
}
showTooltip(e, myTrafficTooltipRenderer);
}
private void repaintTrafficTooltip() {
if (myTrafficTooltipRenderer != null) {
myTrafficTooltipRenderer.repaintTooltipWindow();
}
}
private void cancelMyToolTips(final MouseEvent e) {
final TooltipController tooltipController = TooltipController.getInstance();
if (!tooltipController.shouldSurvive(e)) {
@@ -680,6 +713,27 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark
public TooltipRenderer calcTooltipRenderer(@NotNull final String text, final int width) {
return new LineTooltipRenderer(text, width);
}
@Override
public TrafficTooltipRenderer createTrafficTooltipRenderer(final Runnable onHide) {
return new TrafficTooltipRenderer() {
@Override
public void repaintTooltipWindow() {
}
@Override
public LightweightHint show(Editor editor, Point p, boolean alignToRight, TooltipGroup group) {
JLabel label = new JLabel("WTF");
return new LightweightHint(label){
@Override
public void hide() {
super.hide();
onHide.run();
}
};
}
};
}
}
private int offsetToYPosition(int offset) {
@@ -0,0 +1,25 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.editor.impl;
import com.intellij.codeInsight.hint.TooltipRenderer;
/**
* User: cdr
*/
public interface TrafficTooltipRenderer extends TooltipRenderer {
void repaintTooltipWindow();
}
@@ -415,4 +415,7 @@ class TextEditorComponent extends JPanel implements DataProvider{
}
}
public VirtualFile getFile() {
return myFile;
}
}
@@ -144,4 +144,9 @@ public class TextEditorImpl extends UserDataHolderBase implements TextEditor {
OpenFileDescriptor d = (OpenFileDescriptor)navigatable;
d.navigateIn(getEditor());
}
@Override
public String toString() {
return "Editor: "+getComponent().getFile();
}
}
@@ -20,9 +20,10 @@ no.syntax.highlighting.performed.for=No syntax highlighting performed for {0}.
# suppress inspection "UnusedProperty"
no.inspections.performed.for=No inspections performed {0}.
pass.syntax=Syntax
pass.syntax=Syntax analysis
pass.inspection=Inspections
pass.wolf=Checking affected files
pass.wolf=Checking red files
pass.whole.inspections=Slow Inspections
analysis.completed=Analysis completed.
no.errors.or.warnings.found=No errors or warnings found.
@@ -154,17 +154,16 @@ public class DomElementsErrorPanel extends JPanel implements CommittablePanel, H
final DaemonCodeAnalyzerStatus status = super.getDaemonCodeAnalyzerStatus(fillErrorsCount);
if (status != null && isInspectionCompleted()) {
status.errorAnalyzingFinished = true;
for (final DaemonCodeAnalyzerStatus.PassStatus passStatus : status.passStati) {
passStatus.inProgressIcon = null;
}
}
return status;
}
@Override
protected void fillDaemonCodeAnalyzerErrorsStatus(DaemonCodeAnalyzerStatus status, boolean fillErrorsCount) {
protected void fillDaemonCodeAnalyzerErrorsStatus(DaemonCodeAnalyzerStatus status,
boolean fillErrorsCount,
SeverityRegistrar severityRegistrar) {
for (int i = 0; i < status.errorCount.length; i++) {
final HighlightSeverity minSeverity = SeverityRegistrar.getInstance(myProject).getSeverityByIndex(i);
final HighlightSeverity minSeverity = severityRegistrar.getSeverityByIndex(i);
if (minSeverity == null) {
continue;
}