replace highlighters after visible pass

This commit is contained in:
Alexey Kudravtsev
2010-09-23 19:07:31 +04:00
parent b2334a3953
commit e20d9dbc63
8 changed files with 341 additions and 153 deletions
@@ -245,14 +245,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
@Override public void visitClass(PsiClass aClass) {
super.visitClass(aClass);
if (aClass instanceof JspClass) return;
if (aClass.isAnnotationType()) {
if (!PsiUtil.isLanguageLevel5OrHigher(aClass)) {
HighlightInfo info = HighlightInfo
.createHighlightInfo(HighlightInfoType.ERROR, aClass.getNameIdentifier(), JavaErrorMessages.message("annotations.prior.15"));
QuickFixAction.registerQuickFixAction(info, new IncreaseLanguageLevelFix(LanguageLevel.JDK_1_5));
myHolder.add(info);
}
}
if (!myHolder.hasErrorResults()) myHolder.add(GenericsHighlightUtil.checkInterfaceMultipleInheritance(aClass));
if (!myHolder.hasErrorResults()) myHolder.add(HighlightClassUtil.checkDuplicateTopLevelClass(aClass));
if (!myHolder.hasErrorResults()) myHolder.add(GenericsHighlightUtil.checkEnumMustNotBeLocal(aClass));
@@ -391,10 +383,17 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
myHolder.add(HighlightUtil.checkVariableAlreadyDefined((PsiVariable)parent));
}
else if (parent instanceof PsiClass) {
myHolder.add(HighlightClassUtil.checkClassAlreadyImported((PsiClass)parent, identifier));
myHolder.add(HighlightClassUtil.checkExternalizableHasPublicNoArgsConstructor((PsiClass)parent, identifier));
if (!(parent instanceof PsiAnonymousClass)) {
myHolder.add(HighlightNamesUtil.highlightClassName((PsiClass)parent, ((PsiClass)parent).getNameIdentifier()));
PsiClass aClass = (PsiClass)parent;
if (aClass.isAnnotationType() && !PsiUtil.isLanguageLevel5OrHigher(aClass)) {
HighlightInfo info = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, identifier, JavaErrorMessages.message("annotations.prior.15"));
QuickFixAction.registerQuickFixAction(info, new IncreaseLanguageLevelFix(LanguageLevel.JDK_1_5));
myHolder.add(info);
}
myHolder.add(HighlightClassUtil.checkClassAlreadyImported(aClass, identifier));
myHolder.add(HighlightClassUtil.checkExternalizableHasPublicNoArgsConstructor(aClass, identifier));
if (!(parent instanceof PsiAnonymousClass) && aClass.getNameIdentifier() == identifier) {
myHolder.add(HighlightNamesUtil.highlightClassName(aClass, identifier));
}
}
else if (parent instanceof PsiMethod) {
@@ -103,7 +103,7 @@ public abstract class ImportClassFixBase<T extends PsiElement & PsiReference> im
public enum Result {
POPUP_SHOWN,
CLASS_IMPORTED,
CLASS_AUTO_IMPORTED,
POPUP_NOT_SHOWN
}
@@ -149,7 +149,7 @@ public abstract class ImportClassFixBase<T extends PsiElement & PsiReference> im
action.execute();
}
});
return Result.CLASS_IMPORTED;
return Result.CLASS_AUTO_IMPORTED;
}
if (doShow && canImportHere) {
@@ -174,7 +174,7 @@ public abstract class ImportClassFixBase<T extends PsiElement & PsiReference> im
return false;
}
Result result = doFix(editor, true, false);
return result == Result.POPUP_SHOWN || result == Result.CLASS_IMPORTED;
return result == Result.POPUP_SHOWN || result == Result.CLASS_AUTO_IMPORTED;
}
@NotNull
@@ -205,7 +205,7 @@ public class ImportHelperTest extends DaemonAnalyzerTestCase {
myEditor.getCaretModel().moveToOffset(offset - 1);
result = new ImportClassFix((PsiJavaCodeReferenceElement)ref).doFix(getEditor(), true, false);
assertEquals(ImportClassFixBase.Result.CLASS_IMPORTED, result);
assertEquals(ImportClassFixBase.Result.CLASS_AUTO_IMPORTED, result);
UIUtil.dispatchAllInvocationEvents();
assertEmpty(filter(doHighlighting(), HighlightSeverity.ERROR));
@@ -234,7 +234,7 @@ public class ImportHelperTest extends DaemonAnalyzerTestCase {
assertTrue(ref instanceof PsiJavaCodeReferenceElement);
ImportClassFixBase.Result result = new ImportClassFix((PsiJavaCodeReferenceElement)ref).doFix(getEditor(), true, false);
assertEquals(ImportClassFixBase.Result.CLASS_IMPORTED, result);
assertEquals(ImportClassFixBase.Result.CLASS_AUTO_IMPORTED, result);
UIUtil.dispatchAllInvocationEvents();
assertEmpty(filter(doHighlighting(), HighlightSeverity.ERROR));
@@ -265,7 +265,7 @@ public class ImportHelperTest extends DaemonAnalyzerTestCase {
(PsiJavaCodeReferenceElement)getFile().findReferenceAt(getEditor().getCaretModel().getOffset() - 2);
ImportClassFix fix = new ImportClassFix(element);
ImportClassFixBase.Result result = fix.doFix(getEditor(), false, false);
assertEquals(ImportClassFixBase.Result.CLASS_IMPORTED, result);
assertEquals(ImportClassFixBase.Result.CLASS_AUTO_IMPORTED, result);
assertNotSame(0, ((PsiJavaFile)getFile()).getImportList().getAllImportStatements().length);
}
@@ -52,19 +52,21 @@ public class Divider {
}
}
}
private static void getInsideAndOutside(@NotNull PsiFile root,
int startOffset,
int endOffset,
@NotNull TextRange range,
@NotNull List<PsiElement> inside,
@NotNull List<PsiElement> outside) {
@NotNull List<PsiElement> outside
) {
final int currentOffset = root.getTextRange().getStartOffset();
final Condition<PsiElement>[] filters = Extensions.getExtensions(CollectHighlightsUtil.EP_NAME);
int offset = currentOffset;
final TIntStack starts = new TIntStack(STARTING_TREE_HEIGHT);
starts.push(startOffset);
final Stack<PsiElement> elements = new Stack<PsiElement>(STARTING_TREE_HEIGHT);
final Stack<PsiElement> children = new Stack<PsiElement>(STARTING_TREE_HEIGHT);
PsiElement element = root;
@@ -96,7 +98,6 @@ public class Divider {
offset += element.getTextLength();
}
if (elements.isEmpty()) break;
int start = starts.pop();
if (startOffset <= start && offset <= endOffset) {
if (range.containsRange(start, offset)) {
@@ -107,6 +108,7 @@ public class Divider {
}
}
if (elements.isEmpty()) break;
element = elements.pop();
child = children.pop();
}
@@ -120,7 +122,5 @@ public class Divider {
child = PsiUtilBase.NULL_PSI_ELEMENT;
}
}
outside.add(root);
}
}
@@ -31,12 +31,15 @@ import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.HighlighterColors;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.editor.colors.EditorColors;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.colors.EditorColorsScheme;
import com.intellij.openapi.editor.colors.TextAttributesKey;
import com.intellij.openapi.editor.ex.MarkupModelEx;
import com.intellij.openapi.editor.markup.MarkupModel;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileTypes.SyntaxHighlighter;
@@ -52,6 +55,7 @@ import com.intellij.problems.Problem;
import com.intellij.problems.WolfTheProblemSolver;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
import com.intellij.psi.impl.source.tree.injected.Place;
import com.intellij.psi.search.PsiSearchHelper;
import com.intellij.psi.search.TodoItem;
import com.intellij.psi.tree.IElementType;
@@ -60,6 +64,7 @@ import com.intellij.util.SmartList;
import com.intellij.util.ui.UIUtil;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.util.*;
@@ -74,10 +79,10 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
private final int myStartOffset;
private final int myEndOffset;
private final boolean myUpdateAll;
private final TextRange myPriorityRange;
private final ProperTextRange myPriorityRange;
private final Editor myEditor;
private volatile Collection<HighlightInfo> myHighlights = Collections.emptyList();
private final Map<TextRange,Collection<HighlightInfo>> myInjectedPsiHighlights = new HashMap<TextRange, Collection<HighlightInfo>>();
private final Collection<HighlightInfo> myHighlights = new ArrayList<HighlightInfo>();
protected volatile boolean myHasErrorElement;
private volatile boolean myErrorFound;
@@ -86,15 +91,15 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
return o1.order() - o2.order();
}
};
private Runnable myApplyCommand;
public GeneralHighlightingPass(@NotNull Project project,
@NotNull PsiFile file,
@NotNull Document document,
int startOffset,
int endOffset,
boolean updateAll
) {
this(project, file, document, startOffset, endOffset, updateAll, new TextRange(0,0));
boolean updateAll) {
this(project, file, document, startOffset, endOffset, updateAll, new ProperTextRange(0,document.getTextLength()), null);
}
public GeneralHighlightingPass(@NotNull Project project,
@NotNull PsiFile file,
@@ -102,18 +107,29 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
int startOffset,
int endOffset,
boolean updateAll,
@NotNull TextRange priorityRange) {
@NotNull ProperTextRange priorityRange,
@Nullable Editor editor) {
super(project, document, PRESENTABLE_NAME, file, true);
myStartOffset = startOffset;
myEndOffset = endOffset;
myUpdateAll = updateAll;
myPriorityRange = priorityRange;
myEditor = editor;
LOG.assertTrue(file.isValid());
setId(Pass.UPDATE_ALL);
myHasErrorElement = !isWholeFileHighlighting() && Boolean.TRUE.equals(myFile.getUserData(HAS_ERROR_ELEMENT));
FileStatusMap fileStatusMap = ((DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(myProject)).getFileStatusMap();
myErrorFound = !isWholeFileHighlighting() && fileStatusMap.wasErrorFound(myDocument);
myApplyCommand = new Runnable() {
public void run() {
ProperTextRange range = new ProperTextRange(myStartOffset, myEndOffset);
MarkupModel model = myDocument.getMarkupModel(myProject);
UpdateHighlightersUtil.cleanFileLevelHighlights(myProject, Pass.UPDATE_ALL,myFile);
UpdateHighlightersUtil.setHighlightersInRange(range, myHighlights, (MarkupModelEx)model, Pass.UPDATE_ALL, myDocument, myProject);
}
};
}
private static final Key<AtomicInteger> HIGHLIGHT_VISITOR_INSTANCE_COUNT = new Key<AtomicInteger>("HIGHLIGHT_VISITOR_INSTANCE_COUNT");
@@ -144,26 +160,94 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
}
protected void collectInformationWithProgress(final ProgressIndicator progress) {
final Collection<HighlightInfo> result = new THashSet<HighlightInfo>(100);
final Set<HighlightInfo> result = new THashSet<HighlightInfo>(100);
final Set<HighlightInfo> outsideResult = new THashSet<HighlightInfo>(100);
DaemonCodeAnalyzer daemonCodeAnalyzer = DaemonCodeAnalyzer.getInstance(myProject);
FileStatusMap fileStatusMap = ((DaemonCodeAnalyzerImpl)daemonCodeAnalyzer).getFileStatusMap();
final FileStatusMap fileStatusMap = ((DaemonCodeAnalyzerImpl)daemonCodeAnalyzer).getFileStatusMap();
HighlightVisitor[] highlightVisitors = createHighlightVisitors();
HighlightVisitor[] filteredVisitors = filterVisitors(highlightVisitors, myFile);
List<PsiElement> inside = new ArrayList<PsiElement>();
List<PsiElement> outside = new ArrayList<PsiElement>();
final HighlightVisitor[] filteredVisitors = filterVisitors(highlightVisitors, myFile);
final List<PsiElement> inside = new ArrayList<PsiElement>();
final List<PsiElement> outside = new ArrayList<PsiElement>();
try {
Divider.getInsideAndOutside(myFile, myStartOffset, myEndOffset, myPriorityRange, inside, outside, HighlightLevelUtil.AnalysisLevel.HIGHLIGHT);
final List<PsiFile> injectedInside = new ArrayList<PsiFile>();
final List<PsiFile> injectedOutside = new ArrayList<PsiFile>();
divideInjectedPsiHighlights(inside, outside, progress, injectedInside, injectedOutside);
List<PsiElement> elements = inside;
elements.addAll(outside);
setProgressLimit((long)(inside.size()+outside.size()) /** filteredVisitors.length*/);
result.addAll(collectHighlights(elements, progress, filteredVisitors));
if (!addInjectedPsiHighlights(elements, progress)) throw new ProcessCanceledException();
final boolean forceHighlightParents = forceHighlightParents();
if (!isDumbMode()) {
result.addAll(highlightTodos(myFile, myDocument.getCharsSequence(), 0, myFile.getTextLength(), progress));
highlightTodos(myFile, myDocument.getCharsSequence(), myStartOffset, myEndOffset, progress, myPriorityRange, result, outsideResult);
}
collectHighlights(inside, new Runnable() {
@Override
public void run() {
if (!addInjectedPsiHighlights(injectedInside, progress, Collections.synchronizedSet(result))) throw new ProcessCanceledException();
if (!outside.isEmpty() || !injectedOutside.isEmpty()) {
if (!inside.isEmpty()) { // do not apply when there were no elements to highlight
// clear infos found in visible area to avoid applying them twice
final List<HighlightInfo> toApply = new ArrayList<HighlightInfo>(result.size());
for (HighlightInfo info : result) {
if (myPriorityRange.containsRange(info.getStartOffset(), info.getEndOffset())) {
toApply.add(info);
}
else {
outsideResult.add(info);
}
}
myHighlights.addAll(toApply);
result.clear();
result.addAll(outsideResult);
UIUtil.invokeLaterIfNeeded(new Runnable() {
@Override
public void run() {
if (progress.isCanceled()) return;
MarkupModel markupModel = myDocument.getMarkupModel(myProject);
UpdateHighlightersUtil.cleanFileLevelHighlights(myProject, Pass.UPDATE_ALL, myFile);
UpdateHighlightersUtil.setHighlightersInRange(myPriorityRange, toApply, (MarkupModelEx)markupModel, Pass.UPDATE_ALL, myDocument, myProject);
}
});
UIUtil.invokeLaterIfNeeded(new Runnable() {
@Override
public void run() {
if (progress.isCanceled() || myEditor == null) return;
new ShowAutoImportPass(myProject, myFile, myEditor).applyInformationToEditor();
}
});
}
final List<HighlightInfo> injectedOutsideInfos = Collections.synchronizedList(new ArrayList<HighlightInfo>());
if (!addInjectedPsiHighlights(injectedOutside, progress, injectedOutsideInfos)) throw new ProcessCanceledException();
myApplyCommand = new Runnable() {
@Override
public void run() {
UpdateHighlightersUtil.setHighlightersToEditorOutsideRange(myProject, myDocument, result, myStartOffset, myEndOffset, myPriorityRange, Pass.UPDATE_ALL);
Map<TextRange,Collection<HighlightInfo>> myInjectedPsiHighlights = new HashMap<TextRange, Collection<HighlightInfo>>();
for (HighlightInfo info : injectedOutsideInfos) {
TextRange textRange = new TextRange(info.getStartOffset(), info.getEndOffset());
Collection<HighlightInfo> storedInfos = myInjectedPsiHighlights.get(textRange);
if (storedInfos == null) {
storedInfos = new SmartList<HighlightInfo>();
myInjectedPsiHighlights.put(textRange, storedInfos);
}
storedInfos.add(info);
}
UpdateHighlightersUtil.setHighlightersToEditor(myProject, myDocument, myInjectedPsiHighlights, Pass.UPDATE_ALL);
}
};
}
}
}, outside, progress, filteredVisitors, result, forceHighlightParents);
if (myUpdateAll) {
fileStatusMap.setErrorFoundFlag(myDocument, myErrorFound);
}
@@ -171,17 +255,21 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
finally {
incVisitorUsageCount(-1);
}
myHighlights = result;
myHighlights.addAll(result);
}
// returns false if canceled
private boolean addInjectedPsiHighlights(@NotNull final List<PsiElement> elements, final ProgressIndicator progress) {
private void divideInjectedPsiHighlights(@NotNull final List<PsiElement> elements1,
@NotNull final List<PsiElement> elements2,
@NotNull final ProgressIndicator progress,
@NotNull List<PsiFile> inside,
@NotNull List<PsiFile> outside) {
List<DocumentWindow> injected = InjectedLanguageUtil.getCachedInjectedDocuments(myFile);
Collection<PsiElement> hosts = new THashSet<PsiElement>(elements.size() + injected.size());
Collection<PsiElement> hosts = new THashSet<PsiElement>(elements1.size() + elements2.size() + injected.size());
// rehighlight all injected PSI regardless the range,
// since change in one place can lead to invalidation of injected PSI in (completely) other place.
for (DocumentWindow documentRange : injected) {
progress.checkCanceled();
if (!documentRange.isValid()) continue;
PsiFile file = PsiDocumentManager.getInstance(myProject).getPsiFile(documentRange);
if (file == null) continue;
@@ -193,40 +281,50 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
hosts.add(context);
}
}
hosts.addAll(elements);
final Collection<PsiFile> injectedFiles = new THashSet<PsiFile>();
EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme();
final TextAttributes injectedAttributes = scheme.getAttributes(EditorColors.INJECTED_LANGUAGE_FRAGMENT);
hosts.addAll(elements1);
hosts.addAll(elements2);
for (PsiElement element : hosts) {
progress.checkCanceled();
final List<PsiFile> destination = myPriorityRange.contains(element.getTextRange()) ? inside : outside;
InjectedLanguageUtil.enumerate(element, myFile, new PsiLanguageInjectionHost.InjectedPsiVisitor() {
public void visit(@NotNull PsiFile injectedPsi, @NotNull List<PsiLanguageInjectionHost.Shred> places) {
if (injectedFiles.add(injectedPsi)) { // for concatenations there can be many injection hosts with only one injected PSI
for (PsiLanguageInjectionHost.Shred place : places) {
TextRange textRange = place.getRangeInsideHost().shiftRight(place.host.getTextRange().getStartOffset());
if (textRange.isEmpty()) continue;
String desc = injectedPsi.getLanguage().getDisplayName() + ": " + injectedPsi.getText();
HighlightInfo info = HighlightInfo.createHighlightInfo(HighlightInfoType.INJECTED_LANGUAGE_FRAGMENT, textRange, null, desc, injectedAttributes);
addHighlightInfo(textRange, info);
}
}
destination.add(injectedPsi); // for concatenations there can be many injection hosts with only one injected PSI
}
}, false);
}
}
// returns false if canceled
private boolean addInjectedPsiHighlights(@NotNull final List<PsiFile> injectedFiles,
final ProgressIndicator progress,
final Collection<HighlightInfo> infos) {
EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme();
final TextAttributes injectedAttributes = scheme.getAttributes(EditorColors.INJECTED_LANGUAGE_FRAGMENT);
if (injectedFiles.isEmpty()) return true;
final InjectedLanguageManager injectedLanguageManager = InjectedLanguageManager.getInstance(myProject);
return JobUtil.invokeConcurrentlyUnderMyProgress(new ArrayList<PsiFile>(injectedFiles), new Processor<PsiFile>() {
public boolean process(final PsiFile injectedPsi) {
DocumentWindow documentWindow = (DocumentWindow)PsiDocumentManager.getInstance(myProject).getCachedDocument(injectedPsi);
Place places = InjectedLanguageUtil.getShreds(injectedPsi);
for (PsiLanguageInjectionHost.Shred place : places) {
TextRange textRange = place.getRangeInsideHost().shiftRight(place.host.getTextRange().getStartOffset());
if (textRange.isEmpty()) continue;
String desc = injectedPsi.getLanguage().getDisplayName() + ": " + injectedPsi.getText();
HighlightInfo info = HighlightInfo.createHighlightInfo(HighlightInfoType.INJECTED_LANGUAGE_FRAGMENT, textRange, null, desc, injectedAttributes);
infos.add(info);
}
HighlightInfoHolder holder = createInfoHolder(injectedPsi);
runHighlightVisitosForInjected(injectedPsi, holder, progress);
for (int i=0; i<holder.size();i++) {
HighlightInfo info = holder.get(i);
final int startOffset = documentWindow.injectedToHost(info.startOffset);
final TextRange fixedTextRange = getFixedTextRange(documentWindow, startOffset);
addPatchedInfos(info, injectedPsi, documentWindow, injectedLanguageManager, fixedTextRange);
addPatchedInfos(info, injectedPsi, documentWindow, injectedLanguageManager, fixedTextRange, infos);
}
holder.clear();
highlightInjectedSyntax(injectedPsi, holder);
@@ -235,20 +333,21 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
final int startOffset = info.startOffset;
final TextRange fixedTextRange = getFixedTextRange(documentWindow, startOffset);
if (fixedTextRange == null) {
addHighlightInfo(new TextRange(info.startOffset, info.endOffset), info);
infos.add(info);
}
else {
HighlightInfo patched =
new HighlightInfo(info.forcedTextAttributes, info.type, fixedTextRange.getStartOffset(), fixedTextRange.getEndOffset(),
info.description, info.toolTip, info.type.getSeverity(null), info.isAfterEndOfLine, null, false);
addHighlightInfo(fixedTextRange, patched);
infos.add(patched);
}
}
if (!isDumbMode()) {
Collection<HighlightInfo> todos = highlightTodos(injectedPsi, injectedPsi.getText(), 0, injectedPsi.getTextLength(), progress);
List<HighlightInfo> todos = new ArrayList<HighlightInfo>();
highlightTodos(injectedPsi, injectedPsi.getText(), 0, injectedPsi.getTextLength(), progress, myPriorityRange, todos, todos);
for (HighlightInfo info : todos) {
addPatchedInfos(info, injectedPsi, documentWindow, injectedLanguageManager, null);
addPatchedInfos(info, injectedPsi, documentWindow, injectedLanguageManager, null, infos);
}
}
return true;
@@ -272,8 +371,12 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
return fixedTextRange;
}
private void addPatchedInfos(HighlightInfo info, PsiFile injectedPsi, DocumentWindow documentWindow, InjectedLanguageManager injectedLanguageManager,
TextRange fixedTextRange) {
private static void addPatchedInfos(HighlightInfo info,
PsiFile injectedPsi,
DocumentWindow documentWindow,
InjectedLanguageManager injectedLanguageManager,
TextRange fixedTextRange,
Collection<HighlightInfo> out) {
ProperTextRange textRange = new ProperTextRange(info.startOffset, info.endOffset);
List<TextRange> editables = injectedLanguageManager.intersectWithAllEditableFragments(injectedPsi, textRange);
for (TextRange editable : editables) {
@@ -297,18 +400,7 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
}
}
}
addHighlightInfo(hostRange, patched);
}
}
private void addHighlightInfo(@NotNull TextRange textRange, @NotNull HighlightInfo highlightInfo) {
synchronized (myInjectedPsiHighlights) {
Collection<HighlightInfo> infos = myInjectedPsiHighlights.get(textRange);
if (infos == null) {
infos = new SmartList<HighlightInfo>();
myInjectedPsiHighlights.put(textRange, infos);
}
infos.add(highlightInfo);
out.add(patched);
}
}
@@ -393,14 +485,7 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
protected void applyInformationWithProgress() {
myFile.putUserData(HAS_ERROR_ELEMENT, myHasErrorElement);
ProperTextRange range = new ProperTextRange(myStartOffset, myEndOffset);
Collection<HighlightInfo> infos = myInjectedPsiHighlights.get(range);
if (infos == null) {
infos = new ArrayList<HighlightInfo>(myHighlights.size());
myInjectedPsiHighlights.put(range, infos);
}
infos.addAll(myHighlights);
UpdateHighlightersUtil.setHighlightersToEditor(myProject, myDocument, myInjectedPsiHighlights, Pass.UPDATE_ALL);
myApplyCommand.run();
if (myUpdateAll) {
reportErrorsToWolf();
@@ -409,27 +494,24 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
@NotNull
Collection<HighlightInfo> getHighlights() {
ArrayList<HighlightInfo> list = new ArrayList<HighlightInfo>(myHighlights);
for (Collection<HighlightInfo> infos : myInjectedPsiHighlights.values()) {
list.addAll(infos);
}
return list;
return new ArrayList<HighlightInfo>(myHighlights);
}
private Collection<HighlightInfo> collectHighlights(@NotNull final List<PsiElement> elements, @NotNull final ProgressIndicator progress,
final HighlightVisitor[] visitors) {
private void collectHighlights(@NotNull final List<PsiElement> elements1,
@NotNull final Runnable after1,
@NotNull final List<PsiElement> elements2,
@NotNull final ProgressIndicator progress,
@NotNull final HighlightVisitor[] visitors,
@NotNull final Set<HighlightInfo> gotHighlights,
final boolean forceHighlightParents) {
final Set<PsiElement> skipParentsSet = new THashSet<PsiElement>();
final Set<HighlightInfo> gotHighlights = new THashSet<HighlightInfo>();
final boolean forceHighlightParents = forceHighlightParents();
final HighlightInfoHolder holder = createInfoHolder(myFile);
setProgressLimit((long)elements.size() * visitors.length);
final int chunkSize = Math.max(1, elements.size() / 100); // one percent precision is enough
for (final HighlightVisitor visitor : visitors) {
Runnable action = new Runnable() {
public void run() {
final int chunkSize = Math.max(1, (elements1.size()+elements2.size()) / 100); // one percent precision is enough
final Runnable action = new Runnable() {
public void run() {
for (List<PsiElement> elements : new List[]{elements1, elements2}) {
int nextLimit = chunkSize;
for (int i = 0; i < elements.size(); i++) {
PsiElement element = elements.get(i);
@@ -445,7 +527,10 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
}
holder.clear();
visitor.visit(element, holder);
for (final HighlightVisitor visitor : visitors) {
visitor.visit(element, holder);
}
if (i == nextLimit) {
advanceProgress(chunkSize);
nextLimit = i + chunkSize;
@@ -466,21 +551,35 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
}
UIUtil.invokeLaterIfNeeded(new Runnable() {
public void run() {
if (myProject.isDisposed()) return;
if (progress.isCanceled()) return;
UpdateHighlightersUtil.addHighlighterToEditorIncrementally(myProject, myDocument, myFile, 0, myDocument.getTextLength(), info, Pass.UPDATE_ALL);
}
});
}
}
advanceProgress(elements.size() - (nextLimit-chunkSize));
if (elements == elements1) after1.run();
}
};
if (!visitor.analyze(action, myUpdateAll, myFile)) {
}
};
analyzeByVisitors(progress, visitors, action, 0);
}
private void analyzeByVisitors(final ProgressIndicator progress, final HighlightVisitor[] visitors, final Runnable action, final int i) {
if (i == visitors.length) {
action.run();
}
else {
if (!visitors[i].analyze(new Runnable() {
@Override
public void run() {
analyzeByVisitors(progress, visitors, action, i+1);
}
}, myUpdateAll, myFile)) {
cancelAndRestartDaemonLater(progress, myProject, this);
}
}
return gotHighlights;
}
private static HighlightVisitor[] filterVisitors(HighlightVisitor[] highlightVisitors, final PsiFile file) {
@@ -529,17 +628,19 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
return new HighlightInfoHolder(file, filters);
}
private static Collection<HighlightInfo> highlightTodos(@NotNull PsiFile file,
@NotNull CharSequence text,
int startOffset,
int endOffset,
@NotNull ProgressIndicator progress) {
private static void highlightTodos(@NotNull PsiFile file,
@NotNull CharSequence text,
int startOffset,
int endOffset,
@NotNull ProgressIndicator progress,
@NotNull ProperTextRange priorityRange,
@NotNull Collection<HighlightInfo> result,
@NotNull Collection<HighlightInfo> outsideResult) {
PsiManager psiManager = file.getManager();
PsiSearchHelper helper = psiManager.getSearchHelper();
TodoItem[] todoItems = helper.findTodoItems(file, startOffset, endOffset);
if (todoItems.length == 0) return Collections.emptyList();
if (todoItems.length == 0) return;
List<HighlightInfo> list = new ArrayList<HighlightInfo>(todoItems.length);
for (TodoItem todoItem : todoItems) {
progress.checkCanceled();
TextRange range = todoItem.getTextRange();
@@ -547,9 +648,13 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
TextAttributes attributes = todoItem.getPattern().getAttributes().getTextAttributes();
HighlightInfo info = HighlightInfo.createHighlightInfo(HighlightInfoType.TODO, range, description, description, attributes);
assert info != null;
list.add(info);
if (priorityRange.containsRange(info.getStartOffset(), info.getEndOffset())) {
result.add(info);
}
else {
outsideResult.add(info);
}
}
return list;
}
private void reportErrorsToWolf() {
@@ -576,7 +681,8 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
return myUpdateAll ? super.getProgress() : -1;
}
private static List<Problem> convertToProblems(final Collection<HighlightInfo> infos, final VirtualFile file,
private static List<Problem> convertToProblems(final Collection<HighlightInfo> infos,
final VirtualFile file,
final boolean hasErrorElement) {
List<Problem> problems = new SmartList<Problem>();
for (HighlightInfo info : infos) {
@@ -23,6 +23,7 @@ import com.intellij.codeHighlighting.TextEditorHighlightingPassRegistrar;
import com.intellij.openapi.components.AbstractProjectComponent;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.ProperTextRange;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NonNls;
@@ -51,7 +52,7 @@ public class GeneralHighlightingPassFactory extends AbstractProjectComponent imp
TextRange textRange = FileStatusMap.getDirtyTextRange(editor, Pass.UPDATE_ALL);
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);
ProperTextRange visibleRange = VisibleHighlightingPassFactory.calculateVisibleRange(editor);
return new GeneralHighlightingPass(myProject, file, editor.getDocument(), textRange.getStartOffset(), textRange.getEndOffset(), true, visibleRange, editor);
}
}
@@ -38,10 +38,7 @@ import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.TextEditor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.*;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
@@ -94,14 +91,8 @@ public class UpdateHighlightersUtil {
return o1.getStartOffset() - o2.getStartOffset();
}
};
private static final Comparator<TextRange> BY_START_OFFSET_OR_CONTAINS = new Comparator<TextRange>() {
public int compare(final TextRange o1, final TextRange o2) {
if (o1.contains(o2) || o2.contains(o1)) return 0;
return o1.getStartOffset() - o2.getStartOffset();
}
};
private static void cleanFileLevelHighlights(@NotNull Project project, final int group, PsiFile psiFile) {
public static void cleanFileLevelHighlights(@NotNull Project project, final int group, PsiFile psiFile) {
if (psiFile == null || !psiFile.getViewProvider().isPhysical()) return;
VirtualFile vFile = psiFile.getViewProvider().getVirtualFile();
final FileEditorManager manager = FileEditorManager.getInstance(project);
@@ -191,11 +182,10 @@ public class UpdateHighlightersUtil {
return;
}
boolean success = createOrReuseHighlighterFor(info, document, group, file, (MarkupModelEx)markup, null, null, startOffset, endOffset,
SeverityRegistrar.getInstance(project));
if (!success) {
return;
}
if (info.getStartOffset() < startOffset || info.getEndOffset() > endOffset) return;
createOrReuseHighlighterFor(info, document, group, file, (MarkupModelEx)markup, null, null,
SeverityRegistrar.getInstance(project));
DaemonCodeAnalyzerImpl.addHighlight(markup, project, info);
clearWhiteSpaceOptimizationFlag(document);
@@ -241,7 +231,24 @@ public class UpdateHighlightersUtil {
}
}
private static void setHighlightersInRange(final TextRange range,
// set highlights inside startOffset,endOffset but outside range
public static void setHighlightersToEditorOutsideRange(@NotNull Project project,
@NotNull Document document,
@NotNull Collection<HighlightInfo> infos,
int startOffset, int endOffset, @NotNull ProperTextRange range,
final int group) {
ApplicationManager.getApplication().assertIsDispatchThread();
PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document);
cleanFileLevelHighlights(project, group, psiFile);
MarkupModel markup = document.getMarkupModel(project);
assertMarkupConsistent(markup, project);
setHighlightersOutsideRange(startOffset, endOffset, range, infos, (MarkupModelEx)markup, group, document, project);
}
public static void setHighlightersInRange(final TextRange range,
Collection<HighlightInfo> highlightsCo,
final MarkupModelEx markup,
final int group,
@@ -293,10 +300,88 @@ public class UpdateHighlightersUtil {
if (isWarningCoveredByError(info, overlappingIntervals, severityRegistrar)) {
return true;
}
boolean success = createOrReuseHighlighterFor(info, document, group, psiFile, markup, infosToRemove,
ranges2markersCache, range.getStartOffset(), range.getEndOffset(),
if (info.getStartOffset() >= range.getStartOffset() && info.getEndOffset() <= range.getEndOffset()) {
createOrReuseHighlighterFor(info, document, group, psiFile, markup, infosToRemove,
ranges2markersCache,
severityRegistrar);
changed[0] = true;
}
return true;
}
});
for (RangeHighlighter highlighter : infosToRemove.forAllInGarbageBin()) {
markup.removeHighlighter(highlighter);
changed[0] = true;
}
if (changed[0]) {
clearWhiteSpaceOptimizationFlag(document);
}
assertMarkupConsistent(markup, project);
}
private static void setHighlightersOutsideRange(final int startOffset, final int endOffset, final TextRange range,
Collection<HighlightInfo> highlightsCo,
final MarkupModelEx markup,
final int group,
final Document document,
final Project project) {
final List<HighlightInfo> highlights = new ArrayList<HighlightInfo>(highlightsCo);
final SeverityRegistrar severityRegistrar = SeverityRegistrar.getInstance(project);
final HighlightersRecycler infosToRemove = new HighlightersRecycler();
DaemonCodeAnalyzerImpl.processHighlights(document, project, null, startOffset, endOffset, new Processor<HighlightInfo>() {
@Override
public boolean process(HighlightInfo info) {
if (info.group == group) {
RangeHighlighter highlighter = info.highlighter;
int endOffset = highlighter.getEndOffset();
int startOffset = highlighter.getStartOffset();
boolean willBeRemoved = endOffset == document.getTextLength() && range.getEndOffset() != document.getTextLength()
|| !range.contains(startOffset)
&& !range.containsRange(startOffset, endOffset);
if (willBeRemoved) {
infosToRemove.recycleHighlighter(highlighter);
info.highlighter = null;
}
}
return true;
}
});
Collections.sort(highlights, BY_START_OFFSET_NODUPS);
final Map<TextRange, RangeMarker> ranges2markersCache = new THashMap<TextRange, RangeMarker>(10);
final PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document);
final boolean[] changed = {false};
RangeMarkerTree.sweep(new RangeMarkerTree.Generator<HighlightInfo>(){
@Override
public boolean generate(final Processor<HighlightInfo> processor) {
return ContainerUtil.process(highlights, new Processor<HighlightInfo>() {
@Override
public boolean process(HighlightInfo info) {
return info.getStartOffset() < startOffset || info.getEndOffset() > endOffset || processor.process(info);
}
});
}
}, new MarkupModelEx.SweepProcessor<HighlightInfo>() {
@Override
public boolean process(int offset, HighlightInfo info, boolean atStart, Collection<HighlightInfo> overlappingIntervals) {
if (!atStart) {
return true;
}
if (info.isFileLevelAnnotation && psiFile != null && psiFile.getViewProvider().isPhysical()) {
addFileLevelHighlight(project, group, info, psiFile);
changed[0] = true;
return true;
}
if (isWarningCoveredByError(info, overlappingIntervals, severityRegistrar)) {
return true;
}
if (new TextRange(startOffset,endOffset).containsRange(info.getStartOffset(), info.getEndOffset()) &&
(info.getStartOffset() < range.getStartOffset() || info.getEndOffset() > range.getEndOffset())) {
createOrReuseHighlighterFor(info, document, group, psiFile, markup, infosToRemove,
ranges2markersCache,
severityRegistrar);
if (success) {
changed[0] = true;
}
return true;
@@ -332,19 +417,16 @@ public class UpdateHighlightersUtil {
}
// return true if changed
private static boolean createOrReuseHighlighterFor(@NotNull final HighlightInfo info,
@NotNull final Document document,
final int group,
@NotNull final PsiFile psiFile,
@NotNull MarkupModelEx markup,
@Nullable HighlightersRecycler infosToRemove,
@Nullable final Map<TextRange, RangeMarker> ranges2markersCache,
int rangeStartOffset,
int rangeEndOffset,
SeverityRegistrar severityRegistrar) {
private static void createOrReuseHighlighterFor(@NotNull final HighlightInfo info,
@NotNull final Document document,
final int group,
@NotNull final PsiFile psiFile,
@NotNull MarkupModelEx markup,
@Nullable HighlightersRecycler infosToRemove,
@Nullable final Map<TextRange, RangeMarker> ranges2markersCache,
SeverityRegistrar severityRegistrar) {
final int infoStartOffset = info.startOffset;
int infoEndOffset = info.endOffset;
if (infoStartOffset < rangeStartOffset || infoEndOffset > rangeEndOffset) return false;
if (infoEndOffset == infoStartOffset && !info.isAfterEndOfLine) {
infoEndOffset++; //show something in case of empty highlightinfo
@@ -398,7 +480,6 @@ public class UpdateHighlightersUtil {
info.getTextAttributes(psiFile) +
"; highlighter:" +
highlighter.getTextAttributes();
return true;
}
@NotNull
@@ -24,6 +24,7 @@ import com.intellij.openapi.components.AbstractProjectComponent;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.LogicalPosition;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.ProperTextRange;
import com.intellij.openapi.util.TextRange;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -36,7 +37,7 @@ public abstract class VisibleHighlightingPassFactory extends AbstractProjectComp
}
@NotNull
public static TextRange calculateVisibleRange(@NotNull Editor editor) {
public static ProperTextRange calculateVisibleRange(@NotNull Editor editor) {
Rectangle rect = editor.getScrollingModel().getVisibleArea();
LogicalPosition startPosition = editor.xyToLogicalPosition(new Point(rect.x, rect.y));
@@ -45,7 +46,7 @@ public abstract class VisibleHighlightingPassFactory extends AbstractProjectComp
int visibleEnd = editor.logicalPositionToOffset(new LogicalPosition(endPosition.line + 1, 0));
return new TextRange(visibleStart, visibleEnd);
return new ProperTextRange(visibleStart, visibleEnd);
}
@Nullable