mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
+56
-14
@@ -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()]);
|
||||
|
||||
+4
-2
@@ -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);
|
||||
}
|
||||
|
||||
+11
-5
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+5
-1
@@ -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;
|
||||
|
||||
+5
-1
@@ -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() {
|
||||
|
||||
+16
-5
@@ -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++) {
|
||||
|
||||
+1
-1
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+4
-1
@@ -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
|
||||
|
||||
+5
-1
@@ -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();
|
||||
}
|
||||
|
||||
+7
-1
@@ -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();
|
||||
|
||||
+5
-3
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
@@ -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=""_ -> fail""/>
|
||||
</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'"));
|
||||
|
||||
+7
@@ -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])
|
||||
|
||||
+6
-1
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -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() {
|
||||
|
||||
+22
-11
@@ -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;
|
||||
|
||||
+3
-1
@@ -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);
|
||||
|
||||
+88
-26
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+10
-14
@@ -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;
|
||||
|
||||
+1
-25
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+58
@@ -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;
|
||||
|
||||
+1
-1
@@ -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"/>
|
||||
|
||||
+11
-6
@@ -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
|
||||
|
||||
+6
-2
@@ -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" +
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
Reference in New Issue
Block a user