mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-19 21:11:28 +07:00
fix RefCountHolder to avoid "all yellow" syndrome, highlighting cleanup
This commit is contained in:
@@ -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)) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user