fix RefCountHolder to avoid "all yellow" syndrome, highlighting cleanup

This commit is contained in:
Alexey Kudravtsev
2014-11-28 14:17:06 +03:00
parent 49083ec178
commit 6ce8da783c
11 changed files with 206 additions and 208 deletions

View File

@@ -39,7 +39,7 @@ import org.jetbrains.annotations.Nullable;
public class UnusedSymbolUtil {
private static final ImplicitUsageProvider[] ourImplicitUsageProviders = Extensions.getExtensions(ImplicitUsageProvider.EP_NAME);
static boolean isInjected(@NotNull Project project, @NotNull PsiModifierListOwner modifierListOwner) {
public static boolean isInjected(@NotNull Project project, @NotNull PsiModifierListOwner modifierListOwner) {
return EntryPointsManagerBase.getInstance(project).isEntryPoint(modifierListOwner);
}
@@ -57,7 +57,7 @@ public class UnusedSymbolUtil {
return false;
}
static boolean isImplicitRead(@NotNull Project project, @NotNull PsiVariable element, @NotNull ProgressIndicator progress) {
public static boolean isImplicitRead(@NotNull Project project, @NotNull PsiVariable element, @NotNull ProgressIndicator progress) {
for(ImplicitUsageProvider provider: ourImplicitUsageProviders) {
progress.checkCanceled();
if (provider.isImplicitRead(element)) {
@@ -67,9 +67,9 @@ public class UnusedSymbolUtil {
return isInjected(project, element);
}
static boolean isImplicitWrite(@NotNull Project project,
@NotNull PsiVariable element,
@NotNull ProgressIndicator progress) {
public static boolean isImplicitWrite(@NotNull Project project,
@NotNull PsiVariable element,
@NotNull ProgressIndicator progress) {
for(ImplicitUsageProvider provider: ourImplicitUsageProviders) {
progress.checkCanceled();
if (provider.isImplicitWrite(element)) {

View File

@@ -26,7 +26,6 @@ import com.intellij.codeInsight.ExceptionUtil;
import com.intellij.codeInsight.daemon.JavaErrorMessages;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
import com.intellij.codeInsight.daemon.impl.RefCountHolder;
import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixAction;
import com.intellij.codeInsight.intention.QuickFixFactory;
import com.intellij.ide.highlighter.JavaFileType;

View File

@@ -20,7 +20,6 @@ import com.intellij.codeInsight.daemon.DaemonBundle;
import com.intellij.codeInsight.daemon.JavaErrorMessages;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
import com.intellij.codeInsight.daemon.impl.RefCountHolder;
import com.intellij.codeInsight.daemon.impl.quickfix.*;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.intention.QuickFixFactory;

View File

@@ -13,10 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.daemon.impl;
package com.intellij.codeInsight.daemon.impl.analysis;
import com.intellij.codeInsight.daemon.*;
import com.intellij.codeInsight.daemon.impl.analysis.*;
import com.intellij.codeInsight.daemon.HighlightDisplayKey;
import com.intellij.codeInsight.daemon.ImplicitUsageProvider;
import com.intellij.codeInsight.daemon.JavaErrorMessages;
import com.intellij.codeInsight.daemon.UnusedImportProvider;
import com.intellij.codeInsight.daemon.impl.*;
import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixAction;
import com.intellij.codeInsight.intention.EmptyIntentionAction;
import com.intellij.codeInsight.intention.IntentionAction;
@@ -41,7 +44,6 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.PomNamedTarget;
import com.intellij.pom.java.LanguageLevel;
@@ -51,7 +53,6 @@ import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.impl.PsiClassImplUtil;
import com.intellij.psi.search.searches.OverridingMethodsSearch;
import com.intellij.psi.search.searches.SuperMethodsSearch;
import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.util.Processor;
@@ -63,9 +64,8 @@ import org.jetbrains.annotations.PropertyKey;
import java.util.List;
import java.util.Set;
public class PostHighlightingVisitor {
class PostHighlightingVisitor {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.PostHighlightingPass");
private static final Key<Long> LAST_POST_PASS_TIMESTAMP = Key.create("LAST_POST_PASS_TIMESTAMP");
private final LanguageLevel myLanguageLevel;
private final RefCountHolder myRefCountHolder;
@NotNull private final Project myProject;
@@ -82,17 +82,6 @@ public class PostHighlightingVisitor {
private final HighlightInfoType myDeadCodeInfoType;
private final UnusedDeclarationInspectionBase myDeadCodeInspection;
private static boolean isUpToDate(@NotNull PsiFile file) {
Long lastStamp = file.getUserData(LAST_POST_PASS_TIMESTAMP);
long currentStamp = PsiModificationTracker.SERVICE.getInstance(file.getProject()).getModificationCount();
return lastStamp != null && lastStamp == currentStamp || !ProblemHighlightFilter.shouldHighlightFile(file);
}
private static void markFileUpToDate(@NotNull PsiFile file) {
long lastStamp = PsiModificationTracker.SERVICE.getInstance(file.getProject()).getModificationCount();
file.putUserData(LAST_POST_PASS_TIMESTAMP, lastStamp);
}
private void optimizeImportsOnTheFlyLater(@NotNull final ProgressIndicator progress) {
if ((myHasRedundantImports || myHasMissortedImports) && !progress.isCanceled()) {
// schedule optimise action at the time of session disposal, which is after all applyInformation() calls
@@ -237,8 +226,6 @@ public class PostHighlightingVisitor {
fileStatusMap.setErrorFoundFlag(myProject, myDocument, true);
}
markFileUpToDate(myFile);
optimizeImportsOnTheFlyLater(progress);
}
@@ -260,7 +247,6 @@ public class PostHighlightingVisitor {
private HighlightInfo processIdentifier(@NotNull PsiIdentifier identifier, @NotNull ProgressIndicator progress, @NotNull GlobalUsageHelper helper) {
if (SuppressionUtil.inspectionResultSuppressed(identifier, myUnusedSymbolInspection)) return null;
PsiElement parent = identifier.getParent();
if (PsiUtilCore.hasErrorElementChild(parent)) return null;
if (parent instanceof PsiLocalVariable && myUnusedSymbolInspection.LOCAL_VARIABLE) {
return processLocalVariable((PsiLocalVariable)parent, identifier, progress);

View File

@@ -13,8 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.daemon.impl;
package com.intellij.codeInsight.daemon.impl.analysis;
import com.intellij.codeInsight.daemon.impl.DaemonProgressIndicator;
import com.intellij.codeInsight.daemon.impl.FileStatusMap;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.util.Key;
@@ -28,7 +30,7 @@ import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ArrayUtilRt;
import com.intellij.util.containers.BidirectionalMap;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.THashMap;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -40,15 +42,16 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
public class RefCountHolder {
class RefCountHolder {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.RefCountHolder");
private final PsiFile myFile;
private final BidirectionalMap<PsiReference,PsiElement> myLocalRefsMap = new BidirectionalMap<PsiReference, PsiElement>();
private final Map<PsiAnchor, Boolean> myDclsUsedMap = ContainerUtil.newConcurrentMap();
private final Map<PsiReference, PsiImportStatementBase> myImportStatements = ContainerUtil.newConcurrentMap();
private final AtomicReference<ProgressIndicator> myState = new AtomicReference<ProgressIndicator>(READY);
private final Map<PsiAnchor, Boolean> myDclsUsedMap = new THashMap<PsiAnchor, Boolean>();
private final Map<PsiReference, PsiImportStatementBase> myImportStatements = new THashMap<PsiReference, PsiImportStatementBase>();
private final AtomicReference<ProgressIndicator> myState = new AtomicReference<ProgressIndicator>(EMPTY);
// contains useful information
private static final ProgressIndicator READY = new DaemonProgressIndicator() {
{
cancel();
@@ -58,6 +61,16 @@ public class RefCountHolder {
return "READY";
}
};
// contains no information, must be rebuilt before use
private static final ProgressIndicator EMPTY = new DaemonProgressIndicator() {
{
cancel();
}
@Override
public String toString() {
return "EMPTY";
}
};
private static final Key<Reference<RefCountHolder>> REF_COUNT_HOLDER_IN_FILE_KEY = Key.create("REF_COUNT_HOLDER_IN_FILE_KEY");
@@ -265,12 +278,16 @@ public class RefCountHolder {
TextRange dirtyScope,
@NotNull ProgressIndicator indicator,
@NotNull Runnable analyze) {
if (!myState.compareAndSet(READY, indicator)) {
if (myState.compareAndSet(EMPTY, indicator)) {
clear();
}
else if (!myState.compareAndSet(READY, indicator)) {
log("a: failed to change ", myState, "->", indicator);
return false;
}
log("a: changed ", myState, "->", indicator);
boolean success = false;
try {
log("a: changed ", myState, "->", indicator);
if (dirtyScope != null) {
if (dirtyScope.equals(file.getTextRange())) {
clear();
@@ -281,12 +298,14 @@ public class RefCountHolder {
}
analyze.run();
success = true;
return true;
}
finally {
boolean set = myState.compareAndSet(indicator, READY);
ProgressIndicator result = success ? READY : EMPTY;
boolean set = myState.compareAndSet(indicator, result);
assert set : myState.get();
log("a: changed after analyze", indicator, "->", READY);
log("a: changed after analyze", indicator, "->", result);
}
}

View File

@@ -20,5 +20,6 @@ abstract class NodeRef<G extends Node<G, GR>, GR extends NodeRef<G, GR>> extends
class D {
void f() {
Version v = new Node<<error descr="Wildcard type '?' cannot be instantiated directly">?</error>, <error descr="Wildcard type '?' cannot be instantiated directly">?</error>>(){}<EOLError descr="';' expected"></EOLError>
v.hashCode();
}
}

View File

@@ -20,5 +20,6 @@ abstract class NodeRef<G extends Node<G, GR>, GR extends NodeRef<G, GR>> extends
class D {
void f() {
Version v = new Node<<error descr="Wildcard type '?' cannot be instantiated directly">?</error>, <error descr="Wildcard type '?' cannot be instantiated directly">?</error>>(){}<EOLError descr="';' expected"></EOLError>
v.hashCode();
}
}

View File

@@ -358,7 +358,7 @@ public class FileStatusMap implements Disposable {
}
private static final StringBuffer log = new StringBuffer();
private static final boolean IN_TESTS = ApplicationManager.getApplication().isUnitTestMode();
static void log(@NonNls Object... info) {
public static void log(@NonNls @NotNull Object... info) {
if (IN_TESTS) {
if (log.length() > 10000) {
log.replace(0, log.length()-5000, "");
@@ -373,5 +373,4 @@ public class FileStatusMap implements Disposable {
log.setLength(0);
return l;
}
}

View File

@@ -16,7 +16,6 @@
package com.intellij.codeInsight.daemon.impl;
import com.intellij.codeHighlighting.TextEditorHighlightingPass;
import com.intellij.codeInsight.daemon.DaemonBundle;
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
import com.intellij.codeInsight.daemon.impl.analysis.CustomHighlightInfoHolder;
@@ -84,17 +83,6 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
protected volatile boolean myHasErrorElement;
private volatile boolean myErrorFound;
private static final Comparator<HighlightVisitor> VISITOR_ORDER_COMPARATOR = new Comparator<HighlightVisitor>() {
@Override
public int compare(final HighlightVisitor o1, final HighlightVisitor o2) {
int delta = o1.order() - o2.order();
if (delta != 0) return delta;
if (o1.getClass() == o2.getClass()) {
LOG.error("Duplicate visitors registered: "+o1 +" and "+o2 + " ("+o1.getClass()+")");
}
return 0;
}
};
protected final EditorColorsScheme myGlobalScheme;
private volatile NotNullProducer<HighlightVisitor[]> myHighlightVisitorProducer = new NotNullProducer<HighlightVisitor[]>() {
@NotNull
@@ -166,9 +154,7 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
Arrays.asList(Extensions.getExtensions(HighlightVisitor.EP_HIGHLIGHT_VISITOR, myProject)));
}
HighlightVisitor[] visitorArray = visitors.toArray(new HighlightVisitor[visitors.size()]);
Arrays.sort(visitorArray, VISITOR_ORDER_COMPARATOR);
return visitorArray;
return visitors.toArray(new HighlightVisitor[visitors.size()]);
}
public void setHighlightVisitorProducer(@NotNull NotNullProducer<HighlightVisitor[]> highlightVisitorProducer) {
@@ -222,32 +208,26 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
outsideResult);
}
Runnable after1 = new Runnable() {
@Override
public void run() {
final TextRange priorityIntersection = myPriorityRange.intersection(myRestrictRange);
if ((!insideElements.isEmpty() || !insideResult.isEmpty()) &&
priorityIntersection != null) { // do not apply when there were no elements to highlight
boolean success = collectHighlights(insideElements, insideRanges, outsideElements, outsideRanges, progress, filteredVisitors, insideResult, outsideResult, forceHighlightParents);
myHighlightInfoProcessor.highlightsInsideVisiblePartAreProduced(myHighlightingSession, insideResult, myPriorityRange, myRestrictRange,
getId());
}
if (success) {
myHighlightInfoProcessor.highlightsOutsideVisiblePartAreProduced(myHighlightingSession, outsideResult, myPriorityRange,
myRestrictRange,
getId());
if (myUpdateAll) {
daemonCodeAnalyzer.getFileStatusMap().setErrorFoundFlag(myProject, myDocument, myErrorFound);
}
};
collectHighlights(insideElements, insideRanges, after1, outsideElements, outsideRanges, progress, filteredVisitors, insideResult, outsideResult, forceHighlightParents);
myHighlightInfoProcessor.highlightsOutsideVisiblePartAreProduced(myHighlightingSession, outsideResult, myPriorityRange, myRestrictRange,
getId());
if (myUpdateAll) {
daemonCodeAnalyzer.getFileStatusMap().setErrorFoundFlag(myProject, myDocument, myErrorFound);
}
else {
cancelAndRestartDaemonLater(progress, myProject);
}
}
finally {
incVisitorUsageCount(-1);
myHighlights.addAll(insideResult);
myHighlights.addAll(outsideResult);
}
myHighlights.addAll(insideResult);
myHighlights.addAll(outsideResult);
}
protected boolean isFailFastOnAcquireReadAction() {
@@ -273,16 +253,15 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
return new ArrayList<HighlightInfo>(myHighlights);
}
private void collectHighlights(@NotNull final List<PsiElement> elements1,
@NotNull final List<ProperTextRange> ranges1,
@NotNull final Runnable after1,
@NotNull final List<PsiElement> elements2,
@NotNull final List<ProperTextRange> ranges2,
@NotNull final ProgressIndicator progress,
@NotNull final HighlightVisitor[] visitors,
@NotNull final List<HighlightInfo> insideResult,
@NotNull final List<HighlightInfo> outsideResult,
final boolean forceHighlightParents) {
private boolean collectHighlights(@NotNull final List<PsiElement> elements1,
@NotNull final List<ProperTextRange> ranges1,
@NotNull final List<PsiElement> elements2,
@NotNull final List<ProperTextRange> ranges2,
@NotNull final ProgressIndicator progress,
@NotNull final HighlightVisitor[] visitors,
@NotNull final List<HighlightInfo> insideResult,
@NotNull final List<HighlightInfo> outsideResult,
final boolean forceHighlightParents) {
final Set<PsiElement> skipParentsSet = new THashSet<PsiElement>();
// TODO - add color scheme to holder
@@ -290,131 +269,32 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
final int chunkSize = Math.max(1, (elements1.size()+elements2.size()) / 100); // one percent precision is enough
final Runnable action = new Runnable() {
boolean success = analyzeByVisitors(visitors, holder, 0, new Runnable() {
@Override
public void run() {
Stack<Pair<TextRange, List<HighlightInfo>>> nested = new Stack<Pair<TextRange, List<HighlightInfo>>>();
boolean failed = false;
List<ProperTextRange> ranges = ranges1;
//noinspection unchecked
for (List<PsiElement> elements : new List[]{elements1, elements2}) {
nested.clear();
int nextLimit = chunkSize;
for (int i = 0; i < elements.size(); i++) {
PsiElement element = elements.get(i);
progress.checkCanceled();
PsiElement parent = element.getParent();
if (element != myFile && !skipParentsSet.isEmpty() && element.getFirstChild() != null && skipParentsSet.contains(element)) {
skipParentsSet.add(parent);
continue;
}
boolean isErrorElement = element instanceof PsiErrorElement;
if (isErrorElement) {
myHasErrorElement = true;
}
for (final HighlightVisitor visitor : visitors) {
try {
visitor.visit(element);
}
catch (ProcessCanceledException e) {
throw e;
}
catch (IndexNotReadyException e) {
throw e;
}
catch (Exception e) {
if (!failed) {
LOG.error(e);
}
failed = true;
}
}
if (i == nextLimit) {
advanceProgress(chunkSize);
nextLimit = i + chunkSize;
}
TextRange elementRange = ranges.get(i);
List<HighlightInfo> infosForThisRange = holder.size() == 0 ? null : new ArrayList<HighlightInfo>(holder.size());
for (int j = 0; j < holder.size(); j++) {
final HighlightInfo info = holder.get(j);
assert info != null;
if (!myRestrictRange.containsRange(info.getStartOffset(), info.getEndOffset())) continue;
List<HighlightInfo> result = myPriorityRange.containsRange(info.getStartOffset(), info.getEndOffset()) && !(element instanceof PsiFile) ? insideResult : outsideResult;
// have to filter out already obtained highlights
if (!result.add(info)) continue;
boolean isError = info.getSeverity() == HighlightSeverity.ERROR;
if (isError) {
if (!forceHighlightParents) {
skipParentsSet.add(parent);
}
myErrorFound = true;
}
// if this highlight info range is exactly the same as the element range we are visiting
// that means we can clear this highlight as soon as visitors won't produce any highlights during visiting the same range next time.
// We also know that we can remove syntax error element.
info.setBijective(elementRange.equalsToRange(info.startOffset, info.endOffset) || isErrorElement);
myHighlightInfoProcessor.infoIsAvailable(myHighlightingSession, info);
infosForThisRange.add(info);
}
holder.clear();
// include infos which we got while visiting nested elements with the same range
while (true) {
if (!nested.isEmpty() && elementRange.contains(nested.peek().first)) {
Pair<TextRange, List<HighlightInfo>> old = nested.pop();
if (elementRange.equals(old.first)) {
if (infosForThisRange == null) {
infosForThisRange = old.second;
}
else if (old.second != null){
infosForThisRange.addAll(old.second);
}
}
}
else {
break;
}
}
nested.push(Pair.create(elementRange, infosForThisRange));
if (parent == null || !Comparing.equal(elementRange, parent.getTextRange())) {
myHighlightInfoProcessor.allHighlightsForRangeAreProduced(myHighlightingSession, elementRange, infosForThisRange);
}
}
advanceProgress(elements.size() - (nextLimit-chunkSize));
if (elements == elements1) {
after1.run();
ranges = ranges2;
}
runVisitors(elements1, ranges1, chunkSize, progress, skipParentsSet, holder, insideResult, outsideResult, forceHighlightParents, visitors);
final TextRange priorityIntersection = myPriorityRange.intersection(myRestrictRange);
if ((!elements1.isEmpty() || !insideResult.isEmpty()) && priorityIntersection != null) { // do not apply when there were no elements to highlight
myHighlightInfoProcessor.highlightsInsideVisiblePartAreProduced(myHighlightingSession, insideResult, myPriorityRange, myRestrictRange, getId());
}
runVisitors(elements2, ranges2, chunkSize, progress, skipParentsSet, holder, insideResult, outsideResult, forceHighlightParents, visitors);
}
};
analyzeByVisitors(progress, visitors, holder, 0, action);
});
List<HighlightInfo> postInfos = new ArrayList<HighlightInfo>(holder.size());
// there can be extra highlights generated in post-highlight step, e.g. PostHighlightVisitor
// there can be extra highlights generated in PostHighlightVisitor
for (int j = 0; j < holder.size(); j++) {
final HighlightInfo info = holder.get(j);
assert info != null;
postInfos.add(info);
}
myHighlightInfoProcessor.highlightsInsideVisiblePartAreProduced(myHighlightingSession, postInfos, myFile.getTextRange(),
myFile.getTextRange(),
POST_UPDATE_ALL);
myHighlightInfoProcessor.highlightsInsideVisiblePartAreProduced(myHighlightingSession, postInfos, myFile.getTextRange(), myFile.getTextRange(), POST_UPDATE_ALL);
return success;
}
private static final int POST_UPDATE_ALL = 5;
private void analyzeByVisitors(@NotNull final ProgressIndicator progress,
@NotNull final HighlightVisitor[] visitors,
@NotNull final HighlightInfoHolder holder,
final int i,
@NotNull final Runnable action) {
private boolean analyzeByVisitors(@NotNull final HighlightVisitor[] visitors,
@NotNull final HighlightInfoHolder holder,
final int i,
@NotNull final Runnable action) {
if (i == visitors.length) {
action.run();
}
@@ -422,17 +302,126 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
if (!visitors[i].analyze(myFile, myUpdateAll, holder, new Runnable() {
@Override
public void run() {
analyzeByVisitors(progress, visitors, holder, i+1, action);
analyzeByVisitors(visitors, holder, i+1, action);
}
})) {
cancelAndRestartDaemonLater(progress, myProject, this);
return false;
}
}
return true;
}
private void runVisitors(@NotNull List<PsiElement> elements,
@NotNull List<ProperTextRange> ranges,
int chunkSize,
@NotNull ProgressIndicator progress,
@NotNull Set<PsiElement> skipParentsSet,
@NotNull HighlightInfoHolder holder,
@NotNull List<HighlightInfo> insideResult,
@NotNull List<HighlightInfo> outsideResult,
boolean forceHighlightParents,
@NotNull HighlightVisitor[] visitors) {
Stack<TextRange> nestedRange = new Stack<TextRange>();
Stack<List<HighlightInfo>> nestedInfos = new Stack<List<HighlightInfo>>();
boolean failed = false;
int nextLimit = chunkSize;
for (int i = 0; i < elements.size(); i++) {
PsiElement element = elements.get(i);
progress.checkCanceled();
PsiElement parent = element.getParent();
if (element != myFile && !skipParentsSet.isEmpty() && element.getFirstChild() != null && skipParentsSet.contains(element)) {
skipParentsSet.add(parent);
continue;
}
boolean isErrorElement = element instanceof PsiErrorElement;
if (isErrorElement) {
myHasErrorElement = true;
}
for (HighlightVisitor visitor : visitors) {
try {
visitor.visit(element);
}
catch (ProcessCanceledException e) {
throw e;
}
catch (IndexNotReadyException e) {
throw e;
}
catch (Exception e) {
if (!failed) {
LOG.error(e);
}
failed = true;
}
}
if (i == nextLimit) {
advanceProgress(chunkSize);
nextLimit = i + chunkSize;
}
TextRange elementRange = ranges.get(i);
List<HighlightInfo> infosForThisRange = holder.size() == 0 ? null : new ArrayList<HighlightInfo>(holder.size());
for (int j = 0; j < holder.size(); j++) {
final HighlightInfo info = holder.get(j);
assert info != null;
if (!myRestrictRange.containsRange(info.getStartOffset(), info.getEndOffset())) continue;
List<HighlightInfo> result = myPriorityRange.containsRange(info.getStartOffset(), info.getEndOffset()) && !(element instanceof PsiFile) ? insideResult : outsideResult;
// have to filter out already obtained highlights
if (!result.add(info)) continue;
boolean isError = info.getSeverity() == HighlightSeverity.ERROR;
if (isError) {
if (!forceHighlightParents) {
skipParentsSet.add(parent);
}
myErrorFound = true;
}
// if this highlight info range is exactly the same as the element range we are visiting
// that means we can clear this highlight as soon as visitors won't produce any highlights during visiting the same range next time.
// We also know that we can remove syntax error element.
info.setBijective(elementRange.equalsToRange(info.startOffset, info.endOffset) || isErrorElement);
myHighlightInfoProcessor.infoIsAvailable(myHighlightingSession, info);
infosForThisRange.add(info);
}
holder.clear();
// include infos which we got while visiting nested elements with the same range
while (true) {
if (!nestedRange.isEmpty() && elementRange.contains(nestedRange.peek())) {
TextRange oldRange = nestedRange.pop();
List<HighlightInfo> oldInfos = nestedInfos.pop();
if (elementRange.equals(oldRange)) {
if (infosForThisRange == null) {
infosForThisRange = oldInfos;
}
else if (oldInfos != null) {
infosForThisRange.addAll(oldInfos);
}
}
}
else {
break;
}
}
nestedRange.push(elementRange);
nestedInfos.push(infosForThisRange);
// optimisation: this element range does not equal to its parent' range if next element in "ranges" range is different since we top-sorted elements there by ancestry
if (parent == null || i != ranges.size()-1 && !elementRange.equals(ranges.get(i+1)) || !Comparing.equal(elementRange, parent.getTextRange())) {
myHighlightInfoProcessor.allHighlightsForRangeAreProduced(myHighlightingSession, elementRange, infosForThisRange);
}
}
advanceProgress(elements.size() - (nextLimit-chunkSize));
}
private static final int POST_UPDATE_ALL = 5;
static void cancelAndRestartDaemonLater(@NotNull ProgressIndicator progress,
@NotNull final Project project,
@NotNull TextEditorHighlightingPass passCalledFrom) throws ProcessCanceledException {
@NotNull final Project project) throws ProcessCanceledException {
progress.cancel();
JobScheduler.getScheduler().schedule(new Runnable() {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
* Copyright 2000-2014 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.
@@ -30,5 +30,6 @@ public interface HighlightVisitor {
boolean analyze(@NotNull PsiFile file, final boolean updateWholeFile, @NotNull HighlightInfoHolder holder, @NotNull Runnable action);
@NotNull
HighlightVisitor clone();
@Deprecated
int order();
}

View File

@@ -21,12 +21,14 @@ import com.intellij.openapi.Disposable;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.WrappedProgressIndicator;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.UserDataHolder;
import com.intellij.psi.PsiFile;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -74,6 +76,9 @@ public abstract class ProgressableTextEditorHighlightingPass extends TextEditorH
@Override
public final void doCollectInformation(@NotNull final ProgressIndicator progress) {
if (!(progress instanceof DaemonProgressIndicator)) {
throw new IncorrectOperationException("Highlighting must be run under DaemonProgressIndicator, but got: "+progress);
}
myFinished = false;
if (myFile != null) {
myHighlightingSession = new HighlightingSessionImpl(myFile, myEditor, progress, getColorsScheme(), getId(), myRestrictRange);
@@ -86,9 +91,7 @@ public abstract class ProgressableTextEditorHighlightingPass extends TextEditorH
}
progress.checkCanceled();
if (progress instanceof UserDataHolder) {
((UserDataHolder)progress).putUserData(HIGHLIGHTING_SESSION, myHighlightingSession);
}
((DaemonProgressIndicator)progress).putUserData(HIGHLIGHTING_SESSION, myHighlightingSession);
}
try {
collectInformationWithProgress(progress);
@@ -100,11 +103,12 @@ public abstract class ProgressableTextEditorHighlightingPass extends TextEditorH
}
}
private static final Key<HighlightingSession> HIGHLIGHTING_SESSION = Key.create("HIGHLIGHTING_SESSION");
public static HighlightingSession getHighlightingSession(@NotNull ProgressIndicator progressIndicator) {
return ((UserDataHolder)progressIndicator).getUserData(HIGHLIGHTING_SESSION);
public static HighlightingSession getHighlightingSession(@NotNull ProgressIndicator indicator) {
return indicator instanceof WrappedProgressIndicator ?
getHighlightingSession(((WrappedProgressIndicator)indicator).getOriginalProgressIndicator()) :
((UserDataHolder)indicator).getUserData(HIGHLIGHTING_SESSION);
}
protected abstract void collectInformationWithProgress(@NotNull ProgressIndicator progress);
@Override