diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/InstructionFactory.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/InstructionFactory.java deleted file mode 100644 index dd618ae606e7..000000000000 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/InstructionFactory.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2000-2011 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.codeInspection.dataFlow; - -/** - * User: cdr - */ -public class InstructionFactory { -} diff --git a/java/java-impl/src/com/intellij/psi/impl/smartPointers/AnchorElementInfo.java b/java/java-impl/src/com/intellij/psi/impl/smartPointers/AnchorElementInfo.java index e0f4bbe04e62..f5ce0aa2145c 100644 --- a/java/java-impl/src/com/intellij/psi/impl/smartPointers/AnchorElementInfo.java +++ b/java/java-impl/src/com/intellij/psi/impl/smartPointers/AnchorElementInfo.java @@ -15,6 +15,8 @@ */ package com.intellij.psi.impl.smartPointers; +import com.intellij.openapi.editor.RangeMarker; +import com.intellij.openapi.util.ProperTextRange; import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; import com.intellij.psi.impl.source.PsiFileImpl; @@ -35,14 +37,15 @@ class AnchorElementInfo extends SelfElementInfo { private IStubElementType myStubElementType; AnchorElementInfo(@NotNull PsiElement anchor, @NotNull PsiFile containingFile) { - super(containingFile.getProject(), anchor.getTextRange(), anchor.getClass(), containingFile, containingFile.getLanguage()); + super(containingFile.getProject(), ProperTextRange.create(anchor.getTextRange()), anchor.getClass(), containingFile, + containingFile.getLanguage()); } // will restore by stub index until file tree get loaded AnchorElementInfo(@NotNull PsiElement anchor, @NotNull PsiFileWithStubSupport containingFile, int stubId, @NotNull IStubElementType stubElementType) { - super(containingFile.getProject(), new TextRange(0,0), anchor.getClass(), containingFile, containingFile.getLanguage()); + super(containingFile.getProject(), new ProperTextRange(0,0), anchor.getClass(), containingFile, containingFile.getLanguage()); this.stubId = stubId; myStubElementType = stubElementType; IElementType contentElementType = ((PsiFileImpl)containingFile).getContentElementType(); @@ -83,7 +86,7 @@ class AnchorElementInfo extends SelfElementInfo { } @Override - public void fastenBelt(int offset) { + public void fastenBelt(int offset, RangeMarker cachedRangeMarker) { if (stubId != -1) { PsiElement element = restoreElement(); if (element != null) { @@ -94,6 +97,6 @@ class AnchorElementInfo extends SelfElementInfo { setRange((anchor == null ? element : anchor).getTextRange()); } } - super.fastenBelt(offset); + super.fastenBelt(offset, cachedRangeMarker); } } diff --git a/java/java-impl/src/com/intellij/psi/impl/smartPointers/ImplicitVariableElementInfoFactory.java b/java/java-impl/src/com/intellij/psi/impl/smartPointers/ImplicitVariableElementInfoFactory.java index 53888a5a2b69..b2911b90ab07 100644 --- a/java/java-impl/src/com/intellij/psi/impl/smartPointers/ImplicitVariableElementInfoFactory.java +++ b/java/java-impl/src/com/intellij/psi/impl/smartPointers/ImplicitVariableElementInfoFactory.java @@ -16,6 +16,7 @@ package com.intellij.psi.impl.smartPointers; import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Segment; @@ -51,10 +52,6 @@ public class ImplicitVariableElementInfoFactory implements SmartPointerElementIn return null; } - @Override - public void dispose() { - } - @Nullable public Document getDocumentToSynchronize() { return null; @@ -64,7 +61,7 @@ public class ImplicitVariableElementInfoFactory implements SmartPointerElementIn } @Override - public void fastenBelt(int offset) { + public void fastenBelt(int offset, RangeMarker cachedRangeMarker) { } @Override diff --git a/java/java-impl/src/com/intellij/refactoring/introduceVariable/VariableInplaceIntroducer.java b/java/java-impl/src/com/intellij/refactoring/introduceVariable/VariableInplaceIntroducer.java index 384e074bb510..133cec7d944b 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceVariable/VariableInplaceIntroducer.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceVariable/VariableInplaceIntroducer.java @@ -68,7 +68,7 @@ public class VariableInplaceIntroducer extends AbstractInplaceVariableIntroducer public VariableInplaceIntroducer(final Project project, final TypeExpression expression, final Editor editor, - final @NotNull PsiVariable elementToRename, + @NotNull final PsiVariable elementToRename, final boolean cantChangeFinalModifier, final boolean hasTypeSuggestion, final RangeMarker exprMarker, @@ -113,7 +113,7 @@ public class VariableInplaceIntroducer extends AbstractInplaceVariableIntroducer try { if (success) { final Document document = myEditor.getDocument(); - final @Nullable PsiVariable psiVariable = getVariable(); + @Nullable final PsiVariable psiVariable = getVariable(); if (psiVariable == null) { return; } @@ -145,9 +145,11 @@ public class VariableInplaceIntroducer extends AbstractInplaceVariableIntroducer } }); } - } else { - if (getExprMarker() != null) { - myEditor.getCaretModel().moveToOffset(getExprMarker().getStartOffset()); + } + else { + RangeMarker exprMarker = getExprMarker(); + if (exprMarker != null && exprMarker.isValid()) { + myEditor.getCaretModel().moveToOffset(exprMarker.getStartOffset()); myEditor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE); } if (myExpressionText != null) { diff --git a/platform/lang-api/src/com/intellij/execution/filters/CompositeFilter.java b/platform/lang-api/src/com/intellij/execution/filters/CompositeFilter.java index 8812aea617f9..67bfb51c47ed 100644 --- a/platform/lang-api/src/com/intellij/execution/filters/CompositeFilter.java +++ b/platform/lang-api/src/com/intellij/execution/filters/CompositeFilter.java @@ -24,7 +24,7 @@ import java.util.List; public class CompositeFilter implements Filter { private final List myFilters = new ArrayList(); - private DumbService myDumbService; + private final DumbService myDumbService; public CompositeFilter(Project project) { myDumbService = DumbService.getInstance(project); @@ -35,6 +35,7 @@ public class CompositeFilter implements Filter { final boolean dumb = myDumbService.isDumb(); List filters = myFilters; int count = filters.size(); + //noinspection ForLoopReplaceableByForEach for (int i = 0; i < count; i++) { Filter filter = filters.get(i); if (!dumb || DumbService.isDumbAware(filter)) { diff --git a/platform/lang-api/src/com/intellij/execution/filters/OpenFileHyperlinkInfo.java b/platform/lang-api/src/com/intellij/execution/filters/OpenFileHyperlinkInfo.java index beac313f0fbd..e517ba806445 100644 --- a/platform/lang-api/src/com/intellij/execution/filters/OpenFileHyperlinkInfo.java +++ b/platform/lang-api/src/com/intellij/execution/filters/OpenFileHyperlinkInfo.java @@ -23,14 +23,20 @@ import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; public final class OpenFileHyperlinkInfo implements FileHyperlinkInfo { - private final OpenFileDescriptor myDescriptor; + private final Project myProject; + private final VirtualFile myFile; + private final int myLine; + private final int myColumn; public OpenFileHyperlinkInfo(@NotNull OpenFileDescriptor descriptor) { - myDescriptor = descriptor; + this(descriptor.getProject(), descriptor.getFile(), descriptor.getLine(), descriptor.getColumn()); } public OpenFileHyperlinkInfo(Project project, @NotNull final VirtualFile file, final int line, final int column) { - this (new OpenFileDescriptor(project, file, line, column)); + myProject = project; + myFile = file; + myLine = line; + myColumn = column; } public OpenFileHyperlinkInfo(Project project, @NotNull final VirtualFile file, final int line) { @@ -38,15 +44,15 @@ public final class OpenFileHyperlinkInfo implements FileHyperlinkInfo { } public OpenFileDescriptor getDescriptor() { - return myDescriptor; + return new OpenFileDescriptor(myProject, myFile, myLine, myColumn); } public void navigate(final Project project) { ApplicationManager.getApplication().runReadAction(new Runnable() { public void run() { - final VirtualFile file = myDescriptor.getFile(); + final VirtualFile file = myFile; if(file.isValid()) { - FileEditorManager.getInstance(project).openTextEditor(myDescriptor, true); + FileEditorManager.getInstance(project).openTextEditor(getDescriptor(), true); } } }); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonCodeAnalyzerImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonCodeAnalyzerImpl.java index 1b1441d41e31..11f8238b2e32 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonCodeAnalyzerImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonCodeAnalyzerImpl.java @@ -35,6 +35,7 @@ import com.intellij.openapi.components.ProjectComponent; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.editor.ex.EditorMarkupModel; import com.intellij.openapi.editor.ex.MarkupModelEx; import com.intellij.openapi.editor.ex.RangeHighlighterEx; @@ -557,10 +558,13 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzer implements JDOMEx int endOffset = highlighter.getEndOffset(); if (startOffset > offset || offset > endOffset) { if (!includeFixRange) return false; - if (info.fixMarker == null || !info.fixMarker.isValid()) return false; - startOffset = info.fixMarker.getStartOffset(); - endOffset = info.fixMarker.getEndOffset(); - if (startOffset > offset || offset > endOffset) return false; + RangeMarker fixMarker = info.fixMarker; + if (fixMarker != null) { // null means its range is the same as highlighter + if (!fixMarker.isValid()) return false; + startOffset = fixMarker.getStartOffset(); + endOffset = fixMarker.getEndOffset(); + if (startOffset > offset || offset > endOffset) return false; + } } return true; } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/FileStatusMap.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/FileStatusMap.java index 6dfdd8caacf1..18e2dfe0186d 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/FileStatusMap.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/FileStatusMap.java @@ -28,12 +28,14 @@ import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Key; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import com.intellij.util.containers.WeakHashMap; import gnu.trove.TIntObjectHashMap; import gnu.trove.TIntObjectProcedure; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; @@ -74,7 +76,7 @@ public class FileStatusMap implements Disposable { if (!errorFound) return; PsiFile file = PsiDocumentManager.getInstance(myProject).getPsiFile(document); assert file != null : document; - status = new FileStatus(file,document); + status = new FileStatus(file.getProject()); myDocumentToStatusMap.put(document, status); } status.errorFound = errorFound; @@ -90,29 +92,25 @@ public class FileStatusMap implements Disposable { private static class FileStatus { public boolean defensivelyMarked; // file marked dirty without knowledge of specific dirty region. Subsequent markScopeDirty can refine dirty scope, not extend it - private boolean wolfPassFinfished; + private boolean wolfPassFinished; + // if contains the special value "WHOLE_FILE_MARKER" than the corresponding range is (0, document length) private final TIntObjectHashMap dirtyScopes = new TIntObjectHashMap(); private boolean errorFound; - private FileStatus(@NotNull PsiFile file, @NotNull Document document) { - markWholeFile(file, document, file.getProject()); + private FileStatus(@NotNull Project project) { + markWholeFile(project); } - private void markWholeFile(PsiFile file, Document document, Project project) { - dirtyScopes.put(Pass.UPDATE_ALL, createWholeFileMarker(file, document)); - dirtyScopes.put(Pass.EXTERNAL_TOOLS, createWholeFileMarker(file, document)); - dirtyScopes.put(Pass.LOCAL_INSPECTIONS, createWholeFileMarker(file, document)); + private void markWholeFile(@NotNull Project project) { + dirtyScopes.put(Pass.UPDATE_ALL, WHOLE_FILE_MARKER); + dirtyScopes.put(Pass.EXTERNAL_TOOLS, WHOLE_FILE_MARKER); + dirtyScopes.put(Pass.LOCAL_INSPECTIONS, WHOLE_FILE_MARKER); TextEditorHighlightingPassRegistrarImpl registrar = (TextEditorHighlightingPassRegistrarImpl) TextEditorHighlightingPassRegistrar.getInstance(project); for(DirtyScopeTrackingHighlightingPassFactory factory: registrar.getDirtyScopeTrackingFactories()) { - dirtyScopes.put(factory.getPassId(), createWholeFileMarker(file, document)); + dirtyScopes.put(factory.getPassId(), WHOLE_FILE_MARKER); } } - private static RangeMarker createWholeFileMarker(PsiFile file, Document document) { - int length = file == null ? -1 : Math.min(file.getTextLength(), document.getTextLength()); - return length == -1 ? null : document.createRangeMarker(0, length); - } - public boolean allDirtyScopesAreNull() { for (Object o : dirtyScopes.getValues()) { if (o != null) return false; @@ -134,14 +132,14 @@ public class FileStatusMap implements Disposable { @Override public String toString() { - final StringBuilder s = new StringBuilder(); - s.append("defensivelyMarked = " + defensivelyMarked); - s.append("; wolfPassFinfished = " + wolfPassFinfished); - s.append("; errorFound = " + errorFound); + @NonNls final StringBuilder s = new StringBuilder(); + s.append("defensivelyMarked = ").append(defensivelyMarked); + s.append("; wolfPassFinfished = ").append(wolfPassFinished); + s.append("; errorFound = ").append(errorFound); s.append("; dirtyScopes: ("); dirtyScopes.forEachEntry(new TIntObjectProcedure() { public boolean execute(int passId, RangeMarker rangeMarker) { - s.append(" pass: " + passId + " -> " + rangeMarker + ";"); + s.append(" pass: " + passId + " -> " + (rangeMarker == WHOLE_FILE_MARKER ? "Whole file" : rangeMarker)+ ";"); return true; } }); @@ -171,17 +169,17 @@ public class FileStatusMap implements Disposable { synchronized(myDocumentToStatusMap){ FileStatus status = myDocumentToStatusMap.get(document); if (status == null){ - status = new FileStatus(file,document); + status = new FileStatus(file.getProject()); myDocumentToStatusMap.put(document, status); } status.defensivelyMarked=false; if (passId == Pass.WOLF) { - status.wolfPassFinfished = true; + status.wolfPassFinished = true; } else if (status.dirtyScopes.containsKey(passId)) { RangeMarker marker = status.dirtyScopes.get(passId); if (marker != null) { - marker.dispose(); + if (marker != WHOLE_FILE_MARKER) marker.dispose(); status.dirtyScopes.put(passId, null); } } @@ -203,13 +201,12 @@ public class FileStatusMap implements Disposable { return file == null ? null : file.getTextRange(); } if (status.defensivelyMarked) { - //PsiFile file = PsiDocumentManager.getInstance(myProject).getPsiFile(document); - status.markWholeFile(file, document, myProject); + status.markWholeFile(myProject); status.defensivelyMarked = false; } LOG.assertTrue(status.dirtyScopes.containsKey(passId), "Unknown pass " + passId); RangeMarker marker = status.dirtyScopes.get(passId); - return marker == null ? null : marker.isValid() ? TextRange.create(marker) : new TextRange(0, document.getTextLength()); + return marker == null ? null : marker != WHOLE_FILE_MARKER && marker.isValid() ? TextRange.create(marker) : new TextRange(0, document.getTextLength()); } } @@ -221,15 +218,15 @@ public class FileStatusMap implements Disposable { return; } if (passId == Pass.WOLF) { - status.wolfPassFinfished = false; + status.wolfPassFinished = false; } else { LOG.assertTrue(status.dirtyScopes.containsKey(passId)); RangeMarker marker = status.dirtyScopes.get(passId); - if (marker != null) { + if (marker != null && marker != WHOLE_FILE_MARKER) { marker.dispose(); } - marker = document.createRangeMarker(0, document.getTextLength()); + marker = WHOLE_FILE_MARKER; status.dirtyScopes.put(passId, marker); } } @@ -269,8 +266,10 @@ public class FileStatusMap implements Disposable { private static RangeMarker combineScopes(RangeMarker old, TextRange scope, int textLength, @NotNull Document document) { if (scope == null) return old; if (old == null) { + if (scope.equalsToRange(0, textLength)) return WHOLE_FILE_MARKER; return document.createRangeMarker(scope); } + if (old == WHOLE_FILE_MARKER) return old; TextRange oldRange = TextRange.create(old); TextRange union = scope.union(oldRange); if (old.isValid() && union.equals(oldRange)) { @@ -280,6 +279,7 @@ public class FileStatusMap implements Disposable { union = union.intersection(new TextRange(0, textLength)); } old.dispose(); + assert union != null; return document.createRangeMarker(union); } @@ -289,7 +289,7 @@ public class FileStatusMap implements Disposable { if (!ProblemHighlightFilter.shouldHighlightFile(file)) return true; FileStatus status = myDocumentToStatusMap.get(document); - return status != null && !status.defensivelyMarked && status.wolfPassFinfished && status.allDirtyScopesAreNull(); + return status != null && !status.defensivelyMarked && status.wolfPassFinished && status.allDirtyScopesAreNull(); } } @@ -297,7 +297,7 @@ public class FileStatusMap implements Disposable { public void assertAllDirtyScopesAreNull(@NotNull Document document) { synchronized (myDocumentToStatusMap) { FileStatus status = myDocumentToStatusMap.get(document); - assert status != null && !status.defensivelyMarked && status.wolfPassFinfished && status.allDirtyScopesAreNull() : status; + assert status != null && !status.defensivelyMarked && status.wolfPassFinished && status.allDirtyScopesAreNull() : status; } } @@ -305,4 +305,63 @@ public class FileStatusMap implements Disposable { public void allowDirt(boolean allow) { myAllowDirt = allow; } + + private static final RangeMarker WHOLE_FILE_MARKER = new RangeMarker(){ + @NotNull + @Override + public Document getDocument() { + throw new UnsupportedOperationException(); + } + + @Override + public int getStartOffset() { + throw new UnsupportedOperationException(); + } + + @Override + public int getEndOffset() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isValid() { + throw new UnsupportedOperationException(); + } + + @Override + public void setGreedyToLeft(boolean greedy) { + throw new UnsupportedOperationException(); + } + + @Override + public void setGreedyToRight(boolean greedy) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isGreedyToRight() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isGreedyToLeft() { + throw new UnsupportedOperationException(); + } + + @Override + public void dispose() { + throw new UnsupportedOperationException(); + } + + @Override + public T getUserData(@NotNull Key key) { + throw new UnsupportedOperationException(); + } + + @Override + public void putUserData(@NotNull Key key, @Nullable T value) { + throw new UnsupportedOperationException(); + } + }; + } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/GeneralHighlightingPass.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/GeneralHighlightingPass.java index 50b973f19aeb..21de102f68f5 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/GeneralHighlightingPass.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/GeneralHighlightingPass.java @@ -63,6 +63,7 @@ import com.intellij.psi.tree.IElementType; import com.intellij.util.Processor; import com.intellij.util.SmartList; import com.intellij.util.ui.UIUtil; +import gnu.trove.THashMap; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -545,6 +546,7 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP return new ArrayList(myHighlights); } + private final Map ranges2markersCache = new THashMap(); private void collectHighlights(@NotNull final List elements1, @NotNull final Runnable after1, @NotNull final List elements2, @@ -606,7 +608,7 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP final EditorColorsScheme colorsScheme = getColorsScheme(); UpdateHighlightersUtil.addHighlighterToEditorIncrementally(myProject, myDocument, myFile, 0, myDocument.getTextLength(), - info, colorsScheme, Pass.UPDATE_ALL); + info, colorsScheme, Pass.UPDATE_ALL, ranges2markersCache); } }); } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/LocalInspectionsPass.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/LocalInspectionsPass.java index 9ffe578b47c5..c2c42eabd146 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/LocalInspectionsPass.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/LocalInspectionsPass.java @@ -34,6 +34,7 @@ import com.intellij.openapi.actionSystem.IdeActions; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.editor.colors.CodeInsightColors; import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.colors.TextAttributesKey; @@ -375,6 +376,7 @@ public class LocalInspectionsPass extends ProgressableTextEditorHighlightingPass severity, problemDescriptor.isAfterEndOfLine(), null, isFileLevel); } + private final Map ranges2markersCache = new THashMap(); private final TransferToEDTQueue> myTransferToEDTQueue = new TransferToEDTQueue>("Apply inspection results", new Processor>() { private final InspectionProfile inspectionProfile = InspectionProjectProfileManager.getInstance(myProject).getInspectionProfile(); @@ -402,7 +404,7 @@ public class LocalInspectionsPass extends ProgressableTextEditorHighlightingPass for (HighlightInfo info : infos) { final EditorColorsScheme colorsScheme = getColorsScheme(); UpdateHighlightersUtil.addHighlighterToEditorIncrementally(myProject, myDocument, myFile, myStartOffset, myEndOffset, - info, colorsScheme, getId()); + info, colorsScheme, getId(), ranges2markersCache); } return true; diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/UpdateHighlightersUtil.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/UpdateHighlightersUtil.java index 610eefb0dd07..e0255880a5d1 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/UpdateHighlightersUtil.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/UpdateHighlightersUtil.java @@ -159,7 +159,8 @@ public class UpdateHighlightersUtil { int endOffset, @NotNull final HighlightInfo info, @Nullable final EditorColorsScheme colorsScheme, // if null global scheme will be used - final int group) { + final int group, + @NotNull Map ranges2markersCache) { ApplicationManager.getApplication().assertIsDispatchThread(); if (info.isFileLevelAnnotation || info.getGutterIconRenderer() != null) return; @@ -177,7 +178,7 @@ public class UpdateHighlightersUtil { if (info.getStartOffset() < startOffset || info.getEndOffset() > endOffset) return; - createOrReuseHighlighterFor(info, colorsScheme, document, group, file, (MarkupModelEx)markup, null, null, + createOrReuseHighlighterFor(info, colorsScheme, document, group, file, (MarkupModelEx)markup, null, ranges2markersCache, SeverityRegistrar.getInstance(project)); clearWhiteSpaceOptimizationFlag(document); @@ -388,7 +389,7 @@ public class UpdateHighlightersUtil { @NotNull final PsiFile psiFile, @NotNull MarkupModelEx markup, @Nullable HighlightersRecycler infosToRemove, - @Nullable final Map ranges2markersCache, + @NotNull final Map ranges2markersCache, SeverityRegistrar severityRegistrar) { final int infoStartOffset = info.startOffset; int infoEndOffset = info.endOffset; @@ -424,7 +425,7 @@ public class UpdateHighlightersUtil { GutterIconRenderer renderer = info.getGutterIconRenderer(); finalHighlighter.setGutterIconRenderer(renderer); - if (ranges2markersCache != null) ranges2markersCache.put(new TextRange(infoStartOffset, finalInfoEndOffset), info.highlighter); + ranges2markersCache.put(new TextRange(infoStartOffset, finalInfoEndOffset), info.highlighter); if (info.quickFixActionRanges != null) { List> list = new ArrayList>(info.quickFixActionRanges.size()); @@ -435,7 +436,13 @@ public class UpdateHighlightersUtil { } info.quickFixActionMarkers = new CopyOnWriteArrayList>(list); } - info.fixMarker = getOrCreate(document, ranges2markersCache, new TextRange(info.fixStartOffset, info.fixEndOffset)); + TextRange fixRange = new TextRange(info.fixStartOffset, info.fixEndOffset); + if (fixRange.equalsToRange(infoStartOffset, finalInfoEndOffset)) { + info.fixMarker = null; // null means it the same as highlighter' + } + else { + info.fixMarker = getOrCreate(document, ranges2markersCache, fixRange); + } } }; @@ -469,13 +476,11 @@ public class UpdateHighlightersUtil { return layer; } - private static RangeMarker getOrCreate(@NotNull Document document, @Nullable Map ranges2markersCache, @NotNull TextRange textRange) { - RangeMarker marker = ranges2markersCache == null ? null : ranges2markersCache.get(textRange); + private static RangeMarker getOrCreate(@NotNull Document document, @NotNull Map ranges2markersCache, @NotNull TextRange textRange) { + RangeMarker marker = ranges2markersCache.get(textRange); if (marker == null) { marker = document.createRangeMarker(textRange); - if (ranges2markersCache != null) { - ranges2markersCache.put(textRange, marker); - } + ranges2markersCache.put(textRange, marker); } return marker; } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java index b6cfad896daf..01e9d0bbda87 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java @@ -783,6 +783,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable checkValid(); LOG.assertTrue(myLookupStartMarker.isValid(), "invalid lookup start"); LOG.assertTrue(marker.isValid(), "invalid marker"); + marker.dispose(); if (isVisible()) { updateLookupBounds(); } @@ -814,7 +815,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable updateScrollbarVisibility(); Point p = calculatePosition(getComponent()); - HintManagerImpl.getInstanceImpl().showEditorHint(this, myEditor, p, HintManagerImpl.HIDE_BY_ESCAPE | HintManagerImpl.UPDATE_BY_SCROLLING, 0, false, + HintManagerImpl.getInstanceImpl().showEditorHint(this, myEditor, p, HintManager.HIDE_BY_ESCAPE | HintManager.UPDATE_BY_SCROLLING, 0, false, HintManagerImpl.createHintHint(myEditor, p, this, HintManager.UNDER).setAwtTooltip(false)); LOG.assertTrue(isVisible(), "!visible, disposed=" + myDisposed); LOG.assertTrue(myList.isShowing(), "!showing, disposed=" + myDisposed); @@ -955,6 +956,12 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable ? myEditor.getSelectionModel().getSelectionStart() : myEditor.getCaretModel().getOffset(); int start = Math.max(offset - myMinPrefixLength - myAdditionalPrefix.length(), 0); + if (myLookupStartMarker != null) { + if (myLookupStartMarker.isValid() && myLookupStartMarker.getStartOffset() == start && myLookupStartMarker.getEndOffset() == start) { + return start; + } + myLookupStartMarker.dispose(); + } myLookupStartMarker = myEditor.getDocument().createRangeMarker(start, start); myLookupStartMarker.setGreedyToLeft(true); return start; diff --git a/platform/lang-impl/src/com/intellij/psi/impl/DocumentCommitThread.java b/platform/lang-impl/src/com/intellij/psi/impl/DocumentCommitThread.java index f9b94e6390d1..0577f33f3a77 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/DocumentCommitThread.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/DocumentCommitThread.java @@ -22,6 +22,7 @@ import com.intellij.openapi.application.ex.ApplicationManagerEx; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; @@ -125,18 +126,18 @@ public class DocumentCommitThread implements Runnable, Disposable { useIndicator(null); } - public boolean queueCommit(@NonNls String reason, @NotNull Document document, @NotNull Project project) { + public boolean queueCommit(@NonNls @NotNull String reason, @NotNull Document document, @NotNull Project project, @Nullable DocumentEvent event) { assert !isDisposed : "already disposed"; if (!project.isInitialized()) return false; PsiFile psiFile = PsiDocumentManager.getInstance(project).getCachedPsiFile(document); if (psiFile == null) return false; - doQueue(document, project, getCommitStage(document), reason); + doQueue(document, project, getCommitStage(document), reason, event); return true; } - private void doQueue(@NotNull Document document, @NotNull Project project, CommitStage start, @NonNls String reason) { + private void doQueue(@NotNull Document document, @NotNull Project project, CommitStage start, @NonNls @NotNull String reason, @Nullable DocumentEvent event) { synchronized (documentsToCommit) { if (!changeCommitStage(document, start, CommitStage.QUEUED_TO_COMMIT, false)) return; @@ -156,7 +157,7 @@ public class DocumentCommitThread implements Runnable, Disposable { } ProgressIndicator indicator = new ProgressIndicatorBase(); indicator.start(); - documentsToCommit.addLast(new CommitTask(document, project, indicator)); + documentsToCommit.addLast(new CommitTask(document, project, indicator, reason, event)); log("Queued", document, false, reason); } } @@ -204,13 +205,21 @@ public class DocumentCommitThread implements Runnable, Disposable { private final Document document; private final Project project; private final ProgressIndicator indicator; // progress to commit this doc under. + private final String reason; + @Nullable private final DocumentEvent event; // running = false means document was removed from the queue, should ignore. // canceled = true means commit was canceled, should reschedule for later. - private CommitTask(@NotNull Document document, @NotNull Project project, @NotNull ProgressIndicator indicator) { + private CommitTask(@NotNull Document document, + @NotNull Project project, + @NotNull ProgressIndicator indicator, + @NotNull String reason, + @Nullable DocumentEvent event) { this.document = document; this.project = project; this.indicator = indicator; + this.reason = reason; + this.event = event; } } @@ -239,12 +248,13 @@ public class DocumentCommitThread implements Runnable, Disposable { Project project = null; ProgressIndicator indicator = null; try { + CommitTask task; synchronized (documentsToCommit) { if (!myEnabled || documentsToCommit.isEmpty()) { documentsToCommit.wait(); continue; } - CommitTask task = documentsToCommit.pullFirst(); + task = documentsToCommit.pullFirst(); document = task.document; indicator = task.indicator; project = task.project; @@ -267,7 +277,7 @@ public class DocumentCommitThread implements Runnable, Disposable { Runnable finishRunnable = null; if (!success && !indicator.isCanceled()) { try { - finishRunnable = commit(document, project, null, indicator, false); + finishRunnable = commit(document, project, null, indicator, false, task.reason, task.event); success = finishRunnable != null; log("Committed", document, false, finishRunnable, indicator); } @@ -304,7 +314,7 @@ public class DocumentCommitThread implements Runnable, Disposable { cancel(e); } if (!success && indicator.isRunning()) { // running means sync commit has not intervened - doQueue(document, project, CommitStage.QUEUED_TO_COMMIT, "re-added on failure"); + doQueue(document, project, CommitStage.QUEUED_TO_COMMIT, "re-added on failure", null); } } catch(Throwable e) { @@ -340,7 +350,7 @@ public class DocumentCommitThread implements Runnable, Disposable { ProgressIndicatorBase indicator = new ProgressIndicatorBase(); indicator.start(); log("About to commit sync", document, true, indicator); - Runnable finish = commit(document, project, excludeFile, indicator, true); + Runnable finish = commit(document, project, excludeFile, indicator, true, "Sync commit", null); log("Committed sync", document, true, finish, indicator); assert finish != null; @@ -351,12 +361,14 @@ public class DocumentCommitThread implements Runnable, Disposable { @NotNull final Project project, final PsiFile excludeFile, @NotNull final ProgressIndicator indicator, - final boolean synchronously) { + final boolean synchronously, + @NotNull final String reason, + @Nullable final DocumentEvent event) { final Runnable[] success = new Runnable[1]; ((ProgressManagerImpl)ProgressManager.getInstance()).executeProcessUnderProgress(new Runnable() { @Override public void run() { - success[0] = commitUnderProgress(document, project, excludeFile, indicator, synchronously); + success[0] = commitUnderProgress(document, project, excludeFile, indicator, synchronously, reason, event); } }, indicator); return success[0]; @@ -374,10 +386,12 @@ public class DocumentCommitThread implements Runnable, Disposable { // returns finish commit Runnable (to be invoked later in EDT), or null on failure private Runnable commitUnderProgress(@NotNull final Document document, - @NotNull final Project project, - final PsiFile excludeFile, - @NotNull final ProgressIndicator indicator, - final boolean synchronously) { + @NotNull final Project project, + final PsiFile excludeFile, + @NotNull final ProgressIndicator indicator, + final boolean synchronously, + @NotNull final String reason, + @Nullable final DocumentEvent event) { final List> finishRunnables = new ArrayList>(); Runnable runnable = new Runnable() { public void run() { @@ -436,7 +450,7 @@ public class DocumentCommitThread implements Runnable, Disposable { boolean success = false; try { - success = documentManager.finishCommit(document, finishRunnables, synchronously); + success = documentManager.finishCommit(document, finishRunnables, synchronously, reason, event); log("Finished", document, synchronously, success, Arrays.asList(documentManager.getUncommittedDocuments())); if (synchronously) { assert success; @@ -456,7 +470,7 @@ public class DocumentCommitThread implements Runnable, Disposable { if (!success) { // add document back to the queue log("Re-adding back", document, synchronously, success, documentManager.isCommitted(document), Arrays.asList(documentManager.getUncommittedDocuments())); - queueCommit("Re-added back", document, project); + queueCommit("Re-added back", document, project, null); } } }); @@ -469,7 +483,8 @@ public class DocumentCommitThread implements Runnable, Disposable { private Processor doCommit(@NotNull final Document document, @NotNull final PsiFile file, @NotNull ProgressIndicator indicator, - final boolean synchronously, PsiDocumentManager documentManager) { + final boolean synchronously, + @NotNull PsiDocumentManager documentManager) { ((PsiDocumentManagerImpl)documentManager).clearTreeHardRef(document); final TextBlock textBlock = PsiDocumentManagerImpl.getTextBlock(file); if (textBlock.isEmpty()) return null; diff --git a/platform/lang-impl/src/com/intellij/psi/impl/PsiDocumentManagerImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/PsiDocumentManagerImpl.java index c5a074995d97..75071a7c2f1b 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/PsiDocumentManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/PsiDocumentManagerImpl.java @@ -309,7 +309,11 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec } } - boolean finishCommit(@NotNull final Document document, final List> finishRunnables, boolean synchronously) { + boolean finishCommit(@NotNull final Document document, + final List> finishRunnables, + boolean synchronously, + @NotNull String reason, + @Nullable DocumentEvent event) { if (myProject.isDisposed()) return false; ApplicationManager.getApplication().assertWriteAccessAllowed(); @@ -343,10 +347,27 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec if (success) { runAfterCommitActions(document); + if (LOG.isDebugEnabled()) { + checkAllElementsValid(document, reason, event); + } } return success; } + private void checkAllElementsValid(@NotNull Document document, @NotNull final String reason, @Nullable final DocumentEvent event) { + final PsiFile psiFile = getCachedPsiFile(document); + if (psiFile != null) { + psiFile.accept(new PsiRecursiveElementWalkingVisitor() { + @Override + public void visitElement(PsiElement element) { + if (!element.isValid()) { + LOG.error("Commit to '"+psiFile.getVirtualFile()+"' lead to invalid element: "+element+ "; Reason: '"+reason+"'; Doc change event: "+event); + } + } + }); + } + } + private void doCommit(@NotNull final Document document, final PsiFile excludeFile) { ApplicationManager.getApplication().runWriteAction(new Runnable() { public void run() { @@ -538,7 +559,7 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec if (file == null) continue; if (file.isPhysical() && mySmartPointerManager != null) { // mock tests - SmartPointerManagerImpl.fastenBelts(file, event.getOffset()); + SmartPointerManagerImpl.fastenBelts(file, event.getOffset(), null); } final TextBlock textBlock = getTextBlock(file); @@ -591,7 +612,7 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec if (commitNecessary) { myUncommittedDocuments.add(document); - myDocumentCommitThread.queueCommit("Document changed", document, myProject); + myDocumentCommitThread.queueCommit("Document changed", document, myProject, event); } // Consider that it's worth to perform complete re-parse instead of merge if the whole document text is replaced and diff --git a/platform/lang-impl/src/com/intellij/psi/impl/file/impl/FileManagerImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/file/impl/FileManagerImpl.java index 782325fd300d..e1f42cbbd9e6 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/file/impl/FileManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/file/impl/FileManagerImpl.java @@ -873,7 +873,7 @@ public class FileManagerImpl implements FileManager { } if (oldPsiFile != null && oldPsiFile.isPhysical()) { - SmartPointerManagerImpl.fastenBelts(oldPsiFile, 0); + SmartPointerManagerImpl.fastenBelts(oldPsiFile, 0, null); } ApplicationManager.getApplication().runWriteAction( new ExternalChangeAction() { diff --git a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/ClsElementInfo.java b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/ClsElementInfo.java index 8c9deca83bb9..ac66849c1b88 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/ClsElementInfo.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/ClsElementInfo.java @@ -16,6 +16,7 @@ package com.intellij.psi.impl.smartPointers; import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Segment; @@ -40,7 +41,7 @@ public class ClsElementInfo implements SmartPointerElementInfo { // before change @Override - public void fastenBelt(int offset) { + public void fastenBelt(int offset, RangeMarker cachedRangeMarker) { } // after change @@ -56,10 +57,6 @@ public class ClsElementInfo implements SmartPointerElementInfo { return myStubIndexReference.retrieve(); } - @Override - public void dispose() { - } - @Override public int elementHashCode() { return myStubIndexReference.hashCode(); diff --git a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/FileElementInfo.java b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/FileElementInfo.java index 54412d636905..55bafb8e67c3 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/FileElementInfo.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/FileElementInfo.java @@ -16,6 +16,7 @@ package com.intellij.psi.impl.smartPointers; import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Segment; @@ -48,7 +49,7 @@ class FileElementInfo implements SmartPointerElementInfo { } @Override - public void fastenBelt(int offset) { + public void fastenBelt(int offset, RangeMarker cachedRangeMarker) { } @Override @@ -59,10 +60,6 @@ class FileElementInfo implements SmartPointerElementInfo { return SelfElementInfo.restoreFileFromVirtual(myVirtualFile, myProject); } - @Override - public void dispose() { - } - @Override public int elementHashCode() { return myVirtualFile.hashCode(); diff --git a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/HardElementInfo.java b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/HardElementInfo.java index 50c5ff8b32cd..a95eed3a4035 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/HardElementInfo.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/HardElementInfo.java @@ -16,6 +16,7 @@ package com.intellij.psi.impl.smartPointers; import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Segment; @@ -44,7 +45,7 @@ class HardElementInfo implements SmartPointerElementInfo { } @Override - public void fastenBelt(int offset) { + public void fastenBelt(int offset, RangeMarker cachedRangeMarker) { } @Override @@ -55,10 +56,6 @@ class HardElementInfo implements SmartPointerElementInfo { return myElement; } - @Override - public void dispose() { - } - @Override public int elementHashCode() { return myElement.hashCode(); diff --git a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/InjectedSelfElementInfo.java b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/InjectedSelfElementInfo.java index b4c8a55fc26a..79661277555a 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/InjectedSelfElementInfo.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/InjectedSelfElementInfo.java @@ -38,13 +38,13 @@ class InjectedSelfElementInfo extends SelfElementInfo { private final Class anchorClass; private final Language anchorLanguage; - InjectedSelfElementInfo(@NotNull Project project, @NotNull PsiElement anchor, @NotNull PsiElement context) { - super(project, context); + InjectedSelfElementInfo(@NotNull Project project, @NotNull PsiElement element, @NotNull PsiElement hostContext) { + super(project, hostContext); SmartPointerManager smartPointerManager = SmartPointerManager.getInstance(project); - TextRange range = InjectedLanguageManager.getInstance(project).injectedToHost(anchor, anchor.getTextRange()); - myInjectedFileRangeInHostFile = smartPointerManager.createSmartPsiFileRangePointer(context.getContainingFile(), range); - anchorClass = anchor.getClass(); - anchorLanguage = anchor.getContainingFile().getLanguage(); + TextRange range = InjectedLanguageManager.getInstance(project).injectedToHost(element, element.getTextRange()); + myInjectedFileRangeInHostFile = smartPointerManager.createSmartPsiFileRangePointer(hostContext.getContainingFile(), range); + anchorClass = element.getClass(); + anchorLanguage = element.getContainingFile().getLanguage(); } @Override @@ -56,8 +56,8 @@ class InjectedSelfElementInfo extends SelfElementInfo { @Override public PsiElement restoreElement() { - PsiElement host = super.restoreElement(); - if (host == null) return null; + PsiElement hostContext = super.restoreElement(); + if (hostContext == null) return null; Segment segment = myInjectedFileRangeInHostFile.getRange(); if (segment == null) return null; @@ -65,7 +65,7 @@ class InjectedSelfElementInfo extends SelfElementInfo { final Ref result = new Ref(); final InjectedLanguageManager manager = InjectedLanguageManager.getInstance(getProject()); - InjectedLanguageUtil.enumerate(host, host.getContainingFile(), new PsiLanguageInjectionHost.InjectedPsiVisitor() { + InjectedLanguageUtil.enumerate(hostContext, hostContext.getContainingFile(), new PsiLanguageInjectionHost.InjectedPsiVisitor() { @Override public void visit(@NotNull PsiFile injectedPsi, @NotNull List places) { if (result.get() != null) return; diff --git a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/MultiRootSelfElementInfo.java b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/MultiRootSelfElementInfo.java index 548f3a7ecc50..eeb84c588fab 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/MultiRootSelfElementInfo.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/MultiRootSelfElementInfo.java @@ -17,7 +17,7 @@ package com.intellij.psi.impl.smartPointers; import com.intellij.lang.Language; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.ProperTextRange; import com.intellij.psi.PsiFile; import org.jetbrains.annotations.NotNull; @@ -26,7 +26,7 @@ import org.jetbrains.annotations.NotNull; */ public class MultiRootSelfElementInfo extends SelfElementInfo { public MultiRootSelfElementInfo(@NotNull Project project, - @NotNull TextRange anchor, + @NotNull ProperTextRange anchor, @NotNull Class anchorClass, @NotNull PsiFile containingFile, @NotNull Language language) { diff --git a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SelfElementInfo.java b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SelfElementInfo.java index 77089316d8c6..c8db484fd59d 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SelfElementInfo.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SelfElementInfo.java @@ -21,10 +21,7 @@ import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Comparing; -import com.intellij.openapi.util.Computable; -import com.intellij.openapi.util.Segment; -import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.*; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import org.jetbrains.annotations.NotNull; @@ -49,10 +46,11 @@ public class SelfElementInfo implements SmartPointerElementInfo { protected final Language myLanguage; protected SelfElementInfo(@NotNull Project project, @NotNull PsiElement anchor) { - this(project, anchor.getTextRange(), anchor.getClass(), anchor.getContainingFile(), anchor.getContainingFile().getLanguage()); + this(project, ProperTextRange.create(anchor.getTextRange()), anchor.getClass(), anchor.getContainingFile(), + anchor.getContainingFile().getLanguage()); } public SelfElementInfo(@NotNull Project project, - @NotNull TextRange anchor, + @NotNull ProperTextRange anchor, @NotNull Class anchorClass, @NotNull PsiFile containingFile, @NotNull Language language) { @@ -104,7 +102,7 @@ public class SelfElementInfo implements SmartPointerElementInfo { // before change @Override - public void fastenBelt(int offset) { + public void fastenBelt(int offset, @Nullable RangeMarker cachedRangeMarker) { if (!mySyncMarkerIsValid) return; RangeMarker marker = getMarker(); int actualEndOffset = marker == null || !marker.isValid() ? getSyncEndOffset() : marker.getEndOffset(); @@ -117,15 +115,19 @@ public class SelfElementInfo implements SmartPointerElementInfo { mySyncMarkerIsValid = false; return; } - //PsiToDocumentSynchronizer synchronizer = ((PsiDocumentManagerImpl)PsiDocumentManager.getInstance(myProject)).getSynchronizer(); - //boolean inSynchronization = synchronizer.isInSynchronization(document); - - //if (!inSynchronization) { // otherwise doc offsets are incorrect - int start = Math.min(getSyncStartOffset(), document.getTextLength()); - int end = Math.min(Math.max(getSyncEndOffset(), start), document.getTextLength()); + int start = Math.min(getSyncStartOffset(), document.getTextLength()); + int end = Math.min(Math.max(getSyncEndOffset(), start), document.getTextLength()); + // use supplied cached markers if available + if (cachedRangeMarker != null && + cachedRangeMarker.isValid() && + cachedRangeMarker.getStartOffset() == start && + cachedRangeMarker.getEndOffset() == end) { + marker = cachedRangeMarker; + } + else { marker = document.createRangeMarker(start, end, true); - setMarker(marker); - //} + } + setMarker(marker); } else if (!marker.isValid()) { mySyncMarkerIsValid = false; @@ -151,7 +153,7 @@ public class SelfElementInfo implements SmartPointerElementInfo { mySyncMarkerIsValid = false; } } - myRangeMarker = null; + myRangeMarker = null; // clear hard ref to avoid leak, hold soft ref for not recreating marker later } // commit @@ -196,16 +198,7 @@ public class SelfElementInfo implements SmartPointerElementInfo { return null; } - @Override - public void dispose() { - RangeMarker marker = getMarker(); - if (marker != null) { - marker.dispose(); - setMarker(null); - } - } - - private RangeMarker getMarker() { + RangeMarker getMarker() { Reference ref = myMarkerRef; return ref == null ? null : ref.get(); } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerElementInfo.java b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerElementInfo.java index ef8b6afda90f..6a986a4a24a2 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerElementInfo.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerElementInfo.java @@ -17,6 +17,7 @@ package com.intellij.psi.impl.smartPointers; import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Segment; import com.intellij.openapi.vfs.VirtualFile; @@ -29,14 +30,13 @@ interface SmartPointerElementInfo { Document getDocumentToSynchronize(); void documentAndPsiInSync(); - void fastenBelt(int offset); + void fastenBelt(int offset, RangeMarker cachedRangeMarker); void unfastenBelt(int offset); @Nullable PsiElement restoreElement(); - void dispose(); int elementHashCode(); // must be immutable boolean pointsToTheSameElementAs(@NotNull SmartPointerElementInfo other); diff --git a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerEx.java b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerEx.java index 6435c317f7e8..900ce1916bf3 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerEx.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerEx.java @@ -15,11 +15,13 @@ */ package com.intellij.psi.impl.smartPointers; +import com.intellij.openapi.editor.RangeMarker; import com.intellij.psi.PsiElement; import com.intellij.psi.SmartPsiElementPointer; +import org.jetbrains.annotations.Nullable; public interface SmartPointerEx extends SmartPsiElementPointer { - void fastenBelt(int offset); + void fastenBelt(int offset, @Nullable RangeMarker cachedRangeMarker); void documentAndPsiInSync(); void unfastenBelt(int offset); diff --git a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerManagerImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerManagerImpl.java index 9e7be6befc61..965acf5cbeff 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerManagerImpl.java @@ -16,18 +16,22 @@ package com.intellij.psi.impl.smartPointers; +import com.intellij.injected.editor.DocumentWindow; import com.intellij.lang.Language; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.editor.impl.DocumentImpl; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.ProperTextRange; import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil; import com.intellij.reference.SoftReference; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.lang.ref.Reference; import java.lang.ref.WeakReference; @@ -47,7 +51,7 @@ public class SmartPointerManagerImpl extends SmartPointerManager { myProject = project; } - public static void fastenBelts(@NotNull PsiFile file, int offset) { + public static void fastenBelts(@NotNull PsiFile file, int offset, @Nullable RangeMarker cachedRangeMarker) { synchronized (file) { if (areBeltsFastened(file)) return; @@ -66,15 +70,16 @@ public class SmartPointerManagerImpl extends SmartPointerManager { WeakReference reference = pointers.get(i); SmartPointerEx pointer = reference.get(); if (pointer != null) { - pointer.fastenBelt(offset); + pointer.fastenBelt(offset, cachedRangeMarker); pointers.set(index++, reference); } } - for (Document injectedDoc : InjectedLanguageUtil.getCachedInjectedDocuments(file)) { + for (DocumentWindow injectedDoc : InjectedLanguageUtil.getCachedInjectedDocuments(file)) { PsiFile injectedFile = psiDocumentManager.getPsiFile(injectedDoc); if (injectedFile == null) continue; - fastenBelts(injectedFile, 0); + RangeMarker cachedMarker = getCachedRangeMarkerToInjectedFragment(injectedFile); + fastenBelts(injectedFile, 0, cachedMarker); } int size = pointers.size(); @@ -84,6 +89,19 @@ public class SmartPointerManagerImpl extends SmartPointerManager { } } + private static RangeMarker getCachedRangeMarkerToInjectedFragment(PsiFile injectedFile) { + PsiElement hostContext = injectedFile.getContext(); + RangeMarker cachedMarker = null; + if (hostContext != null) { + SmartPsiElementPointer cachedPointer = getCachedPointer(hostContext); + SmartPointerElementInfo info = cachedPointer == null ? null : ((SmartPsiElementPointerImpl)cachedPointer).getElementInfo(); + if (info instanceof SelfElementInfo) { + cachedMarker = ((SelfElementInfo)info).getMarker(); + } + } + return cachedMarker; + } + public static void unfastenBelts(@NotNull PsiFile file, int offset) { synchronized (file) { PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(file.getProject()); @@ -107,7 +125,7 @@ public class SmartPointerManagerImpl extends SmartPointerManager { } } - for (Document injectedDoc : InjectedLanguageUtil.getCachedInjectedDocuments(file)) { + for (DocumentWindow injectedDoc : InjectedLanguageUtil.getCachedInjectedDocuments(file)) { PsiFile injectedFile = psiDocumentManager.getPsiFile(injectedDoc); if (injectedFile == null) continue; unfastenBelts(injectedFile, 0); @@ -168,8 +186,7 @@ public class SmartPointerManagerImpl extends SmartPointerManager { if (containingFile != null && !containingFile.isValid() || containingFile == null && !element.isValid()) { LOG.error("Invalid element:" + element); } - Reference data = element.getUserData(CACHED_SMART_POINTER_KEY); - SmartPsiElementPointer cachedPointer = data == null ? null : data.get(); + SmartPsiElementPointer cachedPointer = getCachedPointer(element); if (cachedPointer != null) { return cachedPointer; } @@ -180,14 +197,18 @@ public class SmartPointerManagerImpl extends SmartPointerManager { return pointer; } + private static SmartPsiElementPointer getCachedPointer(E element) { + Reference data = element.getUserData(CACHED_SMART_POINTER_KEY); + return data == null ? null : data.get(); + } + @Override @NotNull public SmartPsiFileRange createSmartPsiFileRangePointer(@NotNull PsiFile file, @NotNull TextRange range) { if (!file.isValid()) { LOG.error("Invalid element:" + file); } - - SmartPsiFileRangePointerImpl pointer = new SmartPsiFileRangePointerImpl(file, range); + SmartPsiFileRangePointerImpl pointer = new SmartPsiFileRangePointerImpl(file, ProperTextRange.create(range)); initPointer(pointer, file); return pointer; @@ -215,7 +236,7 @@ public class SmartPointerManagerImpl extends SmartPointerManager { pointers.add(new WeakReference(pointer)); if (areBeltsFastened(containingFile)) { - pointer.fastenBelt(0); + pointer.fastenBelt(0, null); } } } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiElementPointerImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiElementPointerImpl.java index a16b502f821f..b1360aa439ec 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiElementPointerImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiElementPointerImpl.java @@ -19,8 +19,10 @@ package com.intellij.psi.impl.smartPointers; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.ProperTextRange; import com.intellij.openapi.util.Segment; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VirtualFile; @@ -157,8 +159,8 @@ class SmartPsiElementPointerImpl implements SmartPointerEx FileViewProvider viewProvider = containingFile.getViewProvider(); if (viewProvider instanceof InjectedFileViewProvider) { - PsiElement context = containingFile.getContext(); - if (context != null) return new InjectedSelfElementInfo(project, element, context); + PsiElement hostContext = containingFile.getContext(); + if (hostContext != null) return new InjectedSelfElementInfo(project, element, hostContext); } if (element instanceof PsiFile) { @@ -169,6 +171,7 @@ class SmartPsiElementPointerImpl implements SmartPointerEx if (elementRange == null) { return new HardElementInfo(project, element); } + ProperTextRange proper = ProperTextRange.create(elementRange); LOG.assertTrue(element.isPhysical()); LOG.assertTrue(element.isValid()); @@ -177,9 +180,9 @@ class SmartPsiElementPointerImpl implements SmartPointerEx VirtualFile virtualFile = containingFile.getVirtualFile(); boolean isElementInMainRoot = virtualFile == null || containingFile.getManager().findFile(virtualFile) == containingFile; if (isMultiRoot && !isElementInMainRoot) { - return new MultiRootSelfElementInfo(project, elementRange, element.getClass(), containingFile, containingFile.getLanguage()); + return new MultiRootSelfElementInfo(project, proper, element.getClass(), containingFile, containingFile.getLanguage()); } - return new SelfElementInfo(project, elementRange, element.getClass(), containingFile, containingFile.getLanguage()); + return new SelfElementInfo(project, proper, element.getClass(), containingFile, containingFile.getLanguage()); } public void documentAndPsiInSync() { @@ -191,8 +194,8 @@ class SmartPsiElementPointerImpl implements SmartPointerEx myElementInfo.unfastenBelt(offset); } - public void fastenBelt(int offset) { - myElementInfo.fastenBelt(offset); + public void fastenBelt(int offset, @Nullable RangeMarker cachedRangeMarker) { + myElementInfo.fastenBelt(offset, cachedRangeMarker); } @NotNull diff --git a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiFileRangePointerImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiFileRangePointerImpl.java index 9337b7368f6d..caecbcb09a97 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiFileRangePointerImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiFileRangePointerImpl.java @@ -15,7 +15,7 @@ */ package com.intellij.psi.impl.smartPointers; -import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.ProperTextRange; import com.intellij.psi.PsiFile; import com.intellij.psi.SmartPsiFileRange; import org.jetbrains.annotations.NotNull; @@ -24,12 +24,12 @@ import org.jetbrains.annotations.NotNull; * User: cdr */ public class SmartPsiFileRangePointerImpl extends SmartPsiElementPointerImpl implements SmartPsiFileRange { - public SmartPsiFileRangePointerImpl(@NotNull PsiFile containingFile, @NotNull TextRange range) { + public SmartPsiFileRangePointerImpl(@NotNull PsiFile containingFile, @NotNull ProperTextRange range) { super(containingFile, createElementInfo(containingFile, range), PsiFile.class); } @NotNull - private static SmartPointerElementInfo createElementInfo(@NotNull PsiFile containingFile, @NotNull TextRange range) { + private static SmartPointerElementInfo createElementInfo(@NotNull PsiFile containingFile, @NotNull ProperTextRange range) { return new SelfElementInfo(containingFile.getProject(), range, PsiFile.class, containingFile, containingFile.getLanguage()); } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/PostprocessReformattingAspect.java b/platform/lang-impl/src/com/intellij/psi/impl/source/PostprocessReformattingAspect.java index 6a9bc57c8539..dc2c9d6fcdfe 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/PostprocessReformattingAspect.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/PostprocessReformattingAspect.java @@ -292,7 +292,7 @@ public class PostprocessReformattingAspect implements PomModelAspect, Disposable // process all roots in viewProvider to find marked for reformat before elements and create appropriate ragge markers handleReformatMarkers(key, postprocessTasks); - // then we create ranges by changed nodes. One per node. There ranges can instersect. Ranges are sorted by end offset. + // then we create ranges by changed nodes. One per node. There ranges can intersect. Ranges are sorted by end offset. if (astNodes != null) createActionsMap(astNodes, key, postprocessTasks); if ("true".equals(System.getProperty("check.psi.is.valid")) && ApplicationManager.getApplication().isUnitTestMode()) { @@ -358,8 +358,8 @@ public class PostprocessReformattingAspect implements PomModelAspect, Disposable iterator.remove(); } else if (accumulatedTask.getStartOffset() > currentTask.getEndOffset() || - (accumulatedTask.getStartOffset() == currentTask.getEndOffset() && - !canStickActionsTogether(accumulatedTask, currentTask))) { + accumulatedTask.getStartOffset() == currentTask.getEndOffset() && + !canStickActionsTogether(accumulatedTask, currentTask)) { // action can be pushed if (accumulatedTask instanceof ReindentTask) { indentActions.add((ReindentTask) accumulatedTask); @@ -391,7 +391,7 @@ public class PostprocessReformattingAspect implements PomModelAspect, Disposable if (!(accumulatedTask instanceof ReindentTask)) { iterator.remove(); - boolean withLeadingWhitespace = (accumulatedTask instanceof ReformatWithHeadingWhitespaceTask); + boolean withLeadingWhitespace = accumulatedTask instanceof ReformatWithHeadingWhitespaceTask; if (accumulatedTask instanceof ReformatTask && currentTask instanceof ReformatWithHeadingWhitespaceTask && accumulatedTask.getStartOffset() == currentTask.getStartOffset()) { @@ -402,8 +402,20 @@ public class PostprocessReformattingAspect implements PomModelAspect, Disposable accumulatedTask.getStartOffset() < currentTask.getStartOffset()) { withLeadingWhitespace = false; } - RangeMarker rangeMarker = document.createRangeMarker(Math.min(accumulatedTask.getStartOffset(), currentTask.getStartOffset()), - Math.max(accumulatedTask.getEndOffset(), currentTask.getEndOffset())); + int newStart = Math.min(accumulatedTask.getStartOffset(), currentTask.getStartOffset()); + int newEnd = Math.max(accumulatedTask.getEndOffset(), currentTask.getEndOffset()); + RangeMarker rangeMarker; + + if (accumulatedTask.getStartOffset() == newStart && accumulatedTask.getEndOffset() == newEnd) { + rangeMarker = accumulatedTask.getRange(); + } + else if (currentTask.getStartOffset() == newStart && currentTask.getEndOffset() == newEnd) { + rangeMarker = currentTask.getRange(); + } + else { + rangeMarker = document.createRangeMarker(newStart, newEnd); + } + if (withLeadingWhitespace) { accumulatedTask = new ReformatWithHeadingWhitespaceTask(rangeMarker); } diff --git a/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/VariableInplaceRenamer.java b/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/VariableInplaceRenamer.java index db0d21d11ab1..fdf431ed9576 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/VariableInplaceRenamer.java +++ b/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/VariableInplaceRenamer.java @@ -284,6 +284,7 @@ public class VariableInplaceRenamer { } public void templateCancelled(Template template) { + PsiDocumentManager.getInstance(myProject).commitAllDocuments(); finish(); moveOffsetAfter(false); } diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java index 8812374245bf..2b2a8b6d2795 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java @@ -1183,10 +1183,12 @@ public class JBTabsImpl extends JComponent public void setPaintBlocked(boolean blocked, final boolean takeSnapshot) { if (blocked && !myPaintBlocked) { if (takeSnapshot) { - myImage = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB); - final Graphics2D g = myImage.createGraphics(); - super.paint(g); - g.dispose(); + if (getWidth() > 0 && getHeight() > 0) { + myImage = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB); + final Graphics2D g = myImage.createGraphics(); + super.paint(g); + g.dispose(); + } } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/IntervalTreeImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/IntervalTreeImpl.java index 4c0ab32b121e..121c28d0c8a8 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/IntervalTreeImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/IntervalTreeImpl.java @@ -49,7 +49,6 @@ public abstract class IntervalTreeImpl extends RedBla protected final List> intervals; protected int maxEnd; // max of all intervalEnd()s among all children. protected int delta; // delta of startOffset. getStartOffset() = myStartOffset + Sum of deltas up to root - private IntervalNode next; // node following this in the in-order tree traversal. used for optimised tree iteration public IntervalNode(@NotNull T key, int start, int end) { // maxEnd == 0 so to not disrupt existing maxes @@ -383,14 +382,13 @@ public abstract class IntervalTreeImpl extends RedBla return Math.max(start, startOffset) <= Math.min(end, endOffset); } + // next node in inorder traversal private IntervalNode nextNode(@NotNull IntervalNode root, int startOffset, int endOffset, @NotNull int[] newDeltaUpToRootExclusive) { assert root.isValid(); int deltaUpToRootExclusive = newDeltaUpToRootExclusive[0]; int delta = deltaUpToRootExclusive + root.delta; - //int myStartOffset = root.intervalStart() + delta; - //int myEndOffset = root.intervalEnd() + delta; int myMaxEnd = maxEndOf(root, deltaUpToRootExclusive); assert startOffset <= myMaxEnd; @@ -603,16 +601,6 @@ public abstract class IntervalTreeImpl extends RedBla assert ids.add(((RangeMarkerImpl)t).getId()) : t; } - if (assertInvalid) { - IntervalNode next = root.next; - if (next != null) { - assert previous(next) == root; - int nextStart = next.intervalStart() + next.computeDeltaUpToRoot(); - int myStart = root.intervalStart() + deltaUpToRootExclusive + root.delta; - assert nextStart >= myStart; - } - } - keyCounter.addAndGet(root.intervals.size()); nodeCounter.incrementAndGet(); int delta = deltaUpToRootExclusive + (root.isValid() ? root.delta : 0); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeHighlighterImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeHighlighterImpl.java index 3a1c292cc7ae..2bde09a1e701 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeHighlighterImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeHighlighterImpl.java @@ -196,6 +196,6 @@ class RangeHighlighterImpl extends RangeMarkerImpl implements RangeHighlighterEx @Override public String toString() { - return "RangeHighlighter: ("+getStartOffset()+","+getEndOffset()+"); layer="+getLayer(); + return "RangeHighlighter: ("+getStartOffset()+","+getEndOffset()+"); layer="+getLayer()+"; tooltip: "+getData().getErrorStripeTooltip(); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeMarkerImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeMarkerImpl.java index 00daf95631a6..0f372599fc5d 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeMarkerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeMarkerImpl.java @@ -21,12 +21,11 @@ import com.intellij.openapi.editor.ex.DocumentEx; import com.intellij.openapi.editor.ex.RangeMarkerEx; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.UserDataHolderBase; +import com.intellij.util.DistributedCounter; import com.intellij.util.Processor; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; -import java.util.concurrent.atomic.AtomicLong; - public class RangeMarkerImpl extends UserDataHolderBase implements RangeMarkerEx, MutableInterval { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.editor.impl.RangeMarkerImpl"); @@ -34,8 +33,7 @@ public class RangeMarkerImpl extends UserDataHolderBase implements RangeMarkerEx protected RangeMarkerTree.RMNode myNode; private final long myId; - //private static long counter; - private static final AtomicLong counter = new AtomicLong(); + private static final DistributedCounter counter = new DistributedCounter(); protected RangeMarkerImpl(@NotNull DocumentEx document, int start, int end, boolean register) { this(document, start, end, register, false, false); @@ -52,7 +50,7 @@ public class RangeMarkerImpl extends UserDataHolderBase implements RangeMarkerEx } myDocument = document; - myId = counter.getAndIncrement(); + myId = counter.next(); if (register) { registerInTree(start, end, greedyToLeft, greedyToRight, 0); } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeMarkerTree.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeMarkerTree.java index ff3cbaf1d8d6..c54964d542d2 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeMarkerTree.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeMarkerTree.java @@ -15,6 +15,9 @@ */ package com.intellij.openapi.editor.impl; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ex.ApplicationManagerEx; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.editor.ex.MarkupModelEx; @@ -25,13 +28,17 @@ import com.intellij.util.Processor; import org.jetbrains.annotations.NotNull; import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; /** * User: cdr */ public class RangeMarkerTree extends IntervalTreeImpl { + private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.editor.impl.RangeMarkerTree"); + private static final boolean DEBUG = LOG.isDebugEnabled() || ApplicationManager.getApplication().isUnitTestMode() || ApplicationManagerEx.getApplicationEx().isInternal(); + private final PrioritizedDocumentListener myListener; - final Document myDocument; + private final Document myDocument; private final EqualStartIntervalComparator myEqualStartIntervalComparator = new EqualStartIntervalComparator() { @Override public int compare(IntervalNode i1, IntervalNode i2) { @@ -81,6 +88,7 @@ public class RangeMarkerTree extends IntervalTreeImpl.RMNode addInterval(@NotNull T interval, int start, int end, boolean greedyToLeft, boolean greedyToRight, int layer) { RangeMarkerImpl marker = (RangeMarkerImpl)interval; @@ -88,7 +96,23 @@ public class RangeMarkerTree extends IntervalTreeImpl.RMNode node = (RMNode)super.addInterval(interval, start, end, greedyToLeft, greedyToRight, layer); checkBelongsToTheTree(interval, true); - + if (DEBUG && node.intervals.size() > DUPLICATE_LIMIT) { + System.gc();System.gc();System.gc(); + final StringBuilder msg = new StringBuilder(); + final AtomicInteger alive = new AtomicInteger(); + node.processAliveKeys(new Processor() { + @Override + public boolean process(T t) { + msg.append(t).append("\n"); + alive.incrementAndGet(); + return true; + } + }); + if (alive.get() > DUPLICATE_LIMIT) { + msg.insert(0, "Too many range markers (" + alive +") registered at ("+start+","+end+") in "+this+":\n"); + LOG.error(msg); + } + } return node; } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java index a5519bf72259..59f6a4de3d96 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java @@ -319,7 +319,7 @@ final class EditorTabbedContainer implements Disposable, CloseAction.CloseTarget } public void setPaintBlocked(boolean blocked) { - ((JBTabsImpl)myTabs).setPaintBlocked(blocked, false); + ((JBTabsImpl)myTabs).setPaintBlocked(blocked, true); } private class MyQueryable implements Queryable { diff --git a/platform/platform-impl/testSrc/com/intellij/openapi/editor/RangeMarkerTest.java b/platform/platform-impl/testSrc/com/intellij/openapi/editor/RangeMarkerTest.java index 2b9c66697124..284520f58f8d 100644 --- a/platform/platform-impl/testSrc/com/intellij/openapi/editor/RangeMarkerTest.java +++ b/platform/platform-impl/testSrc/com/intellij/openapi/editor/RangeMarkerTest.java @@ -969,8 +969,7 @@ public class RangeMarkerTest extends LightPlatformTestCase { public void testRangeMarkersAreWeakReferenced_NoVerify() throws Exception { final Document document = EditorFactory.getInstance().createDocument("[xxxxxxxxxxxxxx]"); - int COUNT = 100; - for (int i = 0; i < COUNT; i++) { + for (int i = 0; i < 10; i++) { document.createRangeMarker(0, document.getTextLength()); } diff --git a/platform/util/src/com/intellij/util/DistributedCounter.java b/platform/util/src/com/intellij/util/DistributedCounter.java new file mode 100644 index 000000000000..9fdfcd0b15a1 --- /dev/null +++ b/platform/util/src/com/intellij/util/DistributedCounter.java @@ -0,0 +1,50 @@ +/* + * Copyright 2000-2011 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.util; + +import java.util.concurrent.atomic.AtomicLong; + +/** + * Low-contention counter. + * Repeated calls to {@link #next()} return numbers which are unique across all calling threads, and which are increasing over calls within one thread. + */ +public class DistributedCounter { + private static final int CHUNK_SIZE = 1000; + private final AtomicLong nextChunkStart = new AtomicLong(); + // must not ne static + private final ThreadLocal localCounter = new ThreadLocal(); + private static class NextPair { + long nextId; + final long limit; + + private NextPair(long nextId, long limit) { + this.nextId = nextId; + this.limit = limit; + } + } + + public long next() { + NextPair nextPair = localCounter.get(); + if (nextPair == null || nextPair.nextId == nextPair.limit) { + long start = nextChunkStart.getAndAdd(CHUNK_SIZE); + nextPair = new NextPair(start, start + CHUNK_SIZE); + localCounter.set(nextPair); + } + long result = nextPair.nextId; + nextPair.nextId++; + return result; + } +} diff --git a/plugins/ant/src/com/intellij/lang/ant/config/execution/PlainTextView.java b/plugins/ant/src/com/intellij/lang/ant/config/execution/PlainTextView.java index b6a4f4fb7e79..d7200394830c 100644 --- a/plugins/ant/src/com/intellij/lang/ant/config/execution/PlainTextView.java +++ b/plugins/ant/src/com/intellij/lang/ant/config/execution/PlainTextView.java @@ -23,7 +23,6 @@ import com.intellij.execution.process.ProcessHandler; import com.intellij.execution.process.ProcessOutputTypes; import com.intellij.execution.ui.ConsoleView; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.fileEditor.OpenFileDescriptor; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; import com.intellij.openapi.vfs.LocalFileSystem; @@ -172,8 +171,7 @@ public final class PlainTextView implements AntOutputView { int highlightStartOffset = textStartOffset + placeInfo.getLinkStartIndex(); int highlightEndOffset = textStartOffset + placeInfo.getLinkEndIndex() + 1; - OpenFileDescriptor descriptor = new OpenFileDescriptor(myProject, placeInfo.getFile(), placeInfo.getLine(), placeInfo.getColumn()); - OpenFileHyperlinkInfo info = new OpenFileHyperlinkInfo(descriptor); + OpenFileHyperlinkInfo info = new OpenFileHyperlinkInfo(myProject, placeInfo.getFile(), placeInfo.getLine(), placeInfo.getColumn()); return new Result(highlightStartOffset, highlightEndOffset, info); } }