Merge remote-tracking branch 'origin/master'

This commit is contained in:
Roman Shevchenko
2014-09-16 15:59:18 +04:00
41 changed files with 534 additions and 171 deletions
@@ -17,12 +17,12 @@ package com.intellij.codeInsight;
import com.intellij.codeInspection.bytecodeAnalysis.ProjectBytecodeAnalysis;
import com.intellij.codeInspection.dataFlow.ContractInference;
import com.intellij.codeInspection.dataFlow.HardcodedContracts;
import com.intellij.codeInspection.dataFlow.MethodContract;
import com.intellij.codeInspection.dataFlow.PurityInference;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiModifierListOwner;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
@@ -33,14 +33,29 @@ import java.util.List;
import static com.intellij.codeInspection.dataFlow.ControlFlowAnalyzer.ORG_JETBRAINS_ANNOTATIONS_CONTRACT;
public class InferredAnnotationsManagerImpl extends InferredAnnotationsManager {
private final Project myProject;
public InferredAnnotationsManagerImpl(Project project) {
myProject = project;
}
@Nullable
@Override
public PsiAnnotation findInferredAnnotation(@NotNull PsiModifierListOwner listOwner, @NotNull String annotationFQN) {
listOwner = BaseExternalAnnotationsManager.preferCompiledElement(listOwner);
PsiAnnotation fromBytecode = ProjectBytecodeAnalysis.getInstance(listOwner.getProject()).findInferredAnnotation(listOwner, annotationFQN);
if (fromBytecode != null) {
return fromBytecode;
if (ORG_JETBRAINS_ANNOTATIONS_CONTRACT.equals(annotationFQN) && listOwner instanceof PsiMethod) {
PsiAnnotation anno = getHardcodedContractAnnotation((PsiMethod)listOwner);
if (anno != null) {
return anno;
}
}
if (!ignoreBytecodeInference(listOwner, annotationFQN)) {
PsiAnnotation fromBytecode = ProjectBytecodeAnalysis.getInstance(myProject).findInferredAnnotation(listOwner, annotationFQN);
if (fromBytecode != null) {
return fromBytecode;
}
}
if (ORG_JETBRAINS_ANNOTATIONS_CONTRACT.equals(annotationFQN) && canHaveContract(listOwner)) {
@@ -50,10 +65,34 @@ public class InferredAnnotationsManagerImpl extends InferredAnnotationsManager {
return null;
}
private PsiAnnotation getHardcodedContractAnnotation(PsiMethod listOwner) {
List<MethodContract> contracts = HardcodedContracts.getHardcodedContracts(listOwner, null);
return contracts.isEmpty() ? null : createContractAnnotation(contracts, true);
}
private static boolean ignoreBytecodeInference(PsiModifierListOwner owner, String annotationFQN) {
if (ORG_JETBRAINS_ANNOTATIONS_CONTRACT.equals(annotationFQN) && hasHardcodedContracts(owner)) {
return true;
}
if (AnnotationUtil.NOT_NULL.equals(annotationFQN) &&
owner instanceof PsiParameter && owner.getParent() != null &&
hasHardcodedContracts(owner.getParent().getParent())) {
return true;
}
return false;
}
private static boolean hasHardcodedContracts(PsiElement owner) {
return owner instanceof PsiMethod && !HardcodedContracts.getHardcodedContracts((PsiMethod)owner, null).isEmpty();
}
@Nullable
private static PsiAnnotation getInferredContractAnnotation(PsiMethod method) {
List<MethodContract> contracts = ContractInference.inferContracts((PsiMethod)method);
boolean pure = PurityInference.inferPurity(method);
private PsiAnnotation getInferredContractAnnotation(PsiMethod method) {
return createContractAnnotation(ContractInference.inferContracts(method), PurityInference.inferPurity(method));
}
@Nullable
private PsiAnnotation createContractAnnotation(List<MethodContract> contracts, boolean pure) {
final String attrs;
if (!contracts.isEmpty() && pure) {
attrs = "value = " + "\"" + StringUtil.join(contracts, "; ") + "\"; pure = true";
@@ -64,7 +103,7 @@ public class InferredAnnotationsManagerImpl extends InferredAnnotationsManager {
} else {
return null;
}
return ProjectBytecodeAnalysis.getInstance(method.getProject()).createContractAnnotation(attrs);
return ProjectBytecodeAnalysis.getInstance(myProject).createContractAnnotation(attrs);
}
private static boolean canHaveContract(PsiModifierListOwner listOwner) {
@@ -76,15 +115,18 @@ public class InferredAnnotationsManagerImpl extends InferredAnnotationsManager {
public PsiAnnotation[] findInferredAnnotations(@NotNull PsiModifierListOwner listOwner) {
listOwner = BaseExternalAnnotationsManager.preferCompiledElement(listOwner);
List<PsiAnnotation> result = ContainerUtil.newArrayList();
PsiAnnotation[] fromBytecode = ProjectBytecodeAnalysis.getInstance(listOwner.getProject()).findInferredAnnotations(listOwner);
PsiAnnotation[] fromBytecode = ProjectBytecodeAnalysis.getInstance(myProject).findInferredAnnotations(listOwner);
for (PsiAnnotation annotation : fromBytecode) {
if (!ORG_JETBRAINS_ANNOTATIONS_CONTRACT.equals(annotation.getQualifiedName()) || canHaveContract(listOwner)) {
result.add(annotation);
if (!ignoreBytecodeInference(listOwner, annotation.getQualifiedName())) {
if (!ORG_JETBRAINS_ANNOTATIONS_CONTRACT.equals(annotation.getQualifiedName()) || canHaveContract(listOwner)) {
result.add(annotation);
}
}
}
if (canHaveContract(listOwner)) {
ContainerUtil.addIfNotNull(result, getInferredContractAnnotation((PsiMethod)listOwner));
PsiAnnotation hardcoded = getHardcodedContractAnnotation((PsiMethod)listOwner);
ContainerUtil.addIfNotNull(result, hardcoded != null ? hardcoded : getInferredContractAnnotation((PsiMethod)listOwner));
}
return result.isEmpty() ? PsiAnnotation.EMPTY_ARRAY : result.toArray(new PsiAnnotation[result.size()]);
@@ -149,7 +149,8 @@ public class PostHighlightingPass extends ProgressableTextEditorHighlightingPass
myInLibrary = fileIndex.isInLibraryClasses(virtualFile) || fileIndex.isInLibrarySource(virtualFile);
myRefCountHolder = RefCountHolder.endUsing(myFile, progress);
if (myRefCountHolder == null || !myRefCountHolder.retrieveUnusedReferencesInfo(progress, new Runnable() {
boolean r = true;
if (myRefCountHolder == null || !(r=myRefCountHolder.retrieveUnusedReferencesInfo(progress, new Runnable() {
@Override
public void run() {
boolean errorFound = collectHighlights(elementSet, highlights, progress);
@@ -158,8 +159,9 @@ public class PostHighlightingPass extends ProgressableTextEditorHighlightingPass
fileStatusMap.setErrorFoundFlag(myDocument, true);
}
}
})) {
}))) {
// we must be sure GHP will restart
FileStatusMap.log("myRefCountHolder: ", myRefCountHolder, "; retrieved: ", r);
fileStatusMap.markFileScopeDirty(getDocument(), Pass.UPDATE_ALL);
GeneralHighlightingPass.cancelAndRestartDaemonLater(progress, myProject, this);
}
@@ -35,7 +35,10 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.ref.SoftReference;
import java.util.*;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
public class RefCountHolder {
@@ -328,10 +331,11 @@ public class RefCountHolder {
return true;
}
private static void log(@NonNls Object... s) {
//System.err.println("RFC: "+ Arrays.asList(s));
private static void log(@NonNls @NotNull Object... info) {
FileStatusMap.log(info);
}
public boolean retrieveUnusedReferencesInfo(@NotNull ProgressIndicator indicator, @NotNull Runnable analyze) {
ProgressIndicator old = myState.get();
if (!myState.compareAndSet(READY, indicator)) {
@@ -340,7 +344,9 @@ public class RefCountHolder {
}
log("r: changed ", old, "->", indicator);
try {
if (analyzedUnder != indicator) {
ProgressIndicator under = analyzedUnder;
if (under != indicator) {
log("r: analyzed under ", under, "->", indicator);
return false;
}
analyze.run();
@@ -350,6 +356,6 @@ public class RefCountHolder {
assert set : myState.get();
log("r: changed back ", indicator, "->", READY);
}
return true;
return true;
}
}
@@ -64,6 +64,7 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
@NonNls private static final String SHORT_NAME = "ConstantConditions";
public boolean SUGGEST_NULLABLE_ANNOTATIONS = false;
public boolean DONT_REPORT_TRUE_ASSERT_STATEMENTS = false;
public boolean TREAT_UNKNOWN_MEMBERS_AS_NULLABLE = false;
public boolean IGNORE_ASSERT_STATEMENTS = false;
public boolean REPORT_CONSTANT_REFERENCE_VALUES = true;
@@ -82,6 +83,9 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
if (!REPORT_CONSTANT_REFERENCE_VALUES) {
node.addContent(new Element("option").setAttribute("name", "REPORT_CONSTANT_REFERENCE_VALUES").setAttribute("value", "false"));
}
if (TREAT_UNKNOWN_MEMBERS_AS_NULLABLE) {
node.addContent(new Element("option").setAttribute("name", "TREAT_UNKNOWN_MEMBERS_AS_NULLABLE").setAttribute("value", "true"));
}
}
@Override
@@ -121,7 +125,7 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
PsiClass containingClass = PsiTreeUtil.getParentOfType(scope, PsiClass.class);
if (containingClass != null && PsiUtil.isLocalOrAnonymousClass(containingClass)) return;
final StandardDataFlowRunner dfaRunner = new StandardDataFlowRunner(scope) {
final StandardDataFlowRunner dfaRunner = new StandardDataFlowRunner(scope, TREAT_UNKNOWN_MEMBERS_AS_NULLABLE) {
@Override
protected boolean shouldCheckTimeLimit() {
if (!onTheFly) return false;
@@ -57,13 +57,17 @@ public class DataFlowRunner {
public static final int MAX_STATES_PER_BRANCH = 300;
protected DataFlowRunner(PsiElement block) {
this(block, false);
}
protected DataFlowRunner(PsiElement block, boolean unknownMembersAreNullable) {
PsiElement parentConstructor = PsiTreeUtil.findFirstParent(block, new Condition<PsiElement>() {
@Override
public boolean value(PsiElement psiElement) {
return psiElement instanceof PsiMethod && ((PsiMethod)psiElement).isConstructor();
}
});
myValueFactory = new DfaValueFactory(parentConstructor == null);
myValueFactory = new DfaValueFactory(parentConstructor == null, unknownMembersAreNullable);
}
public DfaValueFactory getFactory() {
@@ -15,9 +15,13 @@
*/
package com.intellij.codeInspection.dataFlow;
import com.intellij.psi.*;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiMethodCallExpression;
import com.siyeh.ig.psiutils.ExpressionUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
@@ -28,8 +32,8 @@ import static com.intellij.codeInspection.dataFlow.MethodContract.createConstrai
/**
* @author peter
*/
class HardcodedContracts {
static List<MethodContract> getHardcodedContracts(@NotNull PsiMethod method, @NotNull PsiMethodCallExpression call) {
public class HardcodedContracts {
public static List<MethodContract> getHardcodedContracts(@NotNull PsiMethod method, @Nullable PsiMethodCallExpression call) {
PsiClass owner = method.getContainingClass();
if (owner == null) return Collections.emptyList();
@@ -49,6 +53,13 @@ class HardcodedContracts {
return Collections.singletonList(new MethodContract(constraints, THROW_EXCEPTION));
}
}
else if ("java.util.Objects".equals(className)) {
if ("requireNonNull".equals(methodName) && paramCount > 0) {
MethodContract.ValueConstraint[] constraints = createConstraintArray(paramCount);
constraints[0] = NULL_VALUE;
return Collections.singletonList(new MethodContract(constraints, THROW_EXCEPTION));
}
}
else if ("junit.framework.Assert".equals(className) ||
"org.junit.Assert".equals(className) ||
"junit.framework.TestCase".equals(className) ||
@@ -82,8 +93,8 @@ class HardcodedContracts {
}
private static List<MethodContract> handleTestFrameworks(int paramCount, String className, String methodName,
@NotNull PsiMethodCallExpression call) {
if ("assertThat".equals(methodName)) {
@Nullable PsiMethodCallExpression call) {
if ("assertThat".equals(methodName) && call != null) {
PsiExpression[] args = call.getArgumentList().getExpressions();
if (args.length == paramCount) {
for (int i = 1; i < args.length; i++) {
@@ -34,7 +34,7 @@ import java.util.List;
public class PurityInference {
public static boolean inferPurity(@NotNull final PsiMethod method) {
if (method instanceof PsiCompiledElement || method.getReturnType() == PsiType.VOID || method.getBody() == null) {
if (method instanceof PsiCompiledElement || method.getReturnType() == PsiType.VOID || method.getBody() == null || method.isConstructor()) {
return false;
}
@@ -41,7 +41,10 @@ public class StandardDataFlowRunner extends DataFlowRunner {
private boolean myIsInMethod = false;
public StandardDataFlowRunner(PsiElement block) {
super(block);
this (block, false);
}
public StandardDataFlowRunner(PsiElement block, boolean unknownMembersAreNullable) {
super(block, unknownMembersAreNullable);
}
@Override
@@ -343,7 +343,11 @@ public class StandardInstructionVisitor extends InstructionVisitor {
}
if (type != null && (type instanceof PsiClassType || type.getArrayDimensions() > 0)) {
return factory.createTypeValue(type, myReturnTypeNullability.get(instruction));
Nullness nullability = myReturnTypeNullability.get(instruction);
if (nullability == Nullness.UNKNOWN && factory.isUnknownMembersAreNullable()) {
nullability = Nullness.NULLABLE;
}
return factory.createTypeValue(type, nullability);
}
return DfaUnknownValue.getInstance();
}
@@ -42,9 +42,11 @@ public class DfaValueFactory {
private final Map<Pair<DfaPsiType, DfaPsiType>, Boolean> myConvertibleCache = ContainerUtil.newHashMap();
private final Map<PsiType, DfaPsiType> myDfaTypes = ContainerUtil.newHashMap();
private final boolean myHonorFieldInitializers;
private final boolean myUnknownMembersAreNullable;
public DfaValueFactory(boolean honorFieldInitializers) {
public DfaValueFactory(boolean honorFieldInitializers, boolean unknownMembersAreNullable) {
myHonorFieldInitializers = honorFieldInitializers;
myUnknownMembersAreNullable = unknownMembersAreNullable;
myValues.add(null);
myVarFactory = new DfaVariableValue.Factory(this);
myConstFactory = new DfaConstValue.Factory(this);
@@ -58,6 +60,10 @@ public class DfaValueFactory {
return myHonorFieldInitializers;
}
public boolean isUnknownMembersAreNullable() {
return myUnknownMembersAreNullable;
}
public DfaValue createTypeValue(@Nullable PsiType type, Nullness nullability) {
type = TypeConversionUtil.erasure(type);
if (type == null) return DfaUnknownValue.getInstance();
@@ -165,6 +165,8 @@ public class DfaVariableValue extends DfaValue {
return nullability;
}
Nullness defaultNullability = var instanceof PsiMember && myFactory.isUnknownMembersAreNullable() ? Nullness.NULLABLE : Nullness.UNKNOWN;
if (var instanceof PsiParameter && var.getParent() instanceof PsiForeachStatement) {
PsiExpression iteratedValue = ((PsiForeachStatement)var.getParent()).getIteratedValue();
if (iteratedValue != null) {
@@ -178,7 +180,7 @@ public class DfaVariableValue extends DfaValue {
if (var instanceof PsiField && DfaPsiUtil.isFinalField((PsiVariable)var) && myFactory.isHonorFieldInitializers()) {
List<PsiExpression> initializers = DfaPsiUtil.findAllConstructorInitializers((PsiField)var);
if (initializers.isEmpty()) {
return Nullness.UNKNOWN;
return defaultNullability;
}
boolean hasUnknowns = false;
@@ -204,13 +206,13 @@ public class DfaVariableValue extends DfaValue {
if (DfaPsiUtil.isInitializedNotNull((PsiField)var)) {
return Nullness.NOT_NULL;
}
return Nullness.UNKNOWN;
return defaultNullability;
}
return Nullness.NOT_NULL;
}
return Nullness.UNKNOWN;
return defaultNullability;
}
public boolean isFlushableByCalls() {
@@ -68,6 +68,7 @@ public class DataFlowInspection extends DataFlowInspectionBase {
private final JCheckBox myReportConstantReferences;
private final JCheckBox mySuggestNullables;
private final JCheckBox myDontReportTrueAsserts;
private final JCheckBox myTreatUnknownMembersAsNullable;
private OptionsPanel() {
super(new GridBagLayout());
@@ -116,6 +117,15 @@ public class DataFlowInspection extends DataFlowInspectionBase {
}
});
myTreatUnknownMembersAsNullable = new JCheckBox("Treat non-annotated methods and fields as @Nullable");
myTreatUnknownMembersAsNullable.setSelected(TREAT_UNKNOWN_MEMBERS_AS_NULLABLE);
myTreatUnknownMembersAsNullable.getModel().addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
TREAT_UNKNOWN_MEMBERS_AS_NULLABLE = myTreatUnknownMembersAsNullable.isSelected();
}
});
gc.insets = new Insets(0, 0, 0, 0);
gc.gridy = 0;
add(mySuggestNullables, gc);
@@ -167,6 +177,9 @@ public class DataFlowInspection extends DataFlowInspectionBase {
gc.gridy++;
add(myReportConstantReferences, gc);
gc.gridy++;
add(myTreatUnknownMembersAsNullable, gc);
}
}
@@ -1557,6 +1557,11 @@
<item name="java.lang.System void checkKey(java.lang.String) 0">
<annotation name="org.jetbrains.annotations.NotNull"/>
</item>
<item name="java.lang.System void exit(int)">
<annotation name="org.jetbrains.annotations.Contract">
<val val="&quot;_ -&gt; fail&quot;"/>
</annotation>
</item>
<item name="java.lang.System void setProperties(java.util.Properties) 0">
<annotation name="org.jetbrains.annotations.Nullable"/>
</item>
@@ -0,0 +1,22 @@
import org.jetbrains.annotations.NotNull;
class Test {
Object o;
void field() {
<warning descr="Method invocation 'o.hashCode()' may produce 'java.lang.NullPointerException'">o.hashCode()</warning>;
}
void callUnknownMethod() {
<warning descr="Method invocation 'unknownObject().hashCode()' may produce 'java.lang.NullPointerException'">unknownObject().hashCode()</warning>;
}
void callNotNullMethod() {
knownObject().hashCode();
}
native Object unknownObject();
@NotNull
native Object knownObject();
}
@@ -54,6 +54,12 @@ public class LossyEncodingTest extends DaemonAnalyzerTestCase {
return new LocalInspectionTool[]{new LossyEncodingInspection()};
}
@Override
protected void tearDown() throws Exception {
UIUtil.dispatchAllInvocationEvents(); // invokeLater() in EncodingProjectManagerImpl.reloadAllFilesUnder()
super.tearDown();
}
public void testText() throws Exception {
doTest("Text.txt");
Charset ascii = CharsetToolkit.forName("US-ASCII");
@@ -115,6 +121,7 @@ public class LossyEncodingTest extends DaemonAnalyzerTestCase {
private void doTest(@NonNls String filePath) throws Exception {
doTest(BASE_PATH + "/" + filePath, true, false);
UIUtil.dispatchAllInvocationEvents();
}
public void testNativeEncoding() throws Exception {
@@ -137,6 +137,13 @@ public class DataFlowInspectionTest extends LightCodeInsightFixtureTestCase {
myFixture.testHighlighting(true, false, true, getTestName(false) + ".java");
}
public void testParanoidMode() {
final DataFlowInspection inspection = new DataFlowInspection();
inspection.TREAT_UNKNOWN_MEMBERS_AS_NULLABLE = true;
myFixture.enableInspections(inspection);
myFixture.testHighlighting(true, false, true, getTestName(false) + ".java");
}
public void testReportConstantReferences() {
doTestReportConstantReferences();
myFixture.launchAction(myFixture.findSingleIntention("Replace with 'null'"));
@@ -129,6 +129,13 @@ int smthPure() { return 3; }
"""
}
public void "test don't analyze constructors"() {
assertPure false, """
public Foo() {
}
"""
}
private void assertPure(boolean expected, String classBody) {
def clazz = myFixture.addClass("final class Foo { $classBody }")
assert expected == PurityInference.inferPurity(clazz.methods[0])
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -75,4 +75,9 @@ public class DaemonProgressIndicator extends AbstractProgressIndicatorBase {
public final int hashCode() {
return super.hashCode();
}
@Override
public String toString() {
return super.toString() + (debug ? "; "+myTraceableDisposable.getStackTrace()+"\n;" : "");
}
}
@@ -21,6 +21,7 @@ import com.intellij.codeHighlighting.Pass;
import com.intellij.codeHighlighting.TextEditorHighlightingPassRegistrar;
import com.intellij.codeInsight.daemon.ProblemHighlightFilter;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
@@ -28,8 +29,10 @@ 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.openapi.util.text.StringUtil;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.util.ConcurrencyUtil;
import com.intellij.util.containers.WeakHashMap;
import gnu.trove.TIntObjectHashMap;
import gnu.trove.TIntObjectProcedure;
@@ -39,7 +42,10 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class FileStatusMap implements Disposable {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.FileStatusMap");
@@ -367,4 +373,27 @@ public class FileStatusMap implements Disposable {
}
};
// logging
private static final ConcurrentMap<Thread, Integer> threads = new ConcurrentHashMap<Thread, Integer>();
private static int getThreadNum() {
return ConcurrencyUtil.cacheOrGet(threads, Thread.currentThread(), threads.size());
}
private static final StringBuffer log = new StringBuffer();
private static final boolean IN_TESTS = ApplicationManager.getApplication().isUnitTestMode();
static void log(@NonNls Object... info) {
if (IN_TESTS) {
if (log.length() > 10000) {
log.replace(0, log.length()-5000, "");
}
String s = StringUtil.repeatSymbol(' ', getThreadNum() * 4) + Arrays.asList(info) + "\n";
log.append(s);
}
}
@NotNull
public static String getAndClearLog() {
String l = log.toString();
log.setLength(0);
return l;
}
}
@@ -417,7 +417,7 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
@Override
public void run() {
Application application = ApplicationManager.getApplication();
if (!project.isDisposed() && !application.isDisposed()) {
if (!project.isDisposed() && !application.isDisposed() && !application.isUnitTestMode()) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
@@ -46,6 +46,7 @@ import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.TextEditor;
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.FileTypeManager;
@@ -283,7 +284,7 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzerEx implements JDOM
if (application.isWriteAccessAllowed()) {
throw new AssertionError("Must not start highlighting from within write action, or deadlock is imminent");
}
DaemonProgressIndicator.setDebug(true);
((FileTypeManagerImpl)FileTypeManager.getInstance()).drainReDetectQueue();
// pump first so that queued event do not interfere
UIUtil.dispatchAllInvocationEvents();
@@ -297,6 +298,7 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzerEx implements JDOM
Project project = file.getProject();
setUpdateByTimerEnabled(false);
FileStatusMap.getAndClearLog();
FileStatusMap fileStatusMap = getFileStatusMap();
fileStatusMap.allowDirt(canChangeDocument);
@@ -342,8 +344,15 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzerEx implements JDOM
return getHighlights(document, null, project);
}
finally {
DaemonProgressIndicator.setDebug(false);
String log = FileStatusMap.getAndClearLog();
fileStatusMap.allowDirt(true);
waitForTermination();
try {
waitForTermination();
}
catch (Throwable e) {
LOG.error(log, e);
}
}
}
@@ -741,7 +750,7 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzerEx implements JDOM
if (ApplicationManager.getApplication().isWriteAccessAllowed()) {
// makes no sense to start from within write action, will cancel anyway
// we'll restart when write action finish
// we'll restart when the write action finish
return;
}
if (documentManager.hasUncommitedDocuments()) {
@@ -811,28 +820,30 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzerEx implements JDOM
// Editors in modal context
List<Editor> editors = getActiveEditors();
Collection<FileEditor> activeFileEditors = new THashSet<FileEditor>(editors.size());
Collection<FileEditor> activeTextEditors = new THashSet<FileEditor>(editors.size());
for (Editor editor : editors) {
TextEditor textEditor = TextEditorProvider.getInstance().getTextEditor(editor);
activeFileEditors.add(textEditor);
activeTextEditors.add(textEditor);
}
if (ApplicationManager.getApplication().getCurrentModalityState() != ModalityState.NON_MODAL) {
return activeFileEditors;
return activeTextEditors;
}
// Editors in tabs.
Collection<FileEditor> result = new THashSet<FileEditor>();
Collection<Document> documents = new THashSet<Document>(activeFileEditors.size());
Collection<VirtualFile> files = new THashSet<VirtualFile>(activeTextEditors.size());
final FileEditor[] tabEditors = FileEditorManager.getInstance(myProject).getSelectedEditors();
for (FileEditor tabEditor : tabEditors) {
if (tabEditor instanceof TextEditor) {
documents.add(((TextEditor)tabEditor).getEditor().getDocument());
VirtualFile file = ((FileEditorManagerEx)FileEditorManager.getInstance(myProject)).getFile(tabEditor);
if (file != null) {
files.add(file);
}
result.add(tabEditor);
}
// do not duplicate documents
for (FileEditor fileEditor : activeFileEditors) {
if (fileEditor instanceof TextEditor && documents.contains(((TextEditor)fileEditor).getEditor().getDocument())) continue;
for (FileEditor fileEditor : activeTextEditors) {
VirtualFile file = ((FileEditorManagerEx)FileEditorManager.getInstance(myProject)).getFile(fileEditor);
if (file != null && files.contains(file)) continue;
result.add(fileEditor);
}
return result;
@@ -62,7 +62,9 @@ abstract public class LiveTemplateLookupElement extends LookupElement {
if (shortcut == TemplateSettings.DEFAULT_CHAR) {
shortcut = TemplateSettings.getInstance().getDefaultShortcutChar();
}
presentation.setTypeText(" [" + KeyEvent.getKeyText(shortcut) + "] ");
if (shortcut != TemplateSettings.CUSTOM_CHAR) {
presentation.setTypeText(" [" + KeyEvent.getKeyText(shortcut) + "] ");
}
}
if (StringUtil.isNotEmpty(myDescription)) {
presentation.setTailText(" (" + myDescription + ")", true);
@@ -27,8 +27,11 @@ import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.keymap.KeymapUtil;
import com.intellij.openapi.keymap.impl.ui.KeymapPanel;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.options.SchemesManager;
import com.intellij.openapi.options.ex.Settings;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.ui.*;
import com.intellij.openapi.ui.popup.JBPopupFactory;
@@ -50,13 +53,14 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.*;
import java.util.List;
@@ -80,9 +84,11 @@ public class TemplateListPanel extends JPanel implements Disposable {
private CheckboxTree myTree;
private final List<TemplateGroup> myTemplateGroups = new ArrayList<TemplateGroup>();
private JComboBox myExpandByCombo;
private HyperlinkLabel myOpenKeymapLabel;
private static final String SPACE = CodeInsightBundle.message("template.shortcut.space");
private static final String TAB = CodeInsightBundle.message("template.shortcut.tab");
private static final String ENTER = CodeInsightBundle.message("template.shortcut.enter");
private static final String CUSTOM = "Custom";
private CheckedTreeNode myTreeRoot = new CheckedTreeNode(null);
@@ -136,16 +142,11 @@ public class TemplateListPanel extends JPanel implements Disposable {
initTemplates(groups, templateSettings.getLastSelectedTemplateGroup(), templateSettings.getLastSelectedTemplateKey());
if (templateSettings.getDefaultShortcutChar() == TemplateSettings.TAB_CHAR) {
myExpandByCombo.setSelectedItem(TAB);
}
else if (templateSettings.getDefaultShortcutChar() == TemplateSettings.ENTER_CHAR) {
myExpandByCombo.setSelectedItem(ENTER);
}
else {
myExpandByCombo.setSelectedItem(SPACE);
}
char shortcutChar = templateSettings.getDefaultShortcutChar();
myExpandByCombo.setSelectedItem(shortcutChar == TemplateSettings.CUSTOM_CHAR ? CUSTOM :
shortcutChar == TemplateSettings.TAB_CHAR ? TAB :
shortcutChar == TemplateSettings.ENTER_CHAR ? ENTER :
SPACE);
UiNotifyConnector.doWhenFirstShown(this, new Runnable() {
@Override
@@ -279,15 +280,10 @@ public class TemplateListPanel extends JPanel implements Disposable {
private char getDefaultShortcutChar() {
Object selectedItem = myExpandByCombo.getSelectedItem();
if (TAB.equals(selectedItem)) {
return TemplateSettings.TAB_CHAR;
}
else if (ENTER.equals(selectedItem)) {
return TemplateSettings.ENTER_CHAR;
}
else {
return TemplateSettings.SPACE_CHAR;
}
if (TAB.equals(selectedItem)) return TemplateSettings.TAB_CHAR;
if (ENTER.equals(selectedItem)) return TemplateSettings.ENTER_CHAR;
if (SPACE.equals(selectedItem)) return TemplateSettings.SPACE_CHAR;
else return TemplateSettings.CUSTOM_CHAR;
}
private List<TemplateGroup> getTemplateGroups() {
@@ -347,16 +343,82 @@ public class TemplateListPanel extends JPanel implements Disposable {
gbConstraints.gridx = 1;
gbConstraints.insets = new Insets(0, 4, 0, 0);
myExpandByCombo = new JComboBox();
myExpandByCombo.addItem(SPACE);
myExpandByCombo.addItem(TAB);
myExpandByCombo.addItem(ENTER);
myExpandByCombo = new ComboBox();
panel.add(myExpandByCombo, gbConstraints);
myOpenKeymapLabel = new HyperlinkLabel("Change");
gbConstraints.gridx = 2;
panel.add(myOpenKeymapLabel, gbConstraints);
gbConstraints.gridx = 3;
gbConstraints.weightx = 1;
panel.add(new JPanel(), gbConstraints);
panel.setBorder(new EmptyBorder(0, 0, 10, 0));
myExpandByCombo.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
myOpenKeymapLabel.setVisible(myExpandByCombo.getSelectedItem() == CUSTOM);
}
});
for (String s : ContainerUtil.ar(SPACE, TAB, ENTER, CUSTOM)) {
//noinspection unchecked
myExpandByCombo.addItem(s);
}
//noinspection unchecked
myExpandByCombo.setRenderer(new ListCellRendererWrapper() {
@Override
public void customize(JList list, Object value, int index, boolean selected, boolean hasFocus) {
if (value == CUSTOM) {
Shortcut[] shortcuts = getCurrentCustomShortcuts();
String shortcutText = shortcuts.length == 0 ? "" : KeymapUtil.getShortcutsText(shortcuts);
setText(StringUtil.isEmpty(shortcutText) ? "Custom..." : "Custom (" + shortcutText + ")");
}
}
private Shortcut[] getCurrentCustomShortcuts() {
Settings allSettings = Settings.KEY.getData(DataManager.getInstance().getDataContext(myOpenKeymapLabel));
KeymapPanel keymapPanel = allSettings == null ? null : allSettings.find(KeymapPanel.class);
Shortcut[] shortcuts = keymapPanel == null ? null : keymapPanel.getCurrentShortcuts(IdeActions.ACTION_EXPAND_LIVE_TEMPLATE_CUSTOM);
if (shortcuts == null) {
Shortcut shortcut = ActionManager.getInstance().getKeyboardShortcut(IdeActions.ACTION_EXPAND_LIVE_TEMPLATE_CUSTOM);
shortcuts = shortcut == null ? Shortcut.EMPTY_ARRAY : new Shortcut[]{shortcut};
}
return shortcuts;
}
});
addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChange(final PropertyChangeEvent evt) {
if (isConfigurableOpenEvent(evt)) {
resizeComboToFitCustomShortcut();
}
}
private boolean isConfigurableOpenEvent(PropertyChangeEvent evt) {
return evt.getPropertyName().equals("ancestor") && evt.getNewValue() != null && evt.getOldValue() == null;
}
private void resizeComboToFitCustomShortcut() {
myExpandByCombo.setPrototypeDisplayValue(null);
myExpandByCombo.setPrototypeDisplayValue(CUSTOM);
}
});
myOpenKeymapLabel.addHyperlinkListener(new HyperlinkAdapter() {
@Override
protected void hyperlinkActivated(HyperlinkEvent e) {
Settings allSettings = Settings.KEY.getData(DataManager.getInstance().getDataContext(myOpenKeymapLabel));
final KeymapPanel keymapPanel = allSettings == null ? null : allSettings.find(KeymapPanel.class);
if (keymapPanel != null) {
allSettings.select(keymapPanel).doWhenDone(new Runnable() {
public void run() {
keymapPanel.selectAction(IdeActions.ACTION_EXPAND_LIVE_TEMPLATE_CUSTOM);
}
});
}
}
});
return panel;
}
@@ -68,10 +68,12 @@ public class TemplateSettings implements PersistentStateComponent<Element>, Expo
public static final char TAB_CHAR = '\t';
public static final char ENTER_CHAR = '\n';
public static final char DEFAULT_CHAR = 'D';
public static final char CUSTOM_CHAR = 'C';
@NonNls private static final String SPACE = "SPACE";
@NonNls private static final String TAB = "TAB";
@NonNls private static final String ENTER = "ENTER";
@NonNls private static final String CUSTOM = "CUSTOM";
@NonNls private static final String NAME = "name";
@NonNls private static final String VALUE = "value";
@@ -265,13 +267,10 @@ public class TemplateSettings implements PersistentStateComponent<Element>, Expo
Element element = parentNode.getChild(DEFAULT_SHORTCUT);
if (element != null) {
String shortcut = element.getAttributeValue(SHORTCUT);
if (TAB.equals(shortcut)) {
myDefaultShortcutChar = TAB_CHAR;
} else if (ENTER.equals(shortcut)) {
myDefaultShortcutChar = ENTER_CHAR;
} else {
myDefaultShortcutChar = SPACE_CHAR;
}
myDefaultShortcutChar = TAB.equals(shortcut) ? TAB_CHAR :
ENTER.equals(shortcut) ? ENTER_CHAR :
CUSTOM.equals(shortcut) ? CUSTOM_CHAR :
SPACE_CHAR;
}
ExportableTemplateSettings exportableSettings = ServiceManager.getService(ExportableTemplateSettings.class);
@@ -313,13 +312,10 @@ public class TemplateSettings implements PersistentStateComponent<Element>, Expo
public Element getState() {
Element parentNode = new Element("TemplateSettings");
Element element = new Element(DEFAULT_SHORTCUT);
if (myDefaultShortcutChar == TAB_CHAR) {
element.setAttribute(SHORTCUT, TAB);
} else if (myDefaultShortcutChar == ENTER_CHAR) {
element.setAttribute(SHORTCUT, ENTER);
} else {
element.setAttribute(SHORTCUT, SPACE);
}
element.setAttribute(SHORTCUT, myDefaultShortcutChar == TAB_CHAR ? TAB :
myDefaultShortcutChar == ENTER_CHAR ? ENTER :
myDefaultShortcutChar == CUSTOM_CHAR ? CUSTOM :
SPACE);
parentNode.addContent(element);
return parentNode;
@@ -15,39 +15,15 @@
*/
package com.intellij.codeInsight.template.impl.editorActions;
import com.intellij.codeInsight.template.TemplateManager;
import com.intellij.codeInsight.template.impl.TemplateManagerImpl;
import com.intellij.codeInsight.template.impl.TemplateSettings;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Caret;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.actionSystem.EditorAction;
import com.intellij.openapi.editor.actionSystem.EditorWriteActionHandler;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiDocumentManager;
import org.jetbrains.annotations.Nullable;
/**
* @author peter
*/
public class ExpandLiveTemplateByTabAction extends EditorAction {
public ExpandLiveTemplateByTabAction() {
super(new EditorWriteActionHandler(true) {
@Override
public void executeWriteAction(Editor editor, @Nullable Caret caret, DataContext dataContext) {
Project project = editor.getProject();
assert project != null;
PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument());
TemplateManager.getInstance(project).startTemplate(editor, TemplateSettings.TAB_CHAR);
}
@Override
public boolean isEnabled(Editor editor, DataContext dataContext) {
Project project = editor.getProject();
return project != null &&
((TemplateManagerImpl)TemplateManager.getInstance(project)).prepareTemplate(editor, TemplateSettings.TAB_CHAR, null) != null;
}
});
super(ExpandLiveTemplateCustomAction.createExpandTemplateHandler(TemplateSettings.TAB_CHAR));
setInjectedContext(true);
}
}
@@ -0,0 +1,58 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* 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.codeInsight.template.impl.editorActions;
import com.intellij.codeInsight.template.TemplateManager;
import com.intellij.codeInsight.template.impl.TemplateManagerImpl;
import com.intellij.codeInsight.template.impl.TemplateSettings;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Caret;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.actionSystem.EditorAction;
import com.intellij.openapi.editor.actionSystem.EditorWriteActionHandler;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiDocumentManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author peter
*/
public class ExpandLiveTemplateCustomAction extends EditorAction {
public ExpandLiveTemplateCustomAction() {
super(createExpandTemplateHandler(TemplateSettings.CUSTOM_CHAR));
setInjectedContext(true);
}
static EditorWriteActionHandler createExpandTemplateHandler(final char shortcutChar) {
return new EditorWriteActionHandler(true) {
@Override
public void executeWriteAction(Editor editor, @Nullable Caret caret, DataContext dataContext) {
Project project = editor.getProject();
assert project != null;
PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument());
TemplateManager.getInstance(project).startTemplate(editor, shortcutChar);
}
@Override
protected boolean isEnabledForCaret(@NotNull Editor editor, @NotNull Caret caret, DataContext dataContext) {
Project project = editor.getProject();
return project != null &&
((TemplateManagerImpl)TemplateManager.getInstance(project)).prepareTemplate(editor, shortcutChar, null) != null;
}
};
}
}
@@ -16,6 +16,7 @@
package com.intellij.psi.impl;
import com.intellij.codeInsight.daemon.impl.DaemonProgressIndicator;
import com.intellij.diagnostic.ThreadDumper;
import com.intellij.ide.startup.impl.StartupManagerImpl;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationAdapter;
@@ -51,6 +52,7 @@ import java.util.List;
public class DocumentCommitThread extends DocumentCommitProcessor implements Runnable, Disposable {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.DocumentCommitThread");
private static final String NAME = "Document commit thread";
private final Queue<CommitTask> documentsToCommit = new Queue<CommitTask>(10);
private final List<CommitTask> documentsToApplyInEDT = new ArrayList<CommitTask>(10); // guarded by documentsToCommit
@@ -59,32 +61,49 @@ public class DocumentCommitThread extends DocumentCommitProcessor implements Run
private CommitTask currentTask; // guarded by documentsToCommit
private volatile boolean threadFinished;
private volatile boolean myEnabled; // true if we can do commits. set to false temporarily during the write action.
private int runningWriteActions; // accessed in EDT only
public static DocumentCommitThread getInstance() {
return ServiceManager.getService(DocumentCommitThread.class);
}
public DocumentCommitThread(final ApplicationEx application) {
myApplication = application;
// install listener in EDT to avoid missing events in case we are inside write action right now
application.invokeLater(new Runnable() {
@Override
public void run() {
assert runningWriteActions == 0;
if (application.isDisposed()) return;
assert !application.isWriteAccessAllowed();
application.addApplicationListener(new ApplicationAdapter() {
private int runningWriteActions;
@Override
public void beforeWriteActionStart(Object action) {
if (runningWriteActions++ == 0) {
int writeActionsBefore = runningWriteActions++;
if (writeActionsBefore == 0) {
disable("Write action started: " + action);
}
else {
log("before write action: " + action + "; " + writeActionsBefore + " write actions already running", null, false);
}
}
@Override
public void writeActionFinished(Object action) {
if (--runningWriteActions == 0) {
// crazy things happen when running tests, like starting write action in one thread but firing its end in the other
int writeActionsAfter = runningWriteActions = Math.max(0,runningWriteActions-1);
if (writeActionsAfter == 0) {
enable("Write action finished: " + action);
}
else {
log("after write action: " + action + "; " + writeActionsAfter + " write actions still running", null, false);
if (writeActionsAfter < 0) {
System.err.println("mismatched listeners: " + writeActionsAfter + ";\n==== log==="+log+"\n====end log==="+
";\n=======threaddump====\n" +
ThreadDumper.dumpThreadsToString()+"\n=====END threaddump=======");
clearLog();
assert false;
}
}
}
}, DocumentCommitThread.this);
@@ -92,7 +111,8 @@ public class DocumentCommitThread extends DocumentCommitProcessor implements Run
}
});
log("Starting thread", null, false);
new Thread(this, "Document commit thread").start();
Thread thread = new Thread(this, NAME);
thread.start();
}
@Override
@@ -169,18 +189,20 @@ public class DocumentCommitThread extends DocumentCommitProcessor implements Run
}
}
private final StringBuilder log = new StringBuilder();
final StringBuilder log = new StringBuilder();
@Override
public void log(@NonNls String msg, @Nullable CommitTask task, boolean synchronously, @NonNls Object... args) {
if (true) return;
String indent = new SimpleDateFormat("mm:ss:SSSS").format(new Date()) +
(SwingUtilities.isEventDispatchThread() ? "- " : Thread.currentThread().getName().equals("Document commit thread") ? "- >" : "-");
String indent = new SimpleDateFormat("hh:mm:ss:SSSS").format(new Date()) +
(SwingUtilities.isEventDispatchThread() ? "-(EDT) " :
Thread.currentThread().getName().equals(NAME) ? "-(DCT) " :
"- ");
@NonNls
String s = indent +
msg + (synchronously ? " (sync)" : "") +
(task == null ? "" : "; task: " + task+" ("+System.identityHashCode(task)+")");
(task == null ? " - " : "; task: " + task+" ("+System.identityHashCode(task)+")");
for (Object arg : args) {
if (!StringUtil.isEmpty(String.valueOf(arg))) {
@@ -195,11 +217,13 @@ public class DocumentCommitThread extends DocumentCommitProcessor implements Run
}
}
System.err.println(s);
// System.err.println(s);
log.append(s).append("\n");
if (log.length() > 1000000) {
log.delete(0, 1000000);
synchronized (log) {
log.append(s).append("\n");
if (log.length() > 100000) {
log.delete(0, log.length()-50000);
}
}
}
@@ -219,10 +243,16 @@ public class DocumentCommitThread extends DocumentCommitProcessor implements Run
@TestOnly
public void clearQueue() {
cancelAll();
log.setLength(0);
clearLog();
wakeUpQueue();
}
private void clearLog() {
synchronized (log) {
log.setLength(0);
}
}
private void markRemovedCurrentTask(@Nullable CommitTask newTask) {
CommitTask task = currentTask;
if (task != null && (newTask == null || task.equals(newTask))) {
@@ -285,7 +315,7 @@ public class DocumentCommitThread extends DocumentCommitProcessor implements Run
ProgressIndicator indicator;
synchronized (documentsToCommit) {
if (!myEnabled || documentsToCommit.isEmpty()) {
documentsToCommit.wait();
documentsToCommit.wait(1000);
return;
}
task = documentsToCommit.pullFirst();
@@ -554,4 +584,9 @@ public class DocumentCommitThread extends DocumentCommitProcessor implements Run
boolean isEnabled() {
return myEnabled;
}
@Override
public String toString() {
return "Document commit thread; application: "+myApplication+"; isDisposed: "+isDisposed+"; threadFinished: "+threadFinished+"; myEnabled: "+myEnabled+"; runningWriteActions: "+runningWriteActions;
}
}
@@ -82,6 +82,7 @@ public interface IdeActions {
@NonNls String ACTION_CHOOSE_LOOKUP_ITEM_COMPLETE_STATEMENT = "EditorChooseLookupItemCompleteStatement";
@NonNls String ACTION_CHOOSE_LOOKUP_ITEM_DOT = "EditorChooseLookupItemDot";
@NonNls String ACTION_EXPAND_LIVE_TEMPLATE_BY_TAB = "ExpandLiveTemplateByTab";
@NonNls String ACTION_EXPAND_LIVE_TEMPLATE_CUSTOM = "ExpandLiveTemplateCustom";
@NonNls String ACTION_LOOKUP_UP = "EditorLookupUp";
@NonNls String ACTION_LOOKUP_DOWN = "EditorLookupDown";
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,7 +24,7 @@ import java.util.Queue;
@SomeQueue
public abstract class AbstractTaskQueue<T> {
private final static Logger LOG = Logger.getInstance("#com.intellij.openapi.progress.AbstractTaskQueue");
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.progress.AbstractTaskQueue");
private final Object myLock;
private final Queue<T> myQueue;
@@ -66,6 +66,7 @@ public abstract class AbstractTaskQueue<T> {
}
private class MyWorker implements Runnable {
@Override
public void run() {
while (true) {
try {
@@ -77,17 +78,24 @@ public abstract class AbstractTaskQueue<T> {
// each task is executed only once, once it has been taken from the queue..
runStuff(stuff);
}
} catch (Throwable t) {
}
catch (Throwable t) {
LOG.info(t);
} finally {
synchronized (myLock) {
if (myQueue.isEmpty()) {
myActive = false;
return;
}
}
}
finally {
if (isEmpty()) return;
}
}
}
}
public boolean isEmpty() {
synchronized (myLock) {
if (myQueue.isEmpty()) {
myActive = false;
return true;
}
}
return false;
}
}
@@ -2768,7 +2768,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
effectType, fontType, currentColor, logicalPosition);
final VirtualFile file = getVirtualFile();
if (myProject != null && file != null && !isOneLineMode()) {
int offset = lIterator.getStart();
int offset = position.x;
String additionalText = "";
for (EditorLinePainter painter : EditorLinePainter.EP_NAME.getExtensions()) {
Collection<LineExtensionInfo> extensions = painter.getLineExtensions(myProject, file, lIterator.getLineNumber());
@@ -2785,7 +2785,10 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
}
}
}
myLinePaintersWidth = Math.max(myLinePaintersWidth, position.x);
for (char ch : additionalText.toCharArray()) {
offset += EditorUtil.charWidth(ch, Font.ITALIC, this);
}
myLinePaintersWidth = Math.max(myLinePaintersWidth, offset);
}
position.x = 0;
@@ -232,7 +232,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec
fm.doWhenFocusSettlesDown(run);
}
else {
UIUtil.invokeAndWaitIfNeeded(run);
UIUtil.invokeLaterIfNeeded(run);
}
return result;
@@ -66,6 +66,7 @@ import com.intellij.util.ui.UIUtil;
import com.intellij.util.ui.tree.TreeUtil;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
@@ -955,13 +956,18 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
public void dispose() {
}
@Nullable
public Shortcut[] getCurrentShortcuts(String actionId) {
return mySelectedKeymap == null ? null : mySelectedKeymap.getShortcuts(actionId);
}
private void editSelection(InputEvent e) {
final String actionId = myActionsTree.getSelectedActionId();
if (actionId == null) return;
DefaultActionGroup group = new DefaultActionGroup();
final Shortcut[] shortcuts = mySelectedKeymap.getShortcuts(actionId);
final Shortcut[] shortcuts = getCurrentShortcuts(actionId);
final Set<String> abbreviations = AbbreviationManager.getInstance().getAbbreviations(actionId);
final ShortcutRestrictions restrictions = ActionShortcutRestrictions.getForActionId(actionId);
@@ -57,6 +57,7 @@ group.EditorActions.text=Editor Actions
action.EditorChooseLookupItem.text=Choose Lookup Item
action.EditorChooseLookupItemReplace.text=Choose Lookup Item Replace
action.EditorChooseLookupItemCompleteStatement.text=Choose Lookup Item and Invoke Complete Statement
action.EditorChooseLookupItemDot.text=Choose Lookup Item and Insert Dot
action.NextTemplateVariable.text=Next Template Variable or Finish In-Place Refactoring
action.PreviousTemplateVariable.text=Previous Template Variable
action.NextParameter.text=Next Parameter
@@ -497,6 +498,8 @@ action.ClassNameCompletion.text=Class _Name Completion
action.ClassNameCompletion.description=Complete class name and add import for it
action.InsertLiveTemplate.text=Insert Live _Template...
action.InsertLiveTemplate.description=Show popup list of live templates starting with the specified prefix
action.ExpandLiveTemplateCustom.text=Expand Live Template
action.ExpandLiveTemplateCustom.description=Invoke the live template with the prefix typed in the editor
action.SurroundWithLiveTemplate.text=Surround with Live Tem_plate...
action.SurroundWithLiveTemplate.description=Surrounds the selection with one of the template
action.CommentByLineComment.text=Comment with _Line Comment
@@ -8,6 +8,7 @@
<action id="EditorChooseLookupItemDot" class="com.intellij.codeInsight.lookup.impl.actions.ChooseItemAction$ChooseWithDot"/>
<action id="ExpandLiveTemplateByTab" class="com.intellij.codeInsight.template.impl.editorActions.ExpandLiveTemplateByTabAction" use-shortcut-of="EditorTab"/>
<action id="ExpandLiveTemplateCustom" class="com.intellij.codeInsight.template.impl.editorActions.ExpandLiveTemplateCustomAction"/>
<action id="NextTemplateVariable" class="com.intellij.codeInsight.template.impl.actions.NextVariableAction"/>
<action id="PreviousTemplateVariable" class="com.intellij.codeInsight.template.impl.actions.PreviousVariableAction"/>
@@ -25,6 +25,7 @@ import com.intellij.testFramework.PlatformTestUtil;
import com.intellij.util.Alarm;
import com.intellij.util.Function;
import com.intellij.util.Processor;
import com.intellij.util.SystemProperties;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.DoubleArrayList;
import com.intellij.util.containers.Stack;
@@ -34,6 +35,7 @@ import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -49,7 +51,8 @@ public class ProgressIndicatorTest extends LightPlatformTestCase {
fail("Please restore ProgressIndicatorBase.checkCanceled() check!");
}
catch(ProcessCanceledException ex) {
assertTrue("Should have no stackframe", ex.getStackTrace().length == 0);
boolean isInternal = SystemProperties.getBooleanProperty("idea.is.internal", false);
assertTrue("Should have no stackframe", isInternal ? ex.getStackTrace().length != 0 : ex.getStackTrace().length == 0);
}
}
@@ -138,8 +141,8 @@ public class ProgressIndicatorTest extends LightPlatformTestCase {
assertTrue(indicator.isCanceled());
}
public void testThereIsNoDelayBetweenIndicatorCancelAndProgressManagerCheckCanceled() {
for (int i=0; i<1000;i++) {
public void testThereIsNoDelayBetweenIndicatorCancelAndProgressManagerCheckCanceled() throws Throwable {
for (int i=0; i<100;i++) {
final ProgressIndicatorBase indicator = new ProgressIndicatorBase();
List<Thread> threads = ContainerUtil.map(Collections.nCopies(10, ""), new Function<String, Thread>() {
@Override
@@ -151,13 +154,16 @@ public class ProgressIndicatorTest extends LightPlatformTestCase {
@Override
public void run() {
try {
boolean canceled = indicator.isCanceled();
Thread.sleep(new Random().nextInt(100));
indicator.cancel();
ProgressManager.checkCanceled();
fail("checkCanceled() must know about canceled indicator even from different thread");
}
catch (ProcessCanceledException ignored) {
}
catch (Throwable e) {
exception = e;
}
}
}, indicator);
}
@@ -177,7 +183,7 @@ public class ProgressIndicatorTest extends LightPlatformTestCase {
}
});
}
if (exception != null) throw exception;
}
private volatile boolean checkCanceledCalled;
@@ -288,7 +294,6 @@ public class ProgressIndicatorTest extends LightPlatformTestCase {
@Override
public void initStateFrom(@NotNull ProgressIndicator indicator) {
throw new RuntimeException();
}
@NotNull
@@ -15,6 +15,7 @@
*/
package com.intellij.psi.impl;
import com.intellij.diagnostic.ThreadDumper;
import com.intellij.ide.impl.ProjectUtil;
import com.intellij.mock.MockDocument;
import com.intellij.mock.MockPsiFile;
@@ -26,7 +27,7 @@ import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ex.ProjectManagerEx;
import com.intellij.openapi.util.ThrowableComputable;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtil;
@@ -385,6 +386,9 @@ public class PsiDocumentManagerImplTest extends PlatformLangTestCase {
WriteCommandAction.runWriteCommandAction(null, new Runnable() {
@Override
public void run() {
if (commitThread.isEnabled()) {
System.err.println("commitThread: "+commitThread + ";\n"+commitThread.log+";\n"+ThreadDumper.dumpThreadsToString());
}
assertFalse(commitThread.isEnabled());
WriteCommandAction.runWriteCommandAction(null, new Runnable() {
@Override
@@ -451,7 +455,7 @@ public class PsiDocumentManagerImplTest extends PlatformLangTestCase {
WriteCommandAction.runWriteCommandAction(myProject, new ThrowableComputable<Object, Exception>() {
@Override
public Object compute() throws Exception {
VfsUtil.saveText(vFile, StringUtil.repeat("a", FileUtil.LARGE_FOR_CONTENT_LOADING + 1));
VfsUtil.saveText(vFile, StringUtil.repeat("a", FileUtilRt.LARGE_FOR_CONTENT_LOADING + 1));
return null;
}
});
@@ -482,7 +482,10 @@ public abstract class PlatformTestCase extends UsefulTestCase implements DataPro
private void disposeProject(@NotNull CompositeException result) /* throws nothing */ {
try {
DocumentCommitThread.getInstance().clearQueue();
UIUtil.dispatchAllInvocationEvents();
// sometimes SwingUtilities maybe confused about EDT at this point
if (SwingUtilities.isEventDispatchThread()) {
UIUtil.dispatchAllInvocationEvents();
}
}
catch (Exception e) {
result.add(e);
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,10 +16,12 @@
package com.intellij.openapi.util;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
/**
* Traces creation and disposal by storing corresponding stacktraces.
@@ -72,4 +74,11 @@ public class TraceableDisposable {
super.printStackTrace(s);
}
}
@NotNull
public String getStackTrace() {
StringWriter out = new StringWriter();
new DisposalException("").printStackTrace(new PrintWriter(out));
return out.toString();
}
}
@@ -19,7 +19,6 @@ package com.intellij.util.xmlb;
import com.intellij.openapi.util.JDOMUtil;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.xmlb.annotations.*;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
@@ -254,7 +253,7 @@ public class XmlSerializerTest extends TestCase {
}
public static class BeanWithMap {
public Map<String, String> VALUES = new HashMap<String, String>();
public Map<String, String> VALUES = new LinkedHashMap<String, String>();
{
VALUES.put("a", "1");
@@ -302,7 +301,7 @@ public class XmlSerializerTest extends TestCase {
keyAttributeName = "name",
valueAttributeName = "value"
)
public Map<String, String> VALUES = new HashMap<String, String>();
public Map<String, String> VALUES = new LinkedHashMap<String, String>();
{
VALUES.put("a", "1");
@@ -336,7 +335,7 @@ public class XmlSerializerTest extends TestCase {
public static class BeanWithMapWithBeanValue {
public Map<String, BeanWithProperty> VALUES = new HashMap<String, BeanWithProperty>();
public Map<String, BeanWithProperty> VALUES = new LinkedHashMap<String, BeanWithProperty>();
}
public void testMapWithBeanValue() {
@@ -519,7 +518,7 @@ public class XmlSerializerTest extends TestCase {
Element element = serialize(bean, null);
Element node = (Element)element.getChildren().get(0);
Element node = element.getChildren().get(0);
element.removeContent(node);
element.addContent(node);
@@ -1007,15 +1006,15 @@ public class XmlSerializerTest extends TestCase {
}
public static class BeanWithSetKeysInMap {
public Map<Collection<String>, String> myMap = new HashMap<Collection<String>, String>();
public Map<Collection<String>, String> myMap = new LinkedHashMap<Collection<String>, String>();
}
public void testSetKeysInMap() {
final BeanWithSetKeysInMap bean = new BeanWithSetKeysInMap();
bean.myMap.put(new HashSet<String>(Arrays.asList("1", "2", "3")), "numbers");
bean.myMap.put(new HashSet<String>(Arrays.asList("a", "b", "c")), "letters");
bean.myMap.put(new LinkedHashSet<String>(Arrays.asList("a", "b", "c")), "letters");
bean.myMap.put(new LinkedHashSet<String>(Arrays.asList("1", "2", "3")), "numbers");
BeanWithSetKeysInMap bb = (BeanWithSetKeysInMap)doSerializerTest(
BeanWithSetKeysInMap bb = doSerializerTest(
"<BeanWithSetKeysInMap>\n" +
" <option name=\"myMap\">\n" +
" <map>\n" +
@@ -1087,7 +1086,7 @@ public class XmlSerializerTest extends TestCase {
public static class BeanWithMapWithoutSurround {
@Tag("map")
@MapAnnotation(surroundWithTag = false, entryTagName = "pair", surroundKeyWithTag = false, surroundValueWithTag = false)
public Map<BeanWithPublicFields, BeanWithTextAnnotation> MAP = new HashMap<BeanWithPublicFields, BeanWithTextAnnotation>();
public Map<BeanWithPublicFields, BeanWithTextAnnotation> MAP = new LinkedHashMap<BeanWithPublicFields, BeanWithTextAnnotation>();
}
public void testMapWithNotSurroundingKeyAndValue() {
@@ -1137,7 +1136,7 @@ public class XmlSerializerTest extends TestCase {
public static class BeanWithMapAtTopLevel {
@Property(surroundWithTag = false)
@MapAnnotation(surroundWithTag = false, surroundKeyWithTag = false, surroundValueWithTag = false)
public Map<String, String> map = new HashMap<String, String>();
public Map<String, String> map = new LinkedHashMap<String, String>();
public String option;
}
@@ -1212,13 +1211,14 @@ public class XmlSerializerTest extends TestCase {
return assertSerializer(bean, expected, "Serialization failure", filter);
}
private static Object doSerializerTest(@Language("XML") String expectedText, Object bean) {
private static <T> T doSerializerTest(@Language("XML") String expectedText, T bean) {
try {
Element element = assertSerializer(bean, expectedText, null);
//test deserializer
Object o = XmlSerializer.deserialize(element, bean.getClass());
Class<T> aClass = (Class<T>)bean.getClass();
T o = XmlSerializer.deserialize(element, aClass);
assertSerializer(o, expectedText, "Deserialization failure", null);
return o;
}
@@ -1247,14 +1247,14 @@ public class XmlSerializerTest extends TestCase {
public static class BeanWithMapWithSetValue {
@MapAnnotation(entryTagName = "entry-tag", keyAttributeName = "key-attr", surroundWithTag = false)
public Map<String, Set<String>> myValues = new HashMap<String, Set<String>>();
public Map<String, Set<String>> myValues = new LinkedHashMap<String, Set<String>>();
}
public void testBeanWithMapWithSetValue() {
BeanWithMapWithSetValue bean = new BeanWithMapWithSetValue();
bean.myValues.put("a", ContainerUtil.newHashSet("first1", "second1"));
bean.myValues.put("b", ContainerUtil.newHashSet("first2", "second2"));
bean.myValues.put("a", new LinkedHashSet<String>(Arrays.asList("first1", "second1")));
bean.myValues.put("b", new LinkedHashSet<String>(Arrays.asList("first2", "second2")));
doSerializerTest(
"<BeanWithMapWithSetValue>\n" +
+1 -1
View File
@@ -419,7 +419,7 @@
<lang.importOptimizer language="Python" implementationClass="com.jetbrains.python.codeInsight.imports.PyImportOptimizer"/>
<codeStyleSettingsProvider implementation="com.jetbrains.python.formatter.PyCodeStyleSettingsProvider"/>
<!--<codeStyleSettingsProvider implementation="com.jetbrains.python.formatter.PyCodeStyleSettingsProvider"/>-->
<langCodeStyleSettingsProvider implementation="com.jetbrains.python.formatter.PyLanguageCodeStyleSettingsProvider"/>
<lang.elementManipulator forClass="com.jetbrains.python.psi.PyStringLiteralExpression"
@@ -8,6 +8,7 @@
<action id="EditorChooseLookupItemDot" class="com.intellij.codeInsight.lookup.impl.actions.ChooseItemAction$ChooseWithDot"/>
<action id="ExpandLiveTemplateByTab" class="com.intellij.codeInsight.template.impl.editorActions.ExpandLiveTemplateByTabAction" use-shortcut-of="EditorTab"/>
<action id="ExpandLiveTemplateCustom" class="com.intellij.codeInsight.template.impl.editorActions.ExpandLiveTemplateCustomAction"/>
<action id="NextTemplateVariable" class="com.intellij.codeInsight.template.impl.actions.NextVariableAction"/>
<action id="PreviousTemplateVariable" class="com.intellij.codeInsight.template.impl.actions.PreviousVariableAction"/>