Merge remote branch 'origin/master'

This commit is contained in:
anna
2011-07-19 18:48:30 +04:00
37 changed files with 407 additions and 223 deletions
@@ -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 {
}
@@ -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);
}
}
@@ -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
@@ -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) {
@@ -24,7 +24,7 @@ import java.util.List;
public class CompositeFilter implements Filter {
private final List<Filter> myFilters = new ArrayList<Filter>();
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<Filter> 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)) {
@@ -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);
}
}
});
@@ -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;
}
@@ -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<RangeMarker> dirtyScopes = new TIntObjectHashMap<RangeMarker>();
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<RangeMarker>() {
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> T getUserData(@NotNull Key<T> key) {
throw new UnsupportedOperationException();
}
@Override
public <T> void putUserData(@NotNull Key<T> key, @Nullable T value) {
throw new UnsupportedOperationException();
}
};
}
@@ -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<HighlightInfo>(myHighlights);
}
private final Map<TextRange, RangeMarker> ranges2markersCache = new THashMap<TextRange, RangeMarker>();
private void collectHighlights(@NotNull final List<PsiElement> elements1,
@NotNull final Runnable after1,
@NotNull final List<PsiElement> 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);
}
});
}
@@ -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<TextRange, RangeMarker> ranges2markersCache = new THashMap<TextRange, RangeMarker>();
private final TransferToEDTQueue<Trinity<ProblemDescriptor, LocalInspectionTool,ProgressIndicator>> myTransferToEDTQueue
= new TransferToEDTQueue<Trinity<ProblemDescriptor, LocalInspectionTool,ProgressIndicator>>("Apply inspection results", new Processor<Trinity<ProblemDescriptor, LocalInspectionTool,ProgressIndicator>>() {
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;
@@ -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<TextRange, RangeMarker> 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<TextRange, RangeMarker> ranges2markersCache,
@NotNull final Map<TextRange, RangeMarker> 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<Pair<HighlightInfo.IntentionActionDescriptor, RangeMarker>> list =
new ArrayList<Pair<HighlightInfo.IntentionActionDescriptor, RangeMarker>>(info.quickFixActionRanges.size());
@@ -435,7 +436,13 @@ public class UpdateHighlightersUtil {
}
info.quickFixActionMarkers = new CopyOnWriteArrayList<Pair<HighlightInfo.IntentionActionDescriptor, RangeMarker>>(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<TextRange, RangeMarker> ranges2markersCache, @NotNull TextRange textRange) {
RangeMarker marker = ranges2markersCache == null ? null : ranges2markersCache.get(textRange);
private static RangeMarker getOrCreate(@NotNull Document document, @NotNull Map<TextRange, RangeMarker> 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;
}
@@ -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;
@@ -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<Processor<Document>> finishRunnables = new ArrayList<Processor<Document>>();
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<Document> 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;
@@ -309,7 +309,11 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec
}
}
boolean finishCommit(@NotNull final Document document, final List<Processor<Document>> finishRunnables, boolean synchronously) {
boolean finishCommit(@NotNull final Document document,
final List<Processor<Document>> 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
@@ -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() {
@@ -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();
@@ -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();
@@ -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();
@@ -38,13 +38,13 @@ class InjectedSelfElementInfo extends SelfElementInfo {
private final Class<? extends PsiElement> 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<PsiElement> result = new Ref<PsiElement>();
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<PsiLanguageInjectionHost.Shred> places) {
if (result.get() != null) return;
@@ -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) {
@@ -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<RangeMarker> ref = myMarkerRef;
return ref == null ? null : ref.get();
}
@@ -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);
@@ -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<E extends PsiElement> extends SmartPsiElementPointer<E> {
void fastenBelt(int offset);
void fastenBelt(int offset, @Nullable RangeMarker cachedRangeMarker);
void documentAndPsiInSync();
void unfastenBelt(int offset);
@@ -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<SmartPointerEx> 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<PsiElement> 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<SmartPsiElementPointer> data = element.getUserData(CACHED_SMART_POINTER_KEY);
SmartPsiElementPointer cachedPointer = data == null ? null : data.get();
SmartPsiElementPointer<E> cachedPointer = getCachedPointer(element);
if (cachedPointer != null) {
return cachedPointer;
}
@@ -180,14 +197,18 @@ public class SmartPointerManagerImpl extends SmartPointerManager {
return pointer;
}
private static <E extends PsiElement> SmartPsiElementPointer<E> getCachedPointer(E element) {
Reference<SmartPsiElementPointer> 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<SmartPointerEx>(pointer));
if (areBeltsFastened(containingFile)) {
pointer.fastenBelt(0);
pointer.fastenBelt(0, null);
}
}
}
@@ -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<E extends PsiElement> 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<E extends PsiElement> 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<E extends PsiElement> 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<E extends PsiElement> 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
@@ -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<PsiFile> 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());
}
@@ -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);
}
@@ -284,6 +284,7 @@ public class VariableInplaceRenamer {
}
public void templateCancelled(Template template) {
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
finish();
moveOffsetAfter(false);
}
@@ -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();
}
}
}
@@ -49,7 +49,6 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
protected final List<Getable<T>> 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<T extends MutableInterval> 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<T extends MutableInterval> 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);
@@ -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();
}
}
@@ -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);
}
@@ -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<T extends RangeMarkerEx> extends IntervalTreeImpl<T> {
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<IntervalNode> myEqualStartIntervalComparator = new EqualStartIntervalComparator<IntervalNode>() {
@Override
public int compare(IntervalNode i1, IntervalNode i2) {
@@ -81,6 +88,7 @@ public class RangeMarkerTree<T extends RangeMarkerEx> extends IntervalTreeImpl<T
myDocument.removeDocumentListener(myListener);
}
private static final int DUPLICATE_LIMIT = 30; // assertion: no more than DUPLICATE_LIMIT range markers are allowed to be registered at given (start, end)
@Override
public RangeMarkerTree<T>.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<T extends RangeMarkerEx> extends IntervalTreeImpl<T
RangeMarkerTree<T>.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<T>() {
@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;
}
@@ -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 {
@@ -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());
}
@@ -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<NextPair> localCounter = new ThreadLocal<NextPair>();
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;
}
}
@@ -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);
}
}