diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/DebuggerTreeRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/DebuggerTreeRenderer.java index f307ac919ddb..74d0410d7fa0 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/DebuggerTreeRenderer.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/DebuggerTreeRenderer.java @@ -27,7 +27,11 @@ import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.*; import com.intellij.util.PlatformIcons; +import com.intellij.xdebugger.XDebugSession; +import com.intellij.xdebugger.XDebuggerManager; +import com.intellij.xdebugger.impl.XDebugSessionImpl; import com.intellij.xdebugger.impl.ui.DebuggerUIUtil; +import com.intellij.xdebugger.impl.ui.XDebugSessionTab; import com.intellij.xdebugger.impl.ui.XDebuggerUIConstants; import com.intellij.xdebugger.impl.ui.tree.ValueMarkup; import org.jetbrains.annotations.NotNull; @@ -128,6 +132,18 @@ public class DebuggerTreeRenderer extends ColoredTreeCellRenderer { nodeIcon = AllIcons.Debugger.Value; } } + + // if watches in variables enabled, always use watch icon + if (valueDescriptor instanceof WatchItemDescriptor && nodeIcon != AllIcons.Debugger.Watch) { + XDebugSession session = XDebuggerManager.getInstance(valueDescriptor.getProject()).getCurrentSession(); + if (session != null) { + XDebugSessionTab tab = ((XDebugSessionImpl)session).getSessionTab(); + if (tab != null && tab.isWatchesInVariables()) { + nodeIcon = AllIcons.Debugger.Watch; + } + } + } + final Icon valueIcon = valueDescriptor.getValueIcon(); if (nodeIcon != null && valueIcon != null) { nodeIcon = new RowIcon(nodeIcon, valueIcon); diff --git a/java/execution/impl/src/com/intellij/execution/JavaTestFrameworkRunnableState.java b/java/execution/impl/src/com/intellij/execution/JavaTestFrameworkRunnableState.java index f5ce0f3e0442..f07cb5301613 100644 --- a/java/execution/impl/src/com/intellij/execution/JavaTestFrameworkRunnableState.java +++ b/java/execution/impl/src/com/intellij/execution/JavaTestFrameworkRunnableState.java @@ -369,7 +369,7 @@ public abstract class JavaTestFrameworkRunnableState> perModule) throws FileNotFoundException, UnsupportedEncodingException, CantRunException { - if (perModule != null && perModule.size() > 1) { + if (perModule != null) { final String classpath = getScope() == TestSearchScope.WHOLE_PROJECT ? null : javaParameters.getClassPath().getPathsString(); diff --git a/java/java-analysis-impl/src/com/intellij/refactoring/extractMethod/ParametersFolder.java b/java/java-analysis-impl/src/com/intellij/refactoring/extractMethod/ParametersFolder.java index 50a5e91903d0..b7e0e48f8c4b 100644 --- a/java/java-analysis-impl/src/com/intellij/refactoring/extractMethod/ParametersFolder.java +++ b/java/java-analysis-impl/src/com/intellij/refactoring/extractMethod/ParametersFolder.java @@ -118,15 +118,12 @@ public class ParametersFolder { PsiExpression mostRanked = null; for (int i = mentionedInExpressions.size() - 1; i >= 0; i--) { PsiExpression expression = mentionedInExpressions.get(i); - if (expression instanceof PsiArrayAccessExpression) { - mostRanked = expression; - if (!isConditional(expression, scope)) { - myFoldingSelectedByDefault = true; - break; - } + boolean arrayAccess = expression instanceof PsiArrayAccessExpression && !isConditional(expression, scope); + if (arrayAccess) { + myFoldingSelectedByDefault = true; } final int r = findUsedVariables(data, inputVariables, expression).size(); - if (currentRank < r) { + if (currentRank < r || arrayAccess && currentRank == r) { currentRank = r; mostRanked = expression; } diff --git a/java/java-analysis-impl/src/com/intellij/refactoring/util/VariableData.java b/java/java-analysis-impl/src/com/intellij/refactoring/util/VariableData.java index d367ceb0e768..5c7d95416bb1 100644 --- a/java/java-analysis-impl/src/com/intellij/refactoring/util/VariableData.java +++ b/java/java-analysis-impl/src/com/intellij/refactoring/util/VariableData.java @@ -15,7 +15,10 @@ */ package com.intellij.refactoring.util; -import com.intellij.psi.*; +import com.intellij.psi.LambdaUtil; +import com.intellij.psi.PsiType; +import com.intellij.psi.PsiVariable; +import com.intellij.psi.SmartTypePointerManager; import com.intellij.psi.search.GlobalSearchScope; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -32,7 +35,7 @@ public class VariableData extends AbstractVariableData { public VariableData(@Nullable PsiVariable var, PsiType type) { variable = var; if (var != null) { - if (type instanceof PsiLambdaParameterType || type instanceof PsiLambdaExpressionType || type instanceof PsiMethodReferenceType) { + if (LambdaUtil.notInferredType(type)) { type = PsiType.getJavaLangObject(var.getManager(), GlobalSearchScope.allScope(var.getProject())); } this.type = SmartTypePointerManager.getInstance(var.getProject()).createSmartTypePointer(type).getType(); diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateConstructorParameterFromFieldFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateConstructorParameterFromFieldFix.java index 5ef6d1737af4..97bb45cb531c 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateConstructorParameterFromFieldFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateConstructorParameterFromFieldFix.java @@ -125,7 +125,7 @@ public class CreateConstructorParameterFromFieldFix implements IntentionAction { } } }); - final List cleanupElements = new ArrayList(); + final List> cleanupElements = new ArrayList<>(); final List filtered = filterConstructorsIfFieldAlreadyAssigned(constructors, getField()); if (filtered.size() > 1) { final PsiMethodMember[] members = new PsiMethodMember[filtered.size()]; @@ -188,7 +188,7 @@ public class CreateConstructorParameterFromFieldFix implements IntentionAction { fieldsToFix.clear(); } } - GlobalInspectionContextBase.cleanupElements(project, null, cleanupElements.toArray(new PsiElement[cleanupElements.size()])); + GlobalInspectionContextBase.cleanupElements(project, null, cleanupElements); } @NotNull @@ -237,7 +237,8 @@ public class CreateConstructorParameterFromFieldFix implements IntentionAction { final PsiFile file, final Editor editor, final PsiMethod constructor, - final PsiField[] fields, final List cleanupElements) throws IncorrectOperationException { + final PsiField[] fields, + final List> cleanupElements) throws IncorrectOperationException { final PsiParameterList parameterList = constructor.getParameterList(); final PsiParameter[] parameters = parameterList.getParameters(); ParameterInfoImpl[] newParamInfos = new ParameterInfoImpl[parameters.length + fields.length]; @@ -328,13 +329,14 @@ public class CreateConstructorParameterFromFieldFix implements IntentionAction { } private static boolean doCreate(Project project, Editor editor, PsiParameter[] parameters, SmartPsiElementPointer constructorPointer, - ParameterInfoImpl[] parameterInfos, Map fields, List cleanupElements) { + ParameterInfoImpl[] parameterInfos, Map fields, List> cleanupElements) { PsiMethod constructor = (PsiMethod)constructorPointer.getElement(); assert constructor != null; PsiParameter[] newParameters = constructor.getParameterList().getParameters(); if (newParameters == parameters) return false; //user must have canceled dialog // do not introduce assignment in chained constructor if (JavaHighlightUtil.getChainedConstructors(constructor) == null) { + final SmartPointerManager manager = SmartPointerManager.getInstance(project); boolean created = false; for (PsiField field : fields.keySet()) { final String defaultParamName = fields.get(field); @@ -343,10 +345,10 @@ public class CreateConstructorParameterFromFieldFix implements IntentionAction { continue; } NullableNotNullManager.getInstance(field.getProject()).copyNotNullAnnotation(field, parameter); - cleanupElements.add(parameter); + cleanupElements.add(manager.createSmartPsiElementPointer(parameter)); final PsiElement assignmentStatement = AssignFieldFromParameterAction.addFieldAssignmentStatement(project, field, parameter, editor); if (assignmentStatement != null) { - cleanupElements.add(assignmentStatement); + cleanupElements.add(manager.createSmartPsiElementPointer(assignmentStatement)); } created = true; } diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateFromUsageUtils.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateFromUsageUtils.java index 039e9f82f165..fafe647b4137 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateFromUsageUtils.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateFromUsageUtils.java @@ -261,10 +261,7 @@ public class CreateFromUsageUtils { names = new String[]{"p" + i}; } - if (argType == null || PsiType.NULL.equals(argType) || - argType instanceof PsiLambdaExpressionType || - argType instanceof PsiLambdaParameterType || - argType instanceof PsiMethodReferenceType) { + if (argType == null || PsiType.NULL.equals(argType) || LambdaUtil.notInferredType(argType)) { argType = PsiType.getJavaLangObject(psiManager, resolveScope); } else if (argType instanceof PsiDisjunctionType) { argType = ((PsiDisjunctionType)argType).getLeastUpperBound(); diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateLocalFromUsageFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateLocalFromUsageFix.java index aca399d6b20e..bae4cd9e8bee 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateLocalFromUsageFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateLocalFromUsageFix.java @@ -78,6 +78,9 @@ public class CreateLocalFromUsageFix extends CreateVarFromUsageFix { final SmartTypePointer defaultType = SmartTypePointerManager.getInstance(project).createSmartTypePointer(expectedTypes[0]); final PsiType preferredType = TypeSelectorManagerImpl.getPreferredType(expectedTypes, expectedTypes[0]); PsiType type = preferredType != null ? preferredType : expectedTypes[0]; + if (LambdaUtil.notInferredType(type)) { + type = PsiType.getJavaLangObject(myReferenceExpression.getManager(), targetClass.getResolveScope()); + } String varName = myReferenceExpression.getReferenceName(); PsiExpression initializer = null; diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/MethodReturnTypeFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/MethodReturnTypeFix.java index 2848899bab90..308ffa30cef1 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/MethodReturnTypeFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/MethodReturnTypeFix.java @@ -40,7 +40,6 @@ import com.intellij.psi.util.TypeConversionUtil; import com.intellij.refactoring.changeSignature.ChangeSignatureProcessor; import com.intellij.refactoring.changeSignature.OverriderUsageInfo; import com.intellij.refactoring.changeSignature.ParameterInfoImpl; -import com.intellij.refactoring.typeMigration.TypeMigrationLabeler; import com.intellij.refactoring.typeMigration.TypeMigrationProcessor; import com.intellij.refactoring.typeMigration.TypeMigrationRules; import com.intellij.usageView.UsageInfo; @@ -89,14 +88,16 @@ public class MethodReturnTypeFix extends LocalQuickFixAndIntentionActionOnPsiEle @NotNull PsiElement endElement) { final PsiMethod myMethod = (PsiMethod)startElement; - PsiType myReturnType = myReturnTypePointer.getType(); - return myMethod.isValid() - && myMethod.getManager().isInProject(myMethod) - && myReturnType != null - && myReturnType.isValid() - && !TypeConversionUtil.isNullType(myReturnType) - && myMethod.getReturnType() != null - && !Comparing.equal(myReturnType, myMethod.getReturnType()); + final PsiType myReturnType = myReturnTypePointer.getType(); + if (myMethod.isValid() && + myMethod.getManager().isInProject(myMethod) && + myReturnType != null && + myReturnType.isValid() && + !TypeConversionUtil.isNullType(myReturnType)) { + final PsiType returnType = myMethod.getReturnType(); + if (returnType != null && returnType.isValid() && !Comparing.equal(myReturnType, returnType)) return true; + } + return false; } @Override diff --git a/java/java-impl/src/com/intellij/refactoring/changeSignature/JavaChangeInfoImpl.java b/java/java-impl/src/com/intellij/refactoring/changeSignature/JavaChangeInfoImpl.java index 46021270acde..cb1b753898f4 100644 --- a/java/java-impl/src/com/intellij/refactoring/changeSignature/JavaChangeInfoImpl.java +++ b/java/java-impl/src/com/intellij/refactoring/changeSignature/JavaChangeInfoImpl.java @@ -309,7 +309,7 @@ public class JavaChangeInfoImpl extends UserDataHolderBase implements JavaChange @Nullable public PsiExpression getValue(int i, PsiCallExpression expr) throws IncorrectOperationException { if (defaultValues[i] != null) return defaultValues[i]; - final PsiElement valueAtCallSite = newParms[i].getActualValue(expr); + final PsiElement valueAtCallSite = newParms[i].getActualValue(expr, PsiSubstitutor.EMPTY); return valueAtCallSite instanceof PsiExpression ? (PsiExpression)valueAtCallSite : null; } diff --git a/java/java-impl/src/com/intellij/refactoring/changeSignature/JavaChangeSignatureUsageProcessor.java b/java/java-impl/src/com/intellij/refactoring/changeSignature/JavaChangeSignatureUsageProcessor.java index 1cdde7c66a3f..5d9eb7b102b1 100644 --- a/java/java-impl/src/com/intellij/refactoring/changeSignature/JavaChangeSignatureUsageProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/changeSignature/JavaChangeSignatureUsageProcessor.java @@ -410,7 +410,8 @@ public class JavaChangeSignatureUsageProcessor implements ChangeSignatureUsagePr //This methods works equally well for primary usages as well as for propagated callers' usages private static void fixActualArgumentsList(PsiExpressionList list, JavaChangeInfo changeInfo, - boolean toInsertDefaultValue, PsiSubstitutor substitutor) throws IncorrectOperationException { + boolean toInsertDefaultValue, + PsiSubstitutor substitutor) throws IncorrectOperationException { final PsiElementFactory factory = JavaPsiFacade.getInstance(list.getProject()).getElementFactory(); if (changeInfo.isParameterSetOrOrderChanged()) { if (changeInfo instanceof JavaChangeInfoImpl && ((JavaChangeInfoImpl)changeInfo).isPropagationEnabled) { @@ -418,7 +419,7 @@ public class JavaChangeSignatureUsageProcessor implements ChangeSignatureUsagePr for (ParameterInfoImpl info : createdParmsInfo) { PsiExpression newArg; if (toInsertDefaultValue) { - newArg = createDefaultValue(changeInfo, factory, info, list); + newArg = createDefaultValue(changeInfo, factory, info, list, substitutor); } else { newArg = factory.createExpressionFromText(info.getName(), list); @@ -486,12 +487,12 @@ public class JavaChangeSignatureUsageProcessor implements ChangeSignatureUsagePr continue; } } - newArgs[i] = createActualArgument(changeInfo, list, newParms[i], toInsertDefaultValue, args); + newArgs[i] = createActualArgument(changeInfo, list, newParms[i], toInsertDefaultValue, args, substitutor); } if (changeInfo.isArrayToVarargs()) { if (newVarargInitializers == null) { newArgs[newNonVarargCount] = - createActualArgument(changeInfo, list, newParms[newNonVarargCount], toInsertDefaultValue, args); + createActualArgument(changeInfo, list, newParms[newNonVarargCount], toInsertDefaultValue, args, substitutor); } else { System.arraycopy(newVarargInitializers, 0, newArgs, newNonVarargCount, newVarargInitializers.length); @@ -503,7 +504,7 @@ public class JavaChangeSignatureUsageProcessor implements ChangeSignatureUsagePr for (int i = newNonVarargCount; i < newArgsLength; i++){ final int oldIndex = newParms[newNonVarargCount].getOldIndex(); if (oldIndex >= 0 && oldIndex != nonVarargCount) { - newArgs[i] = createActualArgument(changeInfo, list, newParms[newNonVarargCount], toInsertDefaultValue, args); + newArgs[i] = createActualArgument(changeInfo, list, newParms[newNonVarargCount], toInsertDefaultValue, args, substitutor); } else { System.arraycopy(args, nonVarargCount, newArgs, newNonVarargCount, newVarargCount); break; @@ -526,7 +527,8 @@ public class JavaChangeSignatureUsageProcessor implements ChangeSignatureUsagePr final PsiExpressionList list, final JavaParameterInfo info, final boolean toInsertDefaultValue, - final PsiExpression[] args) throws IncorrectOperationException { + final PsiExpression[] args, + PsiSubstitutor substitutor) throws IncorrectOperationException { final PsiElementFactory factory = JavaPsiFacade.getInstance(list.getProject()).getElementFactory(); final int index = info.getOldIndex(); if (index >= 0 && index < args.length) { @@ -534,7 +536,7 @@ public class JavaChangeSignatureUsageProcessor implements ChangeSignatureUsagePr } else { if (toInsertDefaultValue) { - return createDefaultValue(changeInfo, factory, info, list); + return createDefaultValue(changeInfo, factory, info, list, substitutor); } else { return factory.createExpressionFromText(info.getName(), list); @@ -546,7 +548,7 @@ public class JavaChangeSignatureUsageProcessor implements ChangeSignatureUsagePr private static PsiExpression createDefaultValue(JavaChangeInfo changeInfo, final PsiElementFactory factory, final JavaParameterInfo info, - final PsiExpressionList list) + final PsiExpressionList list, PsiSubstitutor substitutor) throws IncorrectOperationException { if (info.isUseAnySingleVariable()) { final PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(list.getProject()).getResolveHelper(); @@ -593,7 +595,7 @@ public class JavaChangeSignatureUsageProcessor implements ChangeSignatureUsagePr } final PsiCallExpression callExpression = PsiTreeUtil.getParentOfType(list, PsiCallExpression.class); final String defaultValue = info.getDefaultValue(); - return callExpression != null ? (PsiExpression)info.getActualValue(callExpression) + return callExpression != null ? (PsiExpression)info.getActualValue(callExpression, substitutor) : !StringUtil.isEmpty(defaultValue) ? factory.createExpressionFromText(defaultValue, list) : null; } @@ -706,14 +708,13 @@ public class JavaChangeSignatureUsageProcessor implements ChangeSignatureUsagePr private static void addDelegateArguments(JavaChangeInfo changeInfo, PsiElementFactory factory, final PsiCallExpression callExpression) throws IncorrectOperationException { final JavaParameterInfo[] newParms = changeInfo.getNewParameters(); final String[] oldParameterNames = changeInfo.getOldParameterNames(); - for (int i = 0; i < newParms.length; i++) { - JavaParameterInfo newParm = newParms[i]; + for (JavaParameterInfo newParm : newParms) { final PsiExpression actualArg; if (newParm.getOldIndex() >= 0) { actualArg = factory.createExpressionFromText(oldParameterNames[newParm.getOldIndex()], callExpression); } else { - actualArg = (PsiExpression)changeInfo.getActualValue(i, callExpression); + actualArg = (PsiExpression)newParm.getActualValue(callExpression, PsiSubstitutor.EMPTY); } final PsiExpressionList argumentList = callExpression.getArgumentList(); if (actualArg != null && argumentList != null) { diff --git a/java/java-impl/src/com/intellij/refactoring/changeSignature/JavaParameterInfo.java b/java/java-impl/src/com/intellij/refactoring/changeSignature/JavaParameterInfo.java index bc5740508172..b2dd2e8f0dbe 100644 --- a/java/java-impl/src/com/intellij/refactoring/changeSignature/JavaParameterInfo.java +++ b/java/java-impl/src/com/intellij/refactoring/changeSignature/JavaParameterInfo.java @@ -34,8 +34,7 @@ public interface JavaParameterInfo extends ParameterInfo { PsiExpression getValue(PsiCallExpression callExpression); @Nullable - @Override - default PsiElement getActualValue(PsiElement callExpression) { + default PsiElement getActualValue(PsiElement callExpression, Object substitutor) { return callExpression instanceof PsiCallExpression ? getValue((PsiCallExpression)callExpression) : null; } diff --git a/java/java-impl/src/com/intellij/refactoring/inline/InlineParameterExpressionProcessor.java b/java/java-impl/src/com/intellij/refactoring/inline/InlineParameterExpressionProcessor.java index 5ae0546f216f..f5eb4cc76bb7 100644 --- a/java/java-impl/src/com/intellij/refactoring/inline/InlineParameterExpressionProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/inline/InlineParameterExpressionProcessor.java @@ -16,8 +16,8 @@ package com.intellij.refactoring.inline; import com.intellij.codeInsight.ExceptionUtil; -import com.intellij.codeInspection.sameParameterValue.SameParameterValueInspection; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Ref; import com.intellij.psi.*; @@ -26,12 +26,18 @@ import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.refactoring.BaseRefactoringProcessor; +import com.intellij.refactoring.changeSignature.ChangeSignatureProcessorBase; +import com.intellij.refactoring.changeSignature.JavaChangeInfo; +import com.intellij.refactoring.changeSignature.JavaChangeInfoImpl; +import com.intellij.refactoring.changeSignature.ParameterInfoImpl; +import com.intellij.refactoring.util.CanonicalTypes; import com.intellij.refactoring.util.InlineUtil; import com.intellij.refactoring.util.RefactoringUIUtil; import com.intellij.refactoring.util.RefactoringUtil; import com.intellij.usageView.UsageInfo; import com.intellij.usageView.UsageViewDescriptor; import com.intellij.usageView.UsageViewUtil; +import com.intellij.util.VisibilityUtil; import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -53,6 +59,9 @@ public class InlineParameterExpressionProcessor extends BaseRefactoringProcessor private final PsiCodeBlock myCallingBlock; private final boolean myCreateLocal; + private JavaChangeInfo myChangeInfo; + private UsageInfo[] myChangeSignatureUsages; + public InlineParameterExpressionProcessor(final PsiCallExpression methodCall, final PsiMethod method, final PsiParameter parameter, @@ -147,6 +156,28 @@ public class InlineParameterExpressionProcessor extends BaseRefactoringProcessor } } + + final PsiParameter[] parameters = myMethod.getParameterList().getParameters(); + final List psiParameters = new ArrayList(); + int paramIdx = 0; + final String paramName = myParameter.getName(); + for (PsiParameter param : parameters) { + if (!Comparing.strEqual(paramName, param.getName())) { + psiParameters.add(new ParameterInfoImpl(paramIdx, param.getName(), param.getType())); + } + paramIdx++; + } + + PsiType returnType = myMethod.getReturnType(); + myChangeInfo = new JavaChangeInfoImpl(VisibilityUtil.getVisibilityModifier(myMethod.getModifierList()), myMethod, myMethod.getName(), + returnType != null ? CanonicalTypes.createTypeWrapper(returnType) : null, + psiParameters.toArray(new ParameterInfoImpl[psiParameters.size()]), + null, + false, + Collections.emptySet(), + Collections.emptySet() ); + myChangeSignatureUsages = ChangeSignatureProcessorBase.findUsages(myChangeInfo); + final UsageInfo[] usageInfos = result.toArray(new UsageInfo[result.size()]); return UsageViewUtil.removeDuplicatedUsages(usageInfos); } @@ -267,7 +298,7 @@ public class InlineParameterExpressionProcessor extends BaseRefactoringProcessor } } - SameParameterValueInspection.InlineParameterValueFix.removeParameter(myMethod, myParameter); + ChangeSignatureProcessorBase.doChangeSignature(myChangeInfo, myChangeSignatureUsages); if (!thrownExceptions.isEmpty()) { for (PsiClassType exception : thrownExceptions) { diff --git a/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/JavaIntroduceParameterObjectClassDescriptor.java b/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/JavaIntroduceParameterObjectClassDescriptor.java index f19c14328fad..ef2387ea2f8f 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/JavaIntroduceParameterObjectClassDescriptor.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/JavaIntroduceParameterObjectClassDescriptor.java @@ -32,20 +32,19 @@ import com.intellij.refactoring.MoveDestination; import com.intellij.refactoring.changeSignature.ParameterInfoImpl; import com.intellij.refactoring.introduceParameterObject.IntroduceParameterObjectClassDescriptor; import com.intellij.refactoring.introduceParameterObject.IntroduceParameterObjectDelegate; -import com.intellij.util.Function; import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.HashMap; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; public class JavaIntroduceParameterObjectClassDescriptor extends IntroduceParameterObjectClassDescriptor { private static final Logger LOG = Logger.getInstance("#" + JavaIntroduceParameterObjectClassDescriptor.class.getName()); - private final Set myTypeParameters = new HashSet<>(); + private final Set myTypeParameters = new LinkedHashSet<>(); private final Map myExistingClassProperties = new HashMap<>(); private final MoveDestination myMoveDestination; @@ -106,12 +105,7 @@ public class JavaIntroduceParameterObjectClassDescriptor extends IntroduceParame public String createFakeClassTypeText() { String text = StringUtil.getQualifiedName(getPackageName(), getClassName()); if (!myTypeParameters.isEmpty()) { - text += "<" + StringUtil.join(myTypeParameters, new Function() { - @Override - public String fun(PsiTypeParameter parameter) { - return parameter.getName(); - } - }, ", ") + ">"; + text += "<" + StringUtil.join(myTypeParameters, PsiNamedElement::getName, ", ") + ">"; } return text; } diff --git a/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/JavaIntroduceParameterObjectDelegate.java b/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/JavaIntroduceParameterObjectDelegate.java index 713afe952eb5..09ce3b828eaf 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/JavaIntroduceParameterObjectDelegate.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/JavaIntroduceParameterObjectDelegate.java @@ -22,6 +22,7 @@ import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.codeStyle.VariableKind; +import com.intellij.psi.impl.PsiDiamondTypeUtil; import com.intellij.psi.search.LocalSearchScope; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.psi.util.PsiTreeUtil; @@ -42,6 +43,7 @@ import com.intellij.util.VisibilityUtil; import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -86,9 +88,9 @@ public class JavaIntroduceParameterObjectDelegate return new ParameterInfoImpl(-1, paramName, facade.getElementFactory().createTypeFromText(classTypeText, method), null) { @Nullable @Override - public PsiElement getActualValue(PsiElement exp) { + public PsiElement getActualValue(PsiElement exp, Object substitutor) { final IntroduceParameterObjectDelegate> delegate = findDelegate(exp); - return delegate != null ? delegate.createNewParameterInitializerAtCallSite(exp, descriptor, oldMethodParameters) : null; + return delegate != null ? delegate.createNewParameterInitializerAtCallSite(exp, descriptor, oldMethodParameters, substitutor) : null; } }; } @@ -96,7 +98,8 @@ public class JavaIntroduceParameterObjectDelegate @Override public PsiElement createNewParameterInitializerAtCallSite(PsiElement callExpression, IntroduceParameterObjectClassDescriptor descriptor, - List oldMethodParameters) { + List oldMethodParameters, + Object substitutor) { if (callExpression instanceof PsiCallExpression) { final PsiCallExpression expr = (PsiCallExpression)callExpression; final JavaPsiFacade facade = JavaPsiFacade.getInstance(expr.getProject()); @@ -109,16 +112,31 @@ public class JavaIntroduceParameterObjectDelegate final PsiExpression[] args = argumentList.getExpressions(); StringBuilder newExpression = new StringBuilder(); - final JavaResolveResult resolvant = expr.resolveMethodGenerics(); - final PsiSubstitutor substitutor = resolvant.getSubstitutor(); - newExpression.append("new ") - .append(JavaPsiFacade.getElementFactory(expr.getProject()).createType(existingClass, substitutor).getCanonicalText()); + newExpression.append("new ").append(existingClass.getQualifiedName()); + if (descriptor instanceof JavaIntroduceParameterObjectClassDescriptor) { + List types = new ArrayList<>(); + for (PsiTypeParameter parameter : ((JavaIntroduceParameterObjectClassDescriptor)descriptor).getTypeParameters()) { + PsiType type = ((PsiSubstitutor)substitutor).substitute(parameter); + if (type == null) { + types.clear(); + break; + } + types.add(type.getCanonicalText()); + } + if (!types.isEmpty()) { + newExpression.append("<").append(StringUtil.join(types, ", ")).append(">"); + } + } newExpression.append('('); newExpression.append(getMergedArgs(descriptor, oldMethodParameters, args)); newExpression.append(')'); - return JavaCodeStyleManager.getInstance(callExpression.getProject()) + PsiNewExpression newClassExpression = (PsiNewExpression)JavaCodeStyleManager.getInstance(callExpression.getProject()) .shortenClassReferences(facade.getElementFactory().createExpressionFromText(newExpression.toString(), expr)); + if (PsiDiamondTypeUtil.canChangeContextForDiamond(newClassExpression, newClassExpression.getType())) { + PsiDiamondTypeUtil.replaceExplicitWithDiamond(newClassExpression.getClassOrAnonymousClassReference().getParameterList()); + } + return newClassExpression; } return null; } diff --git a/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/ParameterObjectBuilder.java b/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/ParameterObjectBuilder.java index ae0fc205a68c..ac91a848aae8 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/ParameterObjectBuilder.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/ParameterObjectBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -137,8 +137,8 @@ class ParameterObjectBuilder { out.append(CodeStyleSettingsManager.getSettings(myProject).GENERATE_FINAL_PARAMETERS ? " final " : ""); final String parameterName = parameter.getName(); final PsiType type = field.getType(); - final PsiType fieldType = parameter.isVarArgs() && type instanceof PsiArrayType ? - PsiEllipsisType.createEllipsis(((PsiArrayType)type).getComponentType(), PsiAnnotation.EMPTY_ARRAY) : type; + final PsiType fieldType = parameter.isVarArgs() && type instanceof PsiArrayType ? + new PsiEllipsisType(((PsiArrayType)type).getComponentType()) : type; out.append(' ' + fieldType.getCanonicalText() + ' ' + parameterName); if (iterator.hasNext()) { out.append(", "); diff --git a/java/java-impl/src/com/intellij/refactoring/util/RefactoringUtil.java b/java/java-impl/src/com/intellij/refactoring/util/RefactoringUtil.java index 8f48d192d81f..d37425b05967 100644 --- a/java/java-impl/src/com/intellij/refactoring/util/RefactoringUtil.java +++ b/java/java-impl/src/com/intellij/refactoring/util/RefactoringUtil.java @@ -385,7 +385,7 @@ public class RefactoringUtil { public static PsiType getTypeByExpressionWithExpectedType(PsiExpression expr) { PsiElementFactory factory = JavaPsiFacade.getInstance(expr.getProject()).getElementFactory(); PsiType type = getTypeByExpression(expr, factory); - final boolean isFunctionalType = type instanceof PsiLambdaExpressionType || type instanceof PsiMethodReferenceType || type instanceof PsiLambdaParameterType; + final boolean isFunctionalType = LambdaUtil.notInferredType(type); final boolean isDenotable = PsiTypesUtil.isDenotableType(expr.getType()); if (type != null && !isFunctionalType && isDenotable) { return type; @@ -404,9 +404,7 @@ public class RefactoringUtil { public static PsiType getTypeByExpression(PsiExpression expr) { PsiElementFactory factory = JavaPsiFacade.getInstance(expr.getProject()).getElementFactory(); PsiType type = getTypeByExpression(expr, factory); - if (type instanceof PsiLambdaParameterType || - type instanceof PsiLambdaExpressionType || - type instanceof PsiMethodReferenceType) { + if (LambdaUtil.notInferredType(type)) { type = factory.createTypeByFQClassName(CommonClassNames.JAVA_LANG_OBJECT, expr.getResolveScope()); } return type; diff --git a/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaAllOverridingMethodsSearcher.java b/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaAllOverridingMethodsSearcher.java index 4a8655419bd3..b24655b12bde 100644 --- a/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaAllOverridingMethodsSearcher.java +++ b/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaAllOverridingMethodsSearcher.java @@ -59,8 +59,6 @@ public class JavaAllOverridingMethodsSearcher implements QueryExecutor { + public SPIReferencesSearcher() { + super(true); + } + @Override public void processQuery(@NotNull final ReferencesSearch.SearchParameters p, @NotNull final Processor consumer) { - final SearchScope scope = ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public SearchScope compute() { - return p.getEffectiveSearchScope(); - } - }); - if (!(scope instanceof GlobalSearchScope)) return; - final PsiElement element = p.getElementToSearch(); + if (!element.isValid()) return; + + final SearchScope scope = p.getEffectiveSearchScope(); + if (!(scope instanceof GlobalSearchScope)) return; + if (element instanceof PsiClass) { final PsiClass aClass = (PsiClass)element; - final String jvmClassName = ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public String compute() { - return ClassUtil.getJVMClassName(aClass); - } - }); + final String jvmClassName = ClassUtil.getJVMClassName(aClass); if (jvmClassName == null) return; - final PsiFile[] files = ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public PsiFile[] compute() { - return FilenameIndex.getFilesByName(aClass.getProject(), jvmClassName, (GlobalSearchScope)scope); - } - }); + final PsiFile[] files = FilenameIndex.getFilesByName(aClass.getProject(), jvmClassName, (GlobalSearchScope)scope); for (PsiFile file : files) { if (file.getLanguage() == SPILanguage.INSTANCE) { final PsiReference reference = file.getReference(); if (reference != null) { - ApplicationManager.getApplication().runReadAction(new Runnable() { - @Override - public void run() { - consumer.process(reference); - } - }); + consumer.process(reference); } } } - } else if (element instanceof PsiPackage) { + } + else if (element instanceof PsiPackage) { final String qualifiedName = ((PsiPackage)element).getQualifiedName(); final Project project = element.getProject(); - final String[] filenames = ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public String[] compute() { - return FilenameIndex.getAllFilenames(project); - } - }); + final String[] filenames = FilenameIndex.getAllFilenames(project); for (final String filename : filenames) { if (filename.startsWith(qualifiedName + ".")) { - final PsiFile[] files = ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public PsiFile[] compute() { - return FilenameIndex.getFilesByName(project, filename, (GlobalSearchScope)scope); - } - }); + final PsiFile[] files = FilenameIndex.getFilesByName(project, filename, (GlobalSearchScope)scope); for (PsiFile file : files) { if (file.getLanguage() == SPILanguage.INSTANCE) { final PsiReference[] references = file.getReferences(); for (final PsiReference reference : references) { if (reference.getCanonicalText().equals(qualifiedName)) { - ApplicationManager.getApplication().runReadAction(new Runnable() { - public void run() { - consumer.process(reference); - } - }); + consumer.process(reference); } } } diff --git a/java/java-psi-api/src/com/intellij/psi/GenericsUtil.java b/java/java-psi-api/src/com/intellij/psi/GenericsUtil.java index aaf7141512ab..aa050b311359 100644 --- a/java/java-psi-api/src/com/intellij/psi/GenericsUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/GenericsUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -376,11 +376,14 @@ public class GenericsUtil { LOG.assertTrue(toPut == null || toPut.isValid(), toPut); substitutor = substitutor.put(typeParameter, toPut); } - final PsiAnnotation[] applicableAnnotations = classType.getApplicableAnnotations(); - if (substitutor == PsiSubstitutor.EMPTY && !toExtend && applicableAnnotations.length == 0 && !(aClass instanceof PsiTypeParameter)) return classType; + PsiAnnotation[] applicableAnnotations = classType.getApplicableAnnotations(); + if (substitutor == PsiSubstitutor.EMPTY && !toExtend && applicableAnnotations.length == 0 && !(aClass instanceof PsiTypeParameter)) { + return classType; + } PsiManager manager = aClass.getManager(); PsiType result = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory() - .createType(aClass, substitutor, PsiUtil.getLanguageLevel(aClass), applicableAnnotations); + .createType(aClass, substitutor, PsiUtil.getLanguageLevel(aClass)) + .annotate(TypeAnnotationProvider.Static.create(applicableAnnotations)); if (toExtend) result = PsiWildcardType.createExtends(manager, result); return result; } diff --git a/java/java-psi-api/src/com/intellij/psi/JVMElementFactory.java b/java/java-psi-api/src/com/intellij/psi/JVMElementFactory.java index 7c3999e37a7b..6b1f4f8532b5 100644 --- a/java/java-psi-api/src/com/intellij/psi/JVMElementFactory.java +++ b/java/java-psi-api/src/com/intellij/psi/JVMElementFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -34,7 +34,7 @@ public interface JVMElementFactory { * * @param name the name of the class to create. * @return the created class instance. - * @throws com.intellij.util.IncorrectOperationException + * @throws IncorrectOperationException * if name is not a valid Java identifier. */ @NotNull @@ -175,7 +175,7 @@ public interface JVMElementFactory { * * @param name the name of the annotation type to create. * @return the created annotation type instance. - * @throws com.intellij.util.IncorrectOperationException if name is not a valid Java identifier. + * @throws IncorrectOperationException if name is not a valid Java identifier. */ @NotNull PsiClass createAnnotationType(@NotNull @NonNls String name) throws IncorrectOperationException; @@ -201,13 +201,20 @@ public interface JVMElementFactory { @NotNull PsiClassType createType(@NotNull PsiClass resolve, @NotNull PsiSubstitutor substitutor); - /* - additional languageLevel parameter to memorize language level for allowing/prohibiting boxing/unboxing - */ + /** + * Creates a class type for the specified class, using the specified substitutor + * to replace generic type parameters on the class. + * + * @param resolve the class for which the class type is created. + * @param substitutor the substitutor to use. + * @param languageLevel to memorize language level for allowing/prohibiting boxing/unboxing. + * @return the class type instance. + */ @NotNull PsiClassType createType(@NotNull PsiClass resolve, @NotNull PsiSubstitutor substitutor, @NotNull LanguageLevel languageLevel); - @NotNull + /** @deprecated use {@link PsiType#annotate(TypeAnnotationProvider)} (to be removed in IDEA 18) */ + @SuppressWarnings("unused") PsiClassType createType(@NotNull PsiClass resolve, @NotNull PsiSubstitutor substitutor, @NotNull LanguageLevel languageLevel, diff --git a/java/java-psi-api/src/com/intellij/psi/PsiArrayType.java b/java/java-psi-api/src/com/intellij/psi/PsiArrayType.java index 71292309cfc4..5019d5b57fed 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiArrayType.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiArrayType.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -26,24 +26,26 @@ import org.jetbrains.annotations.NotNull; public class PsiArrayType extends PsiType.Stub { private final PsiType myComponentType; - /** - * Creates an array type with the specified component type. - * - * @param componentType the type of the array component. - */ public PsiArrayType(@NotNull PsiType componentType) { - this(componentType, PsiAnnotation.EMPTY_ARRAY); + this(componentType, TypeAnnotationProvider.EMPTY); } public PsiArrayType(@NotNull PsiType componentType, @NotNull PsiAnnotation[] annotations) { super(annotations); myComponentType = componentType; } - public PsiArrayType(@NotNull PsiType componentType, @NotNull TypeAnnotationProvider annotations) { - super(annotations); + + public PsiArrayType(@NotNull PsiType componentType, @NotNull TypeAnnotationProvider provider) { + super(provider); myComponentType = componentType; } + @NotNull + @Override + public PsiArrayType annotate(@NotNull TypeAnnotationProvider provider) { + return provider == getAnnotationProvider() ? this : new PsiArrayType(myComponentType, provider); + } + @NotNull @Override public String getPresentableText() { @@ -117,13 +119,15 @@ public class PsiArrayType extends PsiType.Stub { return myComponentType; } + @Override public boolean equals(Object obj) { return obj instanceof PsiArrayType && (this instanceof PsiEllipsisType == obj instanceof PsiEllipsisType) && myComponentType.equals(((PsiArrayType)obj).getComponentType()); } + @Override public int hashCode() { return myComponentType.hashCode() * 3; } -} +} \ No newline at end of file diff --git a/java/java-psi-api/src/com/intellij/psi/PsiCapturedWildcardType.java b/java/java-psi-api/src/com/intellij/psi/PsiCapturedWildcardType.java index b9d13bc76073..c69c9250b8a2 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiCapturedWildcardType.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiCapturedWildcardType.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -48,7 +48,7 @@ public class PsiCapturedWildcardType extends PsiType.Stub { private PsiCapturedWildcardType(@NotNull PsiWildcardType existential, @NotNull PsiElement context, @Nullable PsiTypeParameter parameter) { - super(PsiAnnotation.EMPTY_ARRAY); + super(TypeAnnotationProvider.EMPTY); myExistential = existential; myContext = context; myParameter = parameter; @@ -208,4 +208,4 @@ public class PsiCapturedWildcardType extends PsiType.Stub { public PsiTypeParameter getTypeParameter() { return myParameter; } -} +} \ No newline at end of file diff --git a/java/java-psi-api/src/com/intellij/psi/PsiClassType.java b/java/java-psi-api/src/com/intellij/psi/PsiClassType.java index 8a322bca993b..4c11523f2c46 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiClassType.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiClassType.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -30,9 +30,6 @@ import org.jetbrains.annotations.Nullable; * @author max */ public abstract class PsiClassType extends PsiType { - /** - * The empty array of PSI class types which can be reused to avoid unnecessary allocations. - */ public static final PsiClassType[] EMPTY_ARRAY = new PsiClassType[0]; public static final ArrayFactory ARRAY_FACTORY = new ArrayFactory() { @NotNull @@ -53,11 +50,17 @@ public abstract class PsiClassType extends PsiType { myLanguageLevel = languageLevel; } - public PsiClassType(LanguageLevel languageLevel, @NotNull TypeAnnotationProvider annotations) { - super(annotations); + public PsiClassType(LanguageLevel languageLevel, @NotNull TypeAnnotationProvider provider) { + super(provider); myLanguageLevel = languageLevel; } + @NotNull + @Override + public PsiClassType annotate(@NotNull TypeAnnotationProvider provider) { + return (PsiClassType)super.annotate(provider); + } + /** * Resolves the class reference and returns the resulting class. * @@ -221,7 +224,7 @@ public abstract class PsiClassType extends PsiType { public abstract PsiClassType rawType(); /** - * Overrides {@link com.intellij.psi.PsiType#getResolveScope()} to narrow specify @NotNull. + * Overrides {@link PsiType#getResolveScope()} to narrow specify @NotNull. */ @Override @NotNull @@ -314,4 +317,4 @@ public abstract class PsiClassType extends PsiType { @Override public abstract String getCanonicalText(boolean annotated); } -} +} \ No newline at end of file diff --git a/java/java-psi-api/src/com/intellij/psi/PsiDiamondType.java b/java/java-psi-api/src/com/intellij/psi/PsiDiamondType.java index bc71843ee20b..8b92ec95a3ae 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiDiamondType.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiDiamondType.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -26,8 +26,8 @@ import java.util.List; public abstract class PsiDiamondType extends PsiType { public static final RecursionGuard ourDiamondGuard = RecursionManager.createGuard("diamondInference"); - public PsiDiamondType(PsiAnnotation[] annotations) { - super(annotations); + public PsiDiamondType() { + super(TypeAnnotationProvider.EMPTY); } public abstract DiamondInferenceResult resolveInferredTypes(); diff --git a/java/java-psi-api/src/com/intellij/psi/PsiDisjunctionType.java b/java/java-psi-api/src/com/intellij/psi/PsiDisjunctionType.java index 2b5299e35327..d2c4a2ea0651 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiDisjunctionType.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiDisjunctionType.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -39,7 +39,7 @@ public class PsiDisjunctionType extends PsiType.Stub { private final CachedValue myLubCache; public PsiDisjunctionType(@NotNull List types, @NotNull PsiManager psiManager) { - super(PsiAnnotation.EMPTY_ARRAY); + super(TypeAnnotationProvider.EMPTY); myManager = psiManager; myTypes = Collections.unmodifiableList(types); @@ -168,4 +168,4 @@ public class PsiDisjunctionType extends PsiType.Stub { return true; } -} +} \ No newline at end of file diff --git a/java/java-psi-api/src/com/intellij/psi/PsiElementFactory.java b/java/java-psi-api/src/com/intellij/psi/PsiElementFactory.java index 41ac72898245..404a39672972 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiElementFactory.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiElementFactory.java @@ -178,12 +178,6 @@ public interface PsiElementFactory extends PsiJavaParserFacade, JVMElementFactor @Override @NotNull PsiClassType createType(@NotNull PsiClass resolve, @NotNull PsiSubstitutor substitutor, @Nullable LanguageLevel languageLevel); - @Override - @NotNull PsiClassType createType(@NotNull PsiClass resolve, - @NotNull PsiSubstitutor substitutor, - @Nullable LanguageLevel languageLevel, - @NotNull PsiAnnotation[] annotations); - /** * Creates a class type for the specified reference pointing to a class. * diff --git a/java/java-psi-api/src/com/intellij/psi/PsiEllipsisType.java b/java/java-psi-api/src/com/intellij/psi/PsiEllipsisType.java index b00ece6a7798..0738462ee21c 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiEllipsisType.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiEllipsisType.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -23,11 +23,6 @@ import org.jetbrains.annotations.NotNull; * @author ven */ public class PsiEllipsisType extends PsiArrayType { - /** - * Creates an ellipsis type instance with the specified component type. - * - * @param componentType the type of the varargs array component. - */ public PsiEllipsisType(@NotNull PsiType componentType) { super(componentType); } @@ -35,11 +30,19 @@ public class PsiEllipsisType extends PsiArrayType { public PsiEllipsisType(@NotNull PsiType componentType, @NotNull PsiAnnotation[] annotations) { super(componentType, annotations); } - public PsiEllipsisType(@NotNull PsiType componentType, @NotNull TypeAnnotationProvider annotations) { - super(componentType, annotations); + + public PsiEllipsisType(@NotNull PsiType componentType, @NotNull TypeAnnotationProvider provider) { + super(componentType, provider); } @NotNull + @Override + public PsiEllipsisType annotate(@NotNull TypeAnnotationProvider provider) { + return provider == getAnnotationProvider() ? this : new PsiEllipsisType(getComponentType(), provider); + } + + /** @deprecated use {@link #annotate(TypeAnnotationProvider)} (to be removed in IDEA 18) */ + @SuppressWarnings("unused") public static PsiType createEllipsis(@NotNull PsiType componentType, @NotNull PsiAnnotation[] annotations) { return new PsiEllipsisType(componentType, annotations); } @@ -82,7 +85,8 @@ public class PsiEllipsisType extends PsiArrayType { return visitor.visitEllipsisType(this); } + @Override public int hashCode() { return super.hashCode() * 5; } -} +} \ No newline at end of file diff --git a/java/java-psi-api/src/com/intellij/psi/PsiIntersectionType.java b/java/java-psi-api/src/com/intellij/psi/PsiIntersectionType.java index f46e68012ed9..0adc5e1c79e9 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiIntersectionType.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiIntersectionType.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -35,7 +35,7 @@ public class PsiIntersectionType extends PsiType.Stub { private final PsiType[] myConjuncts; private PsiIntersectionType(@NotNull PsiType[] conjuncts) { - super(PsiAnnotation.EMPTY_ARRAY); + super(TypeAnnotationProvider.EMPTY); myConjuncts = conjuncts; } @@ -176,6 +176,7 @@ public class PsiIntersectionType extends PsiType.Stub { return myConjuncts[0]; } + @Override public boolean equals(final Object obj) { if (this == obj) return true; if (!(obj instanceof PsiIntersectionType)) return false; @@ -190,6 +191,7 @@ public class PsiIntersectionType extends PsiType.Stub { return true; } + @Override public int hashCode() { return myConjuncts[0].hashCode(); } @@ -221,4 +223,4 @@ public class PsiIntersectionType extends PsiType.Stub { } return null; } -} +} \ No newline at end of file diff --git a/java/java-psi-api/src/com/intellij/psi/PsiJavaParserFacade.java b/java/java-psi-api/src/com/intellij/psi/PsiJavaParserFacade.java index 70d7d3f46bd3..64fe93db80d4 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiJavaParserFacade.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiJavaParserFacade.java @@ -235,11 +235,14 @@ public interface PsiJavaParserFacade { /** * Creates a Java type from the specified text. * - * @param text the text of the type to create (a primitive type keyword). - * @param annotations array (possible empty) of annotations to annotate the created type. + * @param text the text of the type to create (a primitive type keyword). * @return the created type instance. * @throws IncorrectOperationException if some of the parameters are not valid. */ @NotNull + PsiType createPrimitiveTypeFromText(@NotNull String text) throws IncorrectOperationException; + + /** @deprecated use {@link PsiType#annotate(TypeAnnotationProvider)} (to be removed in IDEA 18) */ + @SuppressWarnings("unused") PsiType createPrimitiveType(@NotNull String text, @NotNull PsiAnnotation[] annotations) throws IncorrectOperationException; } \ No newline at end of file diff --git a/java/java-psi-api/src/com/intellij/psi/PsiLambdaExpressionType.java b/java/java-psi-api/src/com/intellij/psi/PsiLambdaExpressionType.java index db9db62596c1..7f6b0e7e53d2 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiLambdaExpressionType.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiLambdaExpressionType.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -26,7 +26,7 @@ public class PsiLambdaExpressionType extends PsiType { private final PsiLambdaExpression myExpression; public PsiLambdaExpressionType(PsiLambdaExpression expression) { - super(PsiAnnotation.EMPTY_ARRAY); + super(TypeAnnotationProvider.EMPTY); myExpression = expression; } @@ -77,4 +77,4 @@ public class PsiLambdaExpressionType extends PsiType { public PsiLambdaExpression getExpression() { return myExpression; } -} +} \ No newline at end of file diff --git a/java/java-psi-api/src/com/intellij/psi/PsiLambdaParameterType.java b/java/java-psi-api/src/com/intellij/psi/PsiLambdaParameterType.java index 6a1991c60b39..9d0e550c6feb 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiLambdaParameterType.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiLambdaParameterType.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -26,7 +26,7 @@ public class PsiLambdaParameterType extends PsiType { private final PsiParameter myParameter; public PsiLambdaParameterType(PsiParameter parameter) { - super(PsiAnnotation.EMPTY_ARRAY); + super(TypeAnnotationProvider.EMPTY); myParameter = parameter; } @@ -77,4 +77,4 @@ public class PsiLambdaParameterType extends PsiType { public PsiParameter getParameter() { return myParameter; } -} +} \ No newline at end of file diff --git a/java/java-psi-api/src/com/intellij/psi/PsiPrimitiveType.java b/java/java-psi-api/src/com/intellij/psi/PsiPrimitiveType.java index 82854ddffb85..1862c29a916c 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiPrimitiveType.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiPrimitiveType.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -19,7 +19,6 @@ import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.PsiUtil; import gnu.trove.THashMap; -import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -36,23 +35,30 @@ public class PsiPrimitiveType extends PsiType.Stub { private final String myName; - PsiPrimitiveType(@NonNls @NotNull String name, @NonNls String boxedName) { - this(name, PsiAnnotation.EMPTY_ARRAY); + PsiPrimitiveType(@NotNull String name, String boxedName) { + this(name, TypeAnnotationProvider.EMPTY); if (boxedName != null) { ourQNameToUnboxed.put(boxedName, this); ourUnboxedToQName.put(this, boxedName); } } - public PsiPrimitiveType(@NonNls @NotNull String name, @NotNull PsiAnnotation[] annotations) { + public PsiPrimitiveType(@NotNull String name, @NotNull PsiAnnotation[] annotations) { super(annotations); myName = name; } - public PsiPrimitiveType(@NonNls @NotNull String name, @NotNull TypeAnnotationProvider annotations) { - super(annotations); + + public PsiPrimitiveType(@NotNull String name, @NotNull TypeAnnotationProvider provider) { + super(provider); myName = name; } + @NotNull + @Override + public PsiPrimitiveType annotate(@NotNull TypeAnnotationProvider provider) { + return provider == getAnnotationProvider() ? this : new PsiPrimitiveType(myName, provider); + } + @NotNull @Override public String getPresentableText() { @@ -119,19 +125,18 @@ public class PsiPrimitiveType extends PsiType.Stub { @Nullable public static PsiPrimitiveType getUnboxedType(PsiType type) { if (!(type instanceof PsiClassType)) return null; + + assert type.isValid() : type; LanguageLevel languageLevel = ((PsiClassType)type).getLanguageLevel(); if (!languageLevel.isAtLeast(LanguageLevel.JDK_1_5)) return null; - assert type.isValid() : type; PsiClass psiClass = ((PsiClassType)type).resolve(); if (psiClass == null) return null; PsiPrimitiveType unboxed = ourQNameToUnboxed.get(psiClass.getQualifiedName()); - PsiAnnotation[] annotations = type.getAnnotations(); - if (unboxed != null && annotations.length > 0) { - unboxed = new PsiPrimitiveType(unboxed.myName, annotations); - } - return unboxed; + if (unboxed == null) return null; + + return unboxed.annotate(type.getAnnotationProvider()); } public String getBoxedTypeName() { @@ -146,31 +151,30 @@ public class PsiPrimitiveType extends PsiType.Stub { * it was not possible to resolve the reference to the class. */ @Nullable - public PsiClassType getBoxedType(PsiElement context) { + public PsiClassType getBoxedType(@NotNull PsiElement context) { PsiFile file = context.getContainingFile(); LanguageLevel languageLevel = PsiUtil.getLanguageLevel(file); if (!languageLevel.isAtLeast(LanguageLevel.JDK_1_5)) return null; String boxedQName = getBoxedTypeName(); - //[ven]previous call returns null for NULL, VOID if (boxedQName == null) return null; + JavaPsiFacade facade = JavaPsiFacade.getInstance(file.getProject()); PsiClass aClass = facade.findClass(boxedQName, file.getResolveScope()); if (aClass == null) return null; PsiElementFactory factory = facade.getElementFactory(); - return factory.createType(aClass, PsiSubstitutor.EMPTY, languageLevel, getAnnotations()); + return factory.createType(aClass, PsiSubstitutor.EMPTY, languageLevel).annotate(getAnnotationProvider()); } @Nullable - public PsiClassType getBoxedType(final PsiManager manager, final GlobalSearchScope resolveScope) { - final String boxedQName = getBoxedTypeName(); - - //[ven]previous call returns null for NULL, VOID + public PsiClassType getBoxedType(@NotNull PsiManager manager, @NotNull GlobalSearchScope resolveScope) { + String boxedQName = getBoxedTypeName(); if (boxedQName == null) return null; - final PsiClass aClass = JavaPsiFacade.getInstance(manager.getProject()).findClass(boxedQName, resolveScope); + PsiClass aClass = JavaPsiFacade.getInstance(manager.getProject()).findClass(boxedQName, resolveScope); if (aClass == null) return null; + return JavaPsiFacade.getInstance(manager.getProject()).getElementFactory().createType(aClass); } @@ -187,4 +191,4 @@ public class PsiPrimitiveType extends PsiType.Stub { public boolean equals(Object obj) { return obj instanceof PsiPrimitiveType && myName.equals(((PsiPrimitiveType)obj).myName); } -} +} \ No newline at end of file diff --git a/java/java-psi-api/src/com/intellij/psi/PsiType.java b/java/java-psi-api/src/com/intellij/psi/PsiType.java index 3c59fd2633e9..6220ffd8cba1 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiType.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiType.java @@ -56,13 +56,7 @@ public abstract class PsiType implements PsiAnnotationOwner { * Constructs a PsiType with given annotations */ protected PsiType(@NotNull final PsiAnnotation[] annotations) { - this(annotations.length == 0 ? TypeAnnotationProvider.EMPTY : new TypeAnnotationProvider() { - @NotNull - @Override - public PsiAnnotation[] getAnnotations() { - return annotations; - } - }); + this(TypeAnnotationProvider.Static.create(annotations)); } /** @@ -72,6 +66,11 @@ public abstract class PsiType implements PsiAnnotationOwner { myAnnotationProvider = annotations; } + @NotNull + public PsiType annotate(@NotNull TypeAnnotationProvider provider) { + throw new UnsupportedOperationException("Not implemented for " + getClass()); + } + /** * Creates array type with this type as a component. */ @@ -80,10 +79,8 @@ public abstract class PsiType implements PsiAnnotationOwner { return new PsiArrayType(this); } - /** - * Creates array type with this type as a component. - */ - @NotNull + /** @deprecated use {@link #annotate(TypeAnnotationProvider)} (to be removed in IDEA 18) */ + @SuppressWarnings("unused") public PsiArrayType createArrayType(@NotNull PsiAnnotation... annotations) { return new PsiArrayType(this, annotations); } diff --git a/java/java-psi-api/src/com/intellij/psi/PsiWildcardType.java b/java/java-psi-api/src/com/intellij/psi/PsiWildcardType.java index f5c199de6642..263f02029ce4 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiWildcardType.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiWildcardType.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -31,7 +31,7 @@ public class PsiWildcardType extends PsiType.Stub { public static final String EXTENDS_PREFIX = "? extends "; public static final String SUPER_PREFIX = "? super "; - private static final Logger LOG = Logger.getInstance("#com.intellij.psi.PsiWildcardType"); + private static final Logger LOG = Logger.getInstance(PsiWildcardType.class); private static final Key UNBOUNDED_WILDCARD = new Key("UNBOUNDED_WILDCARD"); private final PsiManager myManager; @@ -39,14 +39,14 @@ public class PsiWildcardType extends PsiType.Stub { private final PsiType myBound; private PsiWildcardType(@NotNull PsiManager manager, boolean isExtending, @Nullable PsiType bound) { - super(PsiAnnotation.EMPTY_ARRAY); + super(TypeAnnotationProvider.EMPTY); myManager = manager; myIsExtending = isExtending; myBound = bound; } - private PsiWildcardType(@NotNull PsiWildcardType type, @NotNull TypeAnnotationProvider annotations) { - super(annotations); + private PsiWildcardType(@NotNull PsiWildcardType type, @NotNull TypeAnnotationProvider provider) { + super(provider); myManager = type.myManager; myIsExtending = type.myIsExtending; myBound = type.myBound; @@ -75,20 +75,16 @@ public class PsiWildcardType extends PsiType.Stub { return new PsiWildcardType(manager, false, bound); } - @NotNull + /** @deprecated use {@link #annotate(TypeAnnotationProvider)} (to be removed in IDEA 18) */ + @SuppressWarnings("unused") public PsiWildcardType annotate(@NotNull final PsiAnnotation[] annotations) { - return annotations.length == 0 ? this : new PsiWildcardType(this, new TypeAnnotationProvider() { - @NotNull - @Override - public PsiAnnotation[] getAnnotations() { - return annotations; - } - }); + return annotations.length == 0 ? this : new PsiWildcardType(this, TypeAnnotationProvider.Static.create(annotations)); } @NotNull - public PsiWildcardType annotate(@NotNull final TypeAnnotationProvider annotations) { - return new PsiWildcardType(this, annotations); + @Override + public PsiWildcardType annotate(@NotNull TypeAnnotationProvider provider) { + return provider == getAnnotationProvider() ? this : new PsiWildcardType(this, provider); } @NotNull @@ -261,4 +257,4 @@ public class PsiWildcardType extends PsiType.Stub { public PsiType getSuperBound() { return myBound == null || myIsExtending ? NULL : myBound; } -} +} \ No newline at end of file diff --git a/java/java-psi-api/src/com/intellij/psi/TypeAnnotationProvider.java b/java/java-psi-api/src/com/intellij/psi/TypeAnnotationProvider.java index 8184778ee46c..05600cffde2f 100644 --- a/java/java-psi-api/src/com/intellij/psi/TypeAnnotationProvider.java +++ b/java/java-psi-api/src/com/intellij/psi/TypeAnnotationProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -36,4 +36,24 @@ public interface TypeAnnotationProvider { @NotNull PsiAnnotation[] getAnnotations(); -} + + + class Static implements TypeAnnotationProvider { + private final PsiAnnotation[] myAnnotations; + + private Static(PsiAnnotation[] annotations) { + myAnnotations = annotations; + } + + @NotNull + @Override + public PsiAnnotation[] getAnnotations() { + return myAnnotations; + } + + @NotNull + public static TypeAnnotationProvider create(@NotNull PsiAnnotation[] annotations) { + return annotations.length == 0 ? EMPTY : new Static(annotations); + } + } +} \ No newline at end of file diff --git a/java/java-psi-api/src/com/intellij/psi/augment/PsiAugmentProvider.java b/java/java-psi-api/src/com/intellij/psi/augment/PsiAugmentProvider.java index d2fc5de10594..532dda378f07 100644 --- a/java/java-psi-api/src/com/intellij/psi/augment/PsiAugmentProvider.java +++ b/java/java-psi-api/src/com/intellij/psi/augment/PsiAugmentProvider.java @@ -142,8 +142,9 @@ public abstract class PsiAugmentProvider { } private static void forEach(Project project, Processor processor) { + boolean dumb = DumbService.isDumb(project); for (PsiAugmentProvider provider : Extensions.getExtensions(EP_NAME)) { - if (!DumbService.isDumb(project) || DumbService.isDumbAware(provider)) { + if (!dumb || DumbService.isDumbAware(provider)) { try { boolean goOn = processor.process(provider); if (!goOn) break; diff --git a/java/java-psi-impl/src/com/intellij/psi/Bottom.java b/java/java-psi-impl/src/com/intellij/psi/Bottom.java index 022c8636800a..c128579b50cc 100644 --- a/java/java-psi-impl/src/com/intellij/psi/Bottom.java +++ b/java/java-psi-impl/src/com/intellij/psi/Bottom.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -25,7 +25,7 @@ public class Bottom extends PsiType { public static final Bottom BOTTOM = new Bottom(); private Bottom() { - super(PsiAnnotation.EMPTY_ARRAY); + super(TypeAnnotationProvider.EMPTY); } @NotNull @@ -56,12 +56,9 @@ public class Bottom extends PsiType { return text.equals("_"); } + @Override public boolean equals(Object o) { - if (o instanceof Bottom) { - return true; - } - - return false; + return o instanceof Bottom; } @Override @@ -84,4 +81,4 @@ public class Bottom extends PsiType { public GlobalSearchScope getResolveScope() { return null; } -} +} \ No newline at end of file diff --git a/java/java-psi-impl/src/com/intellij/psi/PsiDiamondTypeImpl.java b/java/java-psi-impl/src/com/intellij/psi/PsiDiamondTypeImpl.java index 45dcee6e9460..537a4dcf5759 100644 --- a/java/java-psi-impl/src/com/intellij/psi/PsiDiamondTypeImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/PsiDiamondTypeImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -47,13 +47,12 @@ import java.util.*; * @since Jul 30, 2010 */ public class PsiDiamondTypeImpl extends PsiDiamondType { - private static final Logger LOG = Logger.getInstance("#" + PsiDiamondTypeImpl.class.getName()); + private static final Logger LOG = Logger.getInstance(PsiDiamondTypeImpl.class); private final PsiManager myManager; private final PsiTypeElement myTypeElement; public PsiDiamondTypeImpl(PsiManager manager, PsiTypeElement psiTypeElement) { - super(PsiAnnotation.EMPTY_ARRAY); myManager = manager; myTypeElement = psiTypeElement; } @@ -134,9 +133,9 @@ public class PsiDiamondTypeImpl extends PsiDiamondType { final DiamondInferenceResult inferenceResult = resolveInferredTypesNoCheck(newExpression, context); if (anonymousClass != null && PsiUtil.isLanguageLevel9OrHigher(newExpression)) { - final InferredAnonymTypeVisitor anonymTypeVisitor = new InferredAnonymTypeVisitor(context); + final InferredAnonymousTypeVisitor anonymousTypeVisitor = new InferredAnonymousTypeVisitor(context); for (PsiType type : inferenceResult.getInferredTypes()) { - final Boolean accepted = type.accept(anonymTypeVisitor); + final Boolean accepted = type.accept(anonymousTypeVisitor); if (accepted != null && !accepted.booleanValue()) { return PsiDiamondTypeImpl.DiamondInferenceResult.ANONYMOUS_INNER_RESULT; } @@ -150,17 +149,16 @@ public class PsiDiamondTypeImpl extends PsiDiamondType { final PsiSubstitutor inferredSubstitutor = ourDiamondGuard.doPreventingRecursion(context, false, new Computable() { @Override public PsiSubstitutor compute() { - final JavaResolveResult staticFactoryCandidateInfo = context == newExpression ? - CachedValuesManager.getCachedValue(newExpression, - new CachedValueProvider() { - @Nullable - @Override - public Result compute() { - return new Result(getStaticFactoryCandidateInfo(newExpression, newExpression), - PsiModificationTracker.MODIFICATION_COUNT); - } - }) - : getStaticFactoryCandidateInfo(newExpression, context); + final JavaResolveResult staticFactoryCandidateInfo = + context == newExpression + ? CachedValuesManager.getCachedValue(newExpression, new CachedValueProvider() { + @Nullable + @Override + public Result compute() { + return new Result(getStaticFactoryCandidateInfo(newExpression, newExpression), PsiModificationTracker.MODIFICATION_COUNT); + } + }) + : getStaticFactoryCandidateInfo(newExpression, context); staticFactoryRef.set(staticFactoryCandidateInfo); if (staticFactoryCandidateInfo != null) { PsiSubstitutor substitutor = staticFactoryCandidateInfo.getSubstitutor(); @@ -515,10 +513,10 @@ public class PsiDiamondTypeImpl extends PsiDiamondType { * The term "subexpression" includes type arguments of parameterized types (4.5), bounds of wildcards (4.5.1), and element types of array types (10.1). * It excludes bounds of type variables. */ - private static class InferredAnonymTypeVisitor extends PsiTypeVisitor { + private static class InferredAnonymousTypeVisitor extends PsiTypeVisitor { private final PsiElement myExpression; - public InferredAnonymTypeVisitor(PsiElement expression) { + public InferredAnonymousTypeVisitor(PsiElement expression) { myExpression = expression; } @@ -564,4 +562,4 @@ public class PsiDiamondTypeImpl extends PsiDiamondType { return true; } } -} +} \ No newline at end of file diff --git a/java/java-psi-impl/src/com/intellij/psi/PsiTypeVariable.java b/java/java-psi-impl/src/com/intellij/psi/PsiTypeVariable.java index 872c8ded71ea..83ab83092689 100644 --- a/java/java-psi-impl/src/com/intellij/psi/PsiTypeVariable.java +++ b/java/java-psi-impl/src/com/intellij/psi/PsiTypeVariable.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -22,7 +22,7 @@ import org.jetbrains.annotations.NotNull; */ public abstract class PsiTypeVariable extends PsiType { protected PsiTypeVariable() { - super(PsiAnnotation.EMPTY_ARRAY); + super(TypeAnnotationProvider.EMPTY); } public abstract int getIndex(); @@ -37,4 +37,4 @@ public abstract class PsiTypeVariable extends PsiType { return visitor.visitType(this); } } -} +} \ No newline at end of file diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/PsiJavaParserFacadeImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/PsiJavaParserFacadeImpl.java index 956490c77384..121ba9dbc33b 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/PsiJavaParserFacadeImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/PsiJavaParserFacadeImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -383,13 +383,18 @@ public class PsiJavaParserFacadeImpl implements PsiJavaParserFacade { } @NotNull - @Override - public PsiType createPrimitiveType(@NotNull final String text, @NotNull final PsiAnnotation[] annotations) throws IncorrectOperationException { - final PsiPrimitiveType primitiveType = getPrimitiveType(text); + public PsiType createPrimitiveTypeFromText(@NotNull String text) throws IncorrectOperationException { + PsiPrimitiveType primitiveType = getPrimitiveType(text); if (primitiveType == null) { throw new IncorrectOperationException("Incorrect primitive type '" + text + "'"); } - return annotations.length == 0 ? primitiveType : new PsiPrimitiveType(text, annotations); + return primitiveType; + } + + @NotNull + @Override + public PsiType createPrimitiveType(@NotNull String text, @NotNull PsiAnnotation[] annotations) throws IncorrectOperationException { + return createPrimitiveTypeFromText(text).annotate(TypeAnnotationProvider.Static.create(annotations)); } public static PsiPrimitiveType getPrimitiveType(final String text) { diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/TypeCorrector.java b/java/java-psi-impl/src/com/intellij/psi/impl/TypeCorrector.java index f1cde7acd2ce..f5f2ff22f1e9 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/TypeCorrector.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/TypeCorrector.java @@ -42,7 +42,7 @@ class TypeCorrector extends PsiTypeMapper { @Override public PsiType visitType(PsiType type) { - if (type instanceof PsiLambdaParameterType || type instanceof PsiLambdaExpressionType || type instanceof PsiMethodReferenceType) { + if (LambdaUtil.notInferredType(type)) { return type; } return super.visitType(type); diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiClassReferenceType.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiClassReferenceType.java index c4cf9db93b6c..585922734345 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiClassReferenceType.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiClassReferenceType.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -42,8 +42,8 @@ public class PsiClassReferenceType extends PsiClassType.Stub { myReference = reference; } - public PsiClassReferenceType(@NotNull PsiJavaCodeReferenceElement reference, LanguageLevel level, @NotNull TypeAnnotationProvider annotations) { - super(level, annotations); + public PsiClassReferenceType(@NotNull PsiJavaCodeReferenceElement reference, LanguageLevel level, @NotNull TypeAnnotationProvider provider) { + super(level, provider); myReference = reference; } @@ -58,6 +58,12 @@ public class PsiClassReferenceType extends PsiClassType.Stub { return result == null ? PsiAnnotation.EMPTY_ARRAY : result.toArray(new PsiAnnotation[result.size()]); } + @NotNull + @Override + public PsiClassReferenceType annotate(@NotNull TypeAnnotationProvider provider) { + return provider == getAnnotationProvider() ? this : new PsiClassReferenceType(myReference, myLanguageLevel, provider); + } + @Override public boolean isValid() { return myReference.isValid(); @@ -222,4 +228,4 @@ public class PsiClassReferenceType extends PsiClassType.Stub { public PsiJavaCodeReferenceElement getReference() { return myReference; } -} +} \ No newline at end of file diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiImmediateClassType.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiImmediateClassType.java index 15823d067a90..f2bd79ba335e 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiImmediateClassType.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiImmediateClassType.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -79,11 +79,11 @@ public class PsiImmediateClassType extends PsiClassType.Stub { }; public PsiImmediateClassType(@NotNull PsiClass aClass, @NotNull PsiSubstitutor substitutor) { - this(aClass, substitutor, null, PsiAnnotation.EMPTY_ARRAY); + this(aClass, substitutor, null, TypeAnnotationProvider.EMPTY); } public PsiImmediateClassType(@NotNull PsiClass aClass, @NotNull PsiSubstitutor substitutor, @Nullable LanguageLevel level) { - this(aClass, substitutor, level, PsiAnnotation.EMPTY_ARRAY); + this(aClass, substitutor, level, TypeAnnotationProvider.EMPTY); } public PsiImmediateClassType(@NotNull PsiClass aClass, @@ -96,17 +96,24 @@ public class PsiImmediateClassType extends PsiClassType.Stub { mySubstitutor = substitutor; assert substitutor.isValid(); } + public PsiImmediateClassType(@NotNull PsiClass aClass, @NotNull PsiSubstitutor substitutor, @Nullable LanguageLevel level, - @NotNull TypeAnnotationProvider annotations) { - super(level, annotations); + @NotNull TypeAnnotationProvider provider) { + super(level, provider); myClass = aClass; myManager = aClass.getManager(); mySubstitutor = substitutor; assert substitutor.isValid(); } + @NotNull + @Override + public PsiImmediateClassType annotate(@NotNull TypeAnnotationProvider provider) { + return provider == getAnnotationProvider() ? this : new PsiImmediateClassType(myClass, mySubstitutor, myLanguageLevel, provider); + } + @Override public PsiClass resolve() { return myClass; @@ -116,6 +123,7 @@ public class PsiImmediateClassType extends PsiClassType.Stub { public String getClassName() { return myClass.getName(); } + @Override @NotNull public PsiType[] getParameters() { @@ -127,12 +135,10 @@ public class PsiImmediateClassType extends PsiClassType.Stub { List lst = new ArrayList(); for (PsiTypeParameter parameter : parameters) { PsiType substituted = mySubstitutor.substitute(parameter); - if (substituted != null) { - lst.add(substituted); - } - else { + if (substituted == null) { return PsiType.EMPTY_ARRAY; } + lst.add(substituted); } return lst.toArray(createArray(lst.size())); } @@ -313,4 +319,4 @@ public class PsiImmediateClassType extends PsiClassType.Stub { public PsiClassType setLanguageLevel(@NotNull LanguageLevel level) { return level.equals(myLanguageLevel) ? this : new PsiImmediateClassType(myClass, mySubstitutor, level, getAnnotationProvider()); } -} +} \ No newline at end of file diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaSharedImplUtil.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaSharedImplUtil.java index 18ff2d110ad9..07fcb8023c55 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaSharedImplUtil.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaSharedImplUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -48,7 +48,7 @@ public class JavaSharedImplUtil { List allAnnotations = collectAnnotations(anchor, stopAt); if (allAnnotations == null) return null; for (PsiAnnotation[] annotations : allAnnotations) { - type = type.createArrayType(annotations); + type = type.createArrayType().annotate(TypeAnnotationProvider.Static.create(annotations)); } return type; diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiNewExpressionImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiNewExpressionImpl.java index 7862a7381011..f093215ee502 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiNewExpressionImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiNewExpressionImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -73,20 +73,22 @@ public class PsiNewExpressionImpl extends ExpressionPsiElement implements PsiNew else if (ElementType.PRIMITIVE_TYPE_BIT_SET.contains(elementType)) { assert type == null : this; PsiElementFactory factory = JavaPsiFacade.getInstance(getProject()).getElementFactory(); - type = factory.createPrimitiveType(child.getText(), ContainerUtil.copyAndClear(annotations, PsiAnnotation.ARRAY_FACTORY, true)); + PsiAnnotation[] copy = ContainerUtil.copyAndClear(annotations, PsiAnnotation.ARRAY_FACTORY, true); + type = factory.createPrimitiveTypeFromText(child.getText()).annotate(TypeAnnotationProvider.Static.create(copy)); if (stop) return type; } else if (elementType == JavaTokenType.LBRACKET) { assert type != null : this; - type = type.createArrayType(ContainerUtil.copyAndClear(annotations, PsiAnnotation.ARRAY_FACTORY, true)); + PsiAnnotation[] copy = ContainerUtil.copyAndClear(annotations, PsiAnnotation.ARRAY_FACTORY, true); + type = type.createArrayType().annotate(TypeAnnotationProvider.Static.create(copy)); if (stop) return type; } else if (elementType == JavaElementType.ANONYMOUS_CLASS) { PsiElementFactory factory = JavaPsiFacade.getInstance(getProject()).getElementFactory(); PsiClass aClass = (PsiClass)child.getPsi(); PsiSubstitutor substitutor = aClass instanceof PsiTypeParameter ? PsiSubstitutor.EMPTY : factory.createRawSubstitutor(aClass); - type = factory.createType(aClass, substitutor, PsiUtil.getLanguageLevel(aClass), - ContainerUtil.copyAndClear(annotations, PsiAnnotation.ARRAY_FACTORY, true)); + PsiAnnotation[] copy = ContainerUtil.copyAndClear(annotations, PsiAnnotation.ARRAY_FACTORY, true); + type = factory.createType(aClass, substitutor, PsiUtil.getLanguageLevel(aClass)).annotate(TypeAnnotationProvider.Static.create(copy)); if (stop) return type; } } diff --git a/java/java-psi-impl/src/com/intellij/refactoring/util/CanonicalTypes.java b/java/java-psi-impl/src/com/intellij/refactoring/util/CanonicalTypes.java index ccb12db66200..99c1dad3c89c 100644 --- a/java/java-psi-impl/src/com/intellij/refactoring/util/CanonicalTypes.java +++ b/java/java-psi-impl/src/com/intellij/refactoring/util/CanonicalTypes.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -50,10 +50,10 @@ public class CanonicalTypes { } private abstract static class AnnotatedType extends Type { - protected final PsiAnnotation[] myAnnotations; + protected final TypeAnnotationProvider myProvider; - protected AnnotatedType(@NotNull PsiAnnotation[] annotations) { - myAnnotations = annotations; + public AnnotatedType(@NotNull TypeAnnotationProvider provider) { + myProvider = TypeAnnotationProvider.Static.create(provider.getAnnotations()); } } @@ -61,14 +61,14 @@ public class CanonicalTypes { private final PsiPrimitiveType myType; private Primitive(@NotNull PsiPrimitiveType type) { - super(type.getAnnotations()); + super(type.getAnnotationProvider()); myType = type; } @NotNull @Override public PsiType getType(PsiElement context, PsiManager manager) { - return myAnnotations.length == 0 ? myType : new PsiPrimitiveType(myType.getCanonicalText(false), myAnnotations); + return myType.annotate(myProvider); } @Override @@ -81,14 +81,14 @@ public class CanonicalTypes { protected final Type myComponentType; private Array(@NotNull PsiType original, @NotNull Type componentType) { - super(original.getAnnotations()); + super(original.getAnnotationProvider()); myComponentType = componentType; } @NotNull @Override public PsiType getType(PsiElement context, PsiManager manager) throws IncorrectOperationException { - return myComponentType.getType(context, manager).createArrayType(myAnnotations); + return myComponentType.getType(context, manager).createArrayType().annotate(myProvider); } @Override @@ -115,7 +115,7 @@ public class CanonicalTypes { @NotNull @Override public PsiType getType(PsiElement context, PsiManager manager) throws IncorrectOperationException { - return new PsiEllipsisType(myComponentType.getType(context, manager), myAnnotations); + return new PsiEllipsisType(myComponentType.getType(context, manager)).annotate(myProvider); } @Override @@ -129,7 +129,7 @@ public class CanonicalTypes { private final Type myBound; private WildcardType(@NotNull PsiType original, boolean isExtending, @Nullable Type bound) { - super(original.getAnnotations()); + super(original.getAnnotationProvider()); myIsExtending = isExtending; myBound = bound; } @@ -147,7 +147,7 @@ public class CanonicalTypes { else { type = PsiWildcardType.createSuper(manager, myBound.getType(context, manager)); } - return type.annotate(myAnnotations); + return type.annotate(myProvider); } @Override @@ -205,7 +205,7 @@ public class CanonicalTypes { private final Map mySubstitutor; private ClassType(@NotNull PsiType original, @NotNull String classQName, @NotNull Map substitutor) { - super(original.getAnnotations()); + super(original.getAnnotationProvider()); myPresentableText = original.getPresentableText(); myClassQName = classQName; mySubstitutor = substitutor; @@ -227,7 +227,7 @@ public class CanonicalTypes { Type substitute = mySubstitutor.get(typeParameter.getName()); substitutionMap.put(typeParameter, substitute != null ? substitute.getType(context, manager) : null); } - return factory.createType(aClass, factory.createSubstitutor(substitutionMap), null, myAnnotations); + return factory.createType(aClass, factory.createSubstitutor(substitutionMap), null).annotate(myProvider); } @Override @@ -326,7 +326,7 @@ public class CanonicalTypes { PsiType substitute = substitutor.substitute(typeParameter); substitutionMap.put(typeParameter.getName(), substitute != null ? substitute.accept(this) : null); } - String qualifiedName = ObjectUtils.notNull(aClass.getQualifiedName(), aClass.getName()); + String qualifiedName = ObjectUtils.notNull(aClass.getQualifiedName(), ObjectUtils.assertNotNull(aClass.getName())); return new ClassType(type, qualifiedName, substitutionMap); } } @@ -358,4 +358,4 @@ public class CanonicalTypes { public static Type createTypeWrapper(@NotNull PsiType type) { return type.accept(Creator.INSTANCE); } -} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createLocalFromUsage/afterFromLambdaParameter.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createLocalFromUsage/afterFromLambdaParameter.java new file mode 100644 index 000000000000..3ef4c22eea70 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createLocalFromUsage/afterFromLambdaParameter.java @@ -0,0 +1,8 @@ +// "Create local variable 'a'" "true" +class C { + void foo() { + (s) -> { + Object a = s; + }; + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createLocalFromUsage/beforeFromLambdaParameter.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createLocalFromUsage/beforeFromLambdaParameter.java new file mode 100644 index 000000000000..0c023701321b --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createLocalFromUsage/beforeFromLambdaParameter.java @@ -0,0 +1,8 @@ +// "Create local variable 'a'" "true" +class C { + void foo() { + (s) -> { + a = s; + }; + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/trivialFunctionalExpressionUsage/beforeCodeBlockWithReturnInSwitch.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/trivialFunctionalExpressionUsage/beforeCodeBlockWithReturnInSwitch.java new file mode 100644 index 000000000000..02526bad681f --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/trivialFunctionalExpressionUsage/beforeCodeBlockWithReturnInSwitch.java @@ -0,0 +1,13 @@ +// "Replace method call on lambda with lambda body" "false" +import java.util.function.Supplier; + +class Test { + { + System.out.println(((Supplier)() -> { + switch () { + default: + return ""; + } + }).get()); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/extractMethod/ArrayAccessWithTopExpression.java b/java/java-tests/testData/refactoring/extractMethod/ArrayAccessWithTopExpression.java new file mode 100644 index 000000000000..453e376880d5 --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/ArrayAccessWithTopExpression.java @@ -0,0 +1,8 @@ +class Test { + { + int i = 0; + double[] doubles = null; + + double progressResult = doubles[0] / i; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/extractMethod/ArrayAccessWithTopExpression_after.java b/java/java-tests/testData/refactoring/extractMethod/ArrayAccessWithTopExpression_after.java new file mode 100644 index 000000000000..11b7df990b29 --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/ArrayAccessWithTopExpression_after.java @@ -0,0 +1,12 @@ +class Test { + { + int i = 0; + double[] doubles = null; + + newMethod(doubles[0] / i); + } + + private void newMethod(double v) { + double progressResult = v; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/introduceParameterObject/typeParametersWithSubstitution/after/Param.java b/java/java-tests/testData/refactoring/introduceParameterObject/typeParametersWithSubstitution/after/Param.java new file mode 100644 index 000000000000..1bb40430e713 --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceParameterObject/typeParametersWithSubstitution/after/Param.java @@ -0,0 +1,13 @@ +import java.util.List; + +public class Param { + private final List y; + + public Param(List y) { + this.y = y; + } + + public List getY() { + return y; + } +} diff --git a/java/java-tests/testData/refactoring/introduceParameterObject/typeParametersWithSubstitution/after/Test.java b/java/java-tests/testData/refactoring/introduceParameterObject/typeParametersWithSubstitution/after/Test.java new file mode 100644 index 000000000000..68610f7ba8ee --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceParameterObject/typeParametersWithSubstitution/after/Test.java @@ -0,0 +1,11 @@ +import java.util.List; + +public class Test { + public static String foo(Param param) { + return null; + } + + void bar(List list) { + System.out.println(foo(new Param<>(list))); + } +} diff --git a/java/java-tests/testData/refactoring/introduceParameterObject/typeParametersWithSubstitution/before/Test.java b/java/java-tests/testData/refactoring/introduceParameterObject/typeParametersWithSubstitution/before/Test.java new file mode 100644 index 000000000000..f13937986913 --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceParameterObject/typeParametersWithSubstitution/before/Test.java @@ -0,0 +1,12 @@ +import java.util.Collection; +import java.util.List; + +public class Test { + public static String foo(List y) { + return null; + } + + void bar(List list) { + System.out.println(foo(list)); + } +} diff --git a/java/java-tests/testSrc/com/intellij/refactoring/ExtractMethodTest.java b/java/java-tests/testSrc/com/intellij/refactoring/ExtractMethodTest.java index 7df6b43c8fb7..35946793c3a8 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/ExtractMethodTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/ExtractMethodTest.java @@ -500,6 +500,10 @@ public class ExtractMethodTest extends LightCodeInsightTestCase { doTest(); } + public void testArrayAccessWithTopExpression() throws Exception { + doTest(); + } + public void testArrayAccessWithDuplicates() throws Exception { doDuplicatesTest(); } diff --git a/java/java-tests/testSrc/com/intellij/refactoring/IntroduceParameterObjectTest.java b/java/java-tests/testSrc/com/intellij/refactoring/IntroduceParameterObjectTest.java index 1f82ad987ab0..c35b9fd93c79 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/IntroduceParameterObjectTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/IntroduceParameterObjectTest.java @@ -22,7 +22,9 @@ package com.intellij.refactoring; import com.intellij.JavaTestUtil; import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.roots.LanguageLevelProjectExtension; import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.refactoring.changeSignature.JavaMethodDescriptor; @@ -132,6 +134,18 @@ public class IntroduceParameterObjectTest extends MultiFileTestCase{ doTest(); } + public void testTypeParametersWithSubstitution() throws Exception { + final LanguageLevelProjectExtension projectExtension = LanguageLevelProjectExtension.getInstance(getProject()); + final LanguageLevel oldLevel = projectExtension.getLanguageLevel(); + try { + projectExtension.setLanguageLevel(LanguageLevel.HIGHEST); + doTest(); + } + finally { + projectExtension.setLanguageLevel(oldLevel); + } + } + public void testSameTypeAndVarargs() throws Exception { doTest(false, false, method -> { final PsiParameter[] parameters = method.getParameterList().getParameters(); diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchingVisitor.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchingVisitor.java index e58dfe17f109..477987466086 100644 --- a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchingVisitor.java +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchingVisitor.java @@ -1041,50 +1041,94 @@ public class JavaMatchingVisitor extends JavaElementVisitor { final PsiReferenceExpression mcallRef1 = mcall.getMethodExpression(); final PsiReferenceExpression mcallRef2 = mcall2.getMethodExpression(); - final boolean isTypedVar = myMatchingVisitor.getMatchContext().getPattern().isTypedVar(mcallRef1.getReferenceNameElement()); + final PsiElement patternMethodName = mcallRef1.getReferenceNameElement(); + final boolean isTypedVar = myMatchingVisitor.getMatchContext().getPattern().isTypedVar(patternMethodName); - if (!myMatchingVisitor.matchText(mcallRef1.getReferenceNameElement(), mcallRef2.getReferenceNameElement()) && !isTypedVar) { + if (!isTypedVar && !myMatchingVisitor.matchText(patternMethodName, mcallRef2.getReferenceNameElement())) { myMatchingVisitor.setResult(false); return; } - final PsiExpression qualifier = mcallRef1.getQualifierExpression(); - final PsiExpression elementQualifier = mcallRef2.getQualifierExpression(); - if (qualifier != null) { + final PsiExpression patternQualifier = mcallRef1.getQualifierExpression(); + final PsiExpression matchedQualifier = mcallRef2.getQualifierExpression(); + if (patternQualifier != null) { - if (elementQualifier != null) { - myMatchingVisitor.setResult(myMatchingVisitor.match(qualifier, elementQualifier)); + if (matchedQualifier != null) { + myMatchingVisitor.setResult(myMatchingVisitor.match(patternQualifier, matchedQualifier)); if (!myMatchingVisitor.getResult()) return; } else { final PsiMethod method = mcall2.resolveMethod(); if (method != null) { - if (qualifier instanceof PsiThisExpression) { + if (patternQualifier instanceof PsiThisExpression) { myMatchingVisitor.setResult(!method.hasModifierProperty(PsiModifier.STATIC)); return; } } - final MatchingHandler handler = myMatchingVisitor.getMatchContext().getPattern().getHandler(qualifier); + final MatchingHandler handler = myMatchingVisitor.getMatchContext().getPattern().getHandler(patternQualifier); matchImplicitQualifier(handler, method, myMatchingVisitor.getMatchContext()); if (!myMatchingVisitor.getResult()) { return; } } } - else if (elementQualifier != null) { + else if (matchedQualifier != null) { myMatchingVisitor.setResult(false); return; } myMatchingVisitor.setResult(myMatchingVisitor.matchSons(mcall.getArgumentList(), mcall2.getArgumentList())); + if (myMatchingVisitor.getResult()) { + myMatchingVisitor.setResult(matchTypeParameters(mcallRef1, mcallRef2)); + } + if (myMatchingVisitor.getResult() && isTypedVar) { boolean res = myMatchingVisitor.getResult(); - res &= myMatchingVisitor.handleTypedElement(mcallRef1.getReferenceNameElement(), mcallRef2.getReferenceNameElement()); + res &= myMatchingVisitor.handleTypedElement(patternMethodName, mcallRef2.getReferenceNameElement()); myMatchingVisitor.setResult(res); } } + private boolean matchTypeParameters(PsiJavaCodeReferenceElement mcallRef1, PsiJavaCodeReferenceElement mcallRef2) { + final PsiReferenceParameterList patternParameterList = mcallRef1.getParameterList(); + if (patternParameterList == null) { + return true; + } + final PsiTypeElement[] patternTypeElements = patternParameterList.getTypeParameterElements(); + if (patternTypeElements.length == 0) { + return true; + } + PsiReferenceParameterList matchedParameterList = mcallRef2.getParameterList(); + if (matchedParameterList == null) { + return false; + } + if (matchedParameterList.getFirstChild() == null) { // check inferred type parameters + final JavaResolveResult resolveResult = mcallRef2.advancedResolve(false); + final PsiMethod targetMethod = (PsiMethod)resolveResult.getElement(); + if (targetMethod == null) { + return false; + } + final PsiTypeParameterList typeParameterList = targetMethod.getTypeParameterList(); + if (typeParameterList == null) { + return false; + } + final PsiTypeParameter[] typeParameters = typeParameterList.getTypeParameters(); + final PsiSubstitutor substitutor = resolveResult.getSubstitutor(); + matchedParameterList = (PsiReferenceParameterList)matchedParameterList.copy(); + for (final PsiTypeParameter typeParameter : typeParameters) { + final PsiType type = substitutor.substitute(typeParameter); + if (type == null) { + return false; + } + final PsiTypeElement matchedTypeElement = JavaPsiFacade.getElementFactory(mcallRef1.getProject()).createTypeElement(type); + matchedParameterList.add(matchedTypeElement); + } + } + final PsiTypeElement[] matchedTypeElements = matchedParameterList.getTypeParameterElements(); + return myMatchingVisitor.matchSequentially(patternTypeElements, matchedTypeElements); + } + @Override public void visitExpressionStatement(final PsiExpressionStatement expr) { final PsiElement other = myMatchingVisitor.getElement(); diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextBase.java b/platform/analysis-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextBase.java index d506f8d1c1db..496aa8f55ac2 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextBase.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextBase.java @@ -412,6 +412,12 @@ public class GlobalInspectionContextBase extends UserDataHolderBase implements G elements.add(manager.createSmartPsiElementPointer(element)); } + cleanupElements(project, runnable, elements); + } + + public static void cleanupElements(@NotNull final Project project, + @Nullable final Runnable runnable, + final List> elements) { Runnable cleanupRunnable = new Runnable() { @Override public void run() { @@ -441,7 +447,7 @@ public class GlobalInspectionContextBase extends UserDataHolderBase implements G } } - public void close(boolean noSuspisiousCodeFound) { + public void close(boolean noSuspisiousCodeFound) { cleanup(); } diff --git a/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java b/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java index 101dc097da03..9b5a6ba6fcb8 100644 --- a/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java +++ b/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java @@ -94,7 +94,7 @@ public class PluginManagerCore { if (ourBuildNumber == null) { ourBuildNumber = BUILD_NUMBER == null ? null : BuildNumber.fromString(BUILD_NUMBER); if (ourBuildNumber == null) { - ourBuildNumber = BuildNumber.fromString("SNAPSHOT"); + ourBuildNumber = BuildNumber.currentVersion(); } } return ourBuildNumber; diff --git a/platform/core-impl/src/com/intellij/openapi/application/TransactionGuardImpl.java b/platform/core-impl/src/com/intellij/openapi/application/TransactionGuardImpl.java index 184ed367e9bb..237df5160ca3 100644 --- a/platform/core-impl/src/com/intellij/openapi/application/TransactionGuardImpl.java +++ b/platform/core-impl/src/com/intellij/openapi/application/TransactionGuardImpl.java @@ -60,6 +60,7 @@ public class TransactionGuardImpl extends TransactionGuard { public void finish() { Queue queue = getQueue(prevTransaction); queue.addAll(myCurrentTransaction.myQueue); + myCurrentTransaction.myQueue = queue; if (!queue.isEmpty()) { pollQueueLater(); } @@ -72,14 +73,7 @@ public class TransactionGuardImpl extends TransactionGuard { @NotNull private Queue getQueue(@Nullable TransactionIdImpl transaction) { - if (transaction == null) { - return myQueue; - } - if (myCurrentTransaction != null && transaction.myStartCounter > myCurrentTransaction.myStartCounter) { - // transaction is finished already, it makes no sense to add to its queue - return myCurrentTransaction.myQueue; - } - return transaction.myQueue; + return transaction == null ? myQueue : transaction.myQueue; } private void pollQueueLater() { @@ -304,7 +298,7 @@ public class TransactionGuardImpl extends TransactionGuard { private static class TransactionIdImpl implements TransactionId { private static final AtomicLong ourTransactionCounter = new AtomicLong(); final long myStartCounter = ourTransactionCounter.getAndIncrement(); - final Queue myQueue = new LinkedBlockingQueue(); + Queue myQueue = new LinkedBlockingQueue(); @Override public String toString() { diff --git a/platform/lang-api/src/com/intellij/refactoring/changeSignature/ChangeInfo.java b/platform/lang-api/src/com/intellij/refactoring/changeSignature/ChangeInfo.java index deee40749a0e..6e37873a3118 100644 --- a/platform/lang-api/src/com/intellij/refactoring/changeSignature/ChangeInfo.java +++ b/platform/lang-api/src/com/intellij/refactoring/changeSignature/ChangeInfo.java @@ -52,13 +52,4 @@ public interface ChangeInfo { String getNewName(); Language getLanguage(); - - /** - * For added parameters, return expression to be inserted as argument at the parameter position in callExpression. - * Based on parameter default value {@link ParameterInfo#getActualValue(PsiElement)} - */ - default PsiElement getActualValue(int i, PsiElement callExpression) { - final ParameterInfo[] parameters = getNewParameters(); - return i < parameters.length ? parameters[i].getActualValue(callExpression) : null; - } } diff --git a/platform/lang-api/src/com/intellij/refactoring/changeSignature/ParameterInfo.java b/platform/lang-api/src/com/intellij/refactoring/changeSignature/ParameterInfo.java index 2a9f07a8e736..61198c412b68 100644 --- a/platform/lang-api/src/com/intellij/refactoring/changeSignature/ParameterInfo.java +++ b/platform/lang-api/src/com/intellij/refactoring/changeSignature/ParameterInfo.java @@ -55,7 +55,7 @@ public interface ParameterInfo { * Could be overridden to provide values which depend on the call site */ @Nullable - default PsiElement getActualValue(PsiElement callExpression) { + default PsiElement getActualValue(PsiElement callExpression, Object substitutor) { return null; } diff --git a/platform/lang-impl/src/com/intellij/application/options/colors/FontOptions.java b/platform/lang-impl/src/com/intellij/application/options/colors/FontOptions.java index 7ac5e87668fb..6a14e51b5b72 100644 --- a/platform/lang-impl/src/com/intellij/application/options/colors/FontOptions.java +++ b/platform/lang-impl/src/com/intellij/application/options/colors/FontOptions.java @@ -211,7 +211,7 @@ public class FontOptions extends JPanel implements OptionsPanel{ Object source = event.getSource(); if (source instanceof FontComboBox) { FontComboBox combo = (FontComboBox)source; - if (combo.isEnabled() && combo.getSelectedItem() != null) { + if (combo.isEnabled() && combo.isShowing() && combo.getSelectedItem() != null) { syncFontFamilies(); } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/folding/impl/CodeFoldingManagerImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/folding/impl/CodeFoldingManagerImpl.java index f7e436086ce3..80ac45003bbe 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/folding/impl/CodeFoldingManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/folding/impl/CodeFoldingManagerImpl.java @@ -186,7 +186,6 @@ public class CodeFoldingManagerImpl extends CodeFoldingManager implements Projec Document document = editor.getDocument(); PsiFile file = PsiDocumentManager.getInstance(myProject).getPsiFile(document); if (file == null || !file.getViewProvider().isPhysical() || !file.isValid()) return; - PsiDocumentManager.getInstance(myProject).commitDocument(document); Editor[] otherEditors = EditorFactory.getInstance().getEditors(document, myProject); if (otherEditors.length == 0 && !editor.isDisposed()) { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/folding/impl/DocumentFoldingInfo.java b/platform/lang-impl/src/com/intellij/codeInsight/folding/impl/DocumentFoldingInfo.java index 294b8ee6544f..b29f4c6a5366 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/folding/impl/DocumentFoldingInfo.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/folding/impl/DocumentFoldingInfo.java @@ -80,9 +80,7 @@ class DocumentFoldingInfo implements JDOMExternalizable, CodeFoldingState { LOG.assertTrue(!editor.isDisposed()); clear(); - PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject); - documentManager.commitDocument(editor.getDocument()); - PsiFile file = documentManager.getPsiFile(editor.getDocument()); + PsiFile file = PsiDocumentManager.getInstance(myProject).getPsiFile(editor.getDocument()); SmartPointerManager smartPointerManager = SmartPointerManager.getInstance(myProject); EditorFoldingInfo info = EditorFoldingInfo.get(editor); diff --git a/platform/lang-impl/src/com/intellij/ide/actions/CreateFileFromTemplateDialog.java b/platform/lang-impl/src/com/intellij/ide/actions/CreateFileFromTemplateDialog.java index 2d93ffec1a14..e584694f946d 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/CreateFileFromTemplateDialog.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/CreateFileFromTemplateDialog.java @@ -59,7 +59,7 @@ public class CreateFileFromTemplateDialog extends DialogWrapper { @Override protected ValidationInfo doValidate() { if (myInputValidator != null) { - final String text = myNameField.getText(); + final String text = myNameField.getText().trim(); final boolean canClose = myInputValidator.canClose(text); if (!canClose) { String errorText = LangBundle.message("incorrect.name"); diff --git a/platform/lang-impl/src/com/intellij/openapi/fileEditor/impl/text/PsiAwareTextEditorProvider.java b/platform/lang-impl/src/com/intellij/openapi/fileEditor/impl/text/PsiAwareTextEditorProvider.java index 89866c888758..c4b72aa189f3 100644 --- a/platform/lang-impl/src/com/intellij/openapi/fileEditor/impl/text/PsiAwareTextEditorProvider.java +++ b/platform/lang-impl/src/com/intellij/openapi/fileEditor/impl/text/PsiAwareTextEditorProvider.java @@ -137,7 +137,6 @@ public class PsiAwareTextEditorProvider extends TextEditorProvider implements As if (FileEditorStateLevel.FULL == level) { // Folding if (project != null && !project.isDisposed() && !editor.isDisposed() && project.isInitialized()) { - PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument()); state.setFoldingState(CodeFoldingManager.getInstance(project).saveFoldingState(editor)); } else { @@ -154,7 +153,7 @@ public class PsiAwareTextEditorProvider extends TextEditorProvider implements As // Folding final CodeFoldingState foldState = state.getFoldingState(); if (project != null && foldState != null) { - PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument()); + LOG.assertTrue(PsiDocumentManager.getInstance(project).isCommitted(editor.getDocument())); editor.getFoldingModel().runBatchFoldingOperation( () -> CodeFoldingManager.getInstance(project).restoreFoldingState(editor, foldState) ); diff --git a/platform/lang-impl/src/com/intellij/refactoring/changeSignature/ParameterTableModelBase.java b/platform/lang-impl/src/com/intellij/refactoring/changeSignature/ParameterTableModelBase.java index 21b1a41b8d6f..b1fd7f19fe38 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/changeSignature/ParameterTableModelBase.java +++ b/platform/lang-impl/src/com/intellij/refactoring/changeSignature/ParameterTableModelBase.java @@ -64,13 +64,7 @@ public abstract class ParameterTableModelBase

, Aspect> diff --git a/platform/lang-impl/src/com/intellij/refactoring/introduceParameterObject/IntroduceParameterObjectDelegate.java b/platform/lang-impl/src/com/intellij/refactoring/introduceParameterObject/IntroduceParameterObjectDelegate.java index e7c53c24066b..def9b138945c 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/introduceParameterObject/IntroduceParameterObjectDelegate.java +++ b/platform/lang-impl/src/com/intellij/refactoring/introduceParameterObject/IntroduceParameterObjectDelegate.java @@ -100,10 +100,12 @@ public abstract class IntroduceParameterObjectDelegate oldMethodParameters); + List oldMethodParameters, + Object substitutor); /** * Pass new parameter infos to the change info constructor which corresponds to the language of this delegate diff --git a/platform/lang-impl/src/com/intellij/refactoring/safeDelete/SafeDeleteProcessor.java b/platform/lang-impl/src/com/intellij/refactoring/safeDelete/SafeDeleteProcessor.java index 41ca52e1b334..caad0352049c 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/safeDelete/SafeDeleteProcessor.java +++ b/platform/lang-impl/src/com/intellij/refactoring/safeDelete/SafeDeleteProcessor.java @@ -313,23 +313,18 @@ public class SafeDeleteProcessor extends BaseRefactoringProcessor { @Override public void run() { - ApplicationManager.getApplication().invokeLater(new Runnable() { - @Override - public void run() { - PsiDocumentManager.getInstance(myProject).commitAllDocuments(); - myUsageView.close(); - ArrayList elements = new ArrayList(); - for (SmartPsiElementPointer pointer : myPointers) { - final PsiElement element = pointer.getElement(); - if (element != null) { - elements.add(element); - } - } - if(!elements.isEmpty()) { - SafeDeleteHandler.invoke(myProject, PsiUtilCore.toPsiElementArray(elements), true); - } - } - }); + PsiDocumentManager.getInstance(myProject).commitAllDocuments(); + myUsageView.close(); + ArrayList elements = new ArrayList(); + for (SmartPsiElementPointer pointer : myPointers) { + final PsiElement element = pointer.getElement(); + if (element != null) { + elements.add(element); + } + } + if(!elements.isEmpty()) { + SafeDeleteHandler.invoke(myProject, PsiUtilCore.toPsiElementArray(elements), true); + } } } diff --git a/platform/platform-api/src/com/intellij/ui/components/JBScrollPane.java b/platform/platform-api/src/com/intellij/ui/components/JBScrollPane.java index c0fd4beeb80c..d84bb3341d77 100644 --- a/platform/platform-api/src/com/intellij/ui/components/JBScrollPane.java +++ b/platform/platform-api/src/com/intellij/ui/components/JBScrollPane.java @@ -17,6 +17,7 @@ package com.intellij.ui.components; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.KeyboardShortcut; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.registry.Registry; @@ -177,8 +178,9 @@ public class JBScrollPane extends JScrollPane { } private static void updateInputMap(InputMap map, String prefix, String... actions) { - if (map != null) { + if (map != null && null != ApplicationManager.getApplication()) { ActionManager manager = ActionManager.getInstance(); + if (manager != null) for (String action : actions) { KeyboardShortcut shortcut = manager.getKeyboardShortcut(prefix + action); if (shortcut != null) map.put(shortcut.getFirstKeyStroke(), action); diff --git a/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java b/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java index b41d5f7292e5..8e51040024b8 100644 --- a/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java +++ b/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -787,7 +787,7 @@ public class IdeEventQueue extends EventQueue { if (e instanceof WindowEvent) { final WindowEvent we = (WindowEvent)e; - ApplicationActivationStateManager.get().updateState(we); + ApplicationActivationStateManager.updateState(we); storeLastFocusedComponent(we); } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java index 1c41c4cf2770..361a49b9fe74 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java @@ -4413,7 +4413,9 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi return; } - if (getMouseEventArea(e) == EditorMouseEventArea.LINE_MARKERS_AREA) { + EditorMouseEventArea eventArea = getMouseEventArea(e); + if (eventArea == EditorMouseEventArea.LINE_MARKERS_AREA || + eventArea == EditorMouseEventArea.FOLDING_OUTLINE_AREA && !isInsideGutterWhitespaceArea(e)) { // The general idea is that we don't want to change caret position on gutter marker area click (e.g. on setting a breakpoint) // but do want to allow bulk selection on gutter marker mouse drag. However, when a drag is performed, the first event is // a 'mouse pressed' event, that's why we remember target line on 'mouse pressed' processing and use that information on @@ -5857,7 +5859,8 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi myExpectedCaretOffset = -1; } - if (event.getArea() == EditorMouseEventArea.LINE_MARKERS_AREA) { + if (event.getArea() == EditorMouseEventArea.LINE_MARKERS_AREA || + event.getArea() == EditorMouseEventArea.FOLDING_OUTLINE_AREA && !isInsideGutterWhitespaceArea(e)) { myDragOnGutterSelectionStartLine = EditorUtil.yPositionToLogicalLine(EditorImpl.this, e); } diff --git a/platform/platform-tests/testSrc/com/intellij/application/TransactionTest.groovy b/platform/platform-tests/testSrc/com/intellij/application/TransactionTest.groovy index b810c6eacc27..1b68f58338bd 100644 --- a/platform/platform-tests/testSrc/com/intellij/application/TransactionTest.groovy +++ b/platform/platform-tests/testSrc/com/intellij/application/TransactionTest.groovy @@ -216,4 +216,18 @@ class TransactionTest extends LightPlatformTestCase { assert log == ['1', '2', '3', '4', '5'] } + public void "test don't add transaction to outdated queue"() { + TransactionGuard.submitTransaction testRootDisposable, { + log << '1' + guard.submitTransactionLater testRootDisposable, { log << '3' } + log << '2' + } + TransactionGuard.submitTransaction testRootDisposable, { + UIUtil.dispatchAllInvocationEvents() + assert log == ['1', '2'] + } + UIUtil.dispatchAllInvocationEvents() + assert log == ['1', '2', '3'] + } + } diff --git a/platform/platform-tests/testSrc/com/intellij/util/containers/TreeTraverserTest.java b/platform/platform-tests/testSrc/com/intellij/util/containers/TreeTraverserTest.java index d3cd404e9312..f04035c86f8c 100644 --- a/platform/platform-tests/testSrc/com/intellij/util/containers/TreeTraverserTest.java +++ b/platform/platform-tests/testSrc/com/intellij/util/containers/TreeTraverserTest.java @@ -23,6 +23,7 @@ import junit.framework.TestCase; import org.jetbrains.annotations.NotNull; import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; import static com.intellij.openapi.util.Conditions.not; @@ -227,6 +228,20 @@ public class TreeTraverserTest extends TestCase { assertEquals(Arrays.asList(1, 2), list); } + public void testIteratorContractsSkipAndStop() { + final AtomicInteger count = new AtomicInteger(0); + JBIterator it = new JBIterator() { + + @Override + protected Integer nextImpl() { + return count.get() < 0 ? stop() : + count.incrementAndGet() < 10 ? skip() : + (Integer)count.addAndGet(-count.get() - 1); + } + }; + assertEquals(JBIterable.of(-1).toList(), JBIterable.once(it).toList()); + } + // JBIterable ---------------------------------------------- public void testAppend() { diff --git a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/GeneralIdBasedToSMTRunnerEventsConvertor.java b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/GeneralIdBasedToSMTRunnerEventsConvertor.java index 8cbb3fc8bcea..2646cceaa08d 100644 --- a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/GeneralIdBasedToSMTRunnerEventsConvertor.java +++ b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/GeneralIdBasedToSMTRunnerEventsConvertor.java @@ -270,7 +270,9 @@ public class GeneralIdBasedToSMTRunnerEventsConvertor extends GeneralTestEventsP String stackTrace = testFailedEvent.getStacktrace(); if (comparisonFailureActualText != null && comparisonFailureExpectedText != null) { testProxy.setTestComparisonFailed(failureMessage, stackTrace, - comparisonFailureActualText, comparisonFailureExpectedText, testFailedEvent.getFilePath()); + comparisonFailureActualText, comparisonFailureExpectedText, + testFailedEvent.getExpectedFilePath(), + testFailedEvent.getActualFilePath()); } else if (comparisonFailureActualText == null && comparisonFailureExpectedText == null) { testProxy.setTestFailed(failureMessage, stackTrace, testFailedEvent.isTestError()); } else { diff --git a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/GeneralToSMTRunnerEventsConvertor.java b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/GeneralToSMTRunnerEventsConvertor.java index b8a37e359518..f663eac29541 100644 --- a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/GeneralToSMTRunnerEventsConvertor.java +++ b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/GeneralToSMTRunnerEventsConvertor.java @@ -409,7 +409,7 @@ public class GeneralToSMTRunnerEventsConvertor extends GeneralTestEventsProcesso if (comparisionFailureActualText != null && comparisionFailureExpectedText != null) { testProxy.setTestComparisonFailed(localizedMessage, stackTrace, comparisionFailureActualText, comparisionFailureExpectedText, - testFailedEvent.getFilePath(), testFailedEvent.getActualFilePath()); + testFailedEvent.getExpectedFilePath(), testFailedEvent.getActualFilePath()); } else if (comparisionFailureActualText == null && comparisionFailureExpectedText == null) { testProxy.setTestFailed(localizedMessage, stackTrace, isTestError); diff --git a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/events/TestFailedEvent.java b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/events/TestFailedEvent.java index 9065828abdc7..a24f824c129c 100644 --- a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/events/TestFailedEvent.java +++ b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/events/TestFailedEvent.java @@ -27,17 +27,20 @@ public class TestFailedEvent extends TreeNodeEvent { private final boolean myTestError; private final String myComparisonFailureActualText; private final String myComparisonFailureExpectedText; - private final String myFilePath; + private final String myExpectedFilePath; private final String myActualFilePath; private final long myDurationMillis; public TestFailedEvent(@NotNull TestFailed testFailed, boolean testError) { this(testFailed, testError, null); } - public TestFailedEvent(@NotNull TestFailed testFailed, boolean testError, String filePath) { - this(testFailed, testError, filePath, null); + public TestFailedEvent(@NotNull TestFailed testFailed, boolean testError, @Nullable String expectedFilePath) { + this(testFailed, testError, expectedFilePath, null); } - public TestFailedEvent(@NotNull TestFailed testFailed, boolean testError, String filePath, String actualFilePath) { + public TestFailedEvent(@NotNull TestFailed testFailed, + boolean testError, + @Nullable String expectedFilePath, + @Nullable String actualFilePath) { super(testFailed.getTestName(), TreeNodeEvent.getNodeId(testFailed)); if (testFailed.getFailureMessage() == null) throw new NullPointerException(); myLocalizedFailureMessage = testFailed.getFailureMessage(); @@ -45,7 +48,7 @@ public class TestFailedEvent extends TreeNodeEvent { myTestError = testError; myComparisonFailureActualText = testFailed.getActual(); myComparisonFailureExpectedText = testFailed.getExpected(); - myFilePath = filePath; + myExpectedFilePath = expectedFilePath; myActualFilePath = actualFilePath; myDurationMillis = parseDuration(testFailed.getAttributes().get("duration")); } @@ -85,7 +88,7 @@ public class TestFailedEvent extends TreeNodeEvent { boolean testError, @Nullable String comparisonFailureActualText, @Nullable String comparisonFailureExpectedText, - @Nullable String expectedTextFilePath, + @Nullable String expectedFilePath, long durationMillis) { super(testName, id); myLocalizedFailureMessage = localizedFailureMessage; @@ -93,7 +96,7 @@ public class TestFailedEvent extends TreeNodeEvent { myTestError = testError; myComparisonFailureActualText = comparisonFailureActualText; myComparisonFailureExpectedText = comparisonFailureExpectedText; - myFilePath = expectedTextFilePath; + myExpectedFilePath = expectedFilePath; myActualFilePath = null; myDurationMillis = durationMillis; } @@ -131,10 +134,19 @@ public class TestFailedEvent extends TreeNodeEvent { append(buf, "comparisonFailureExpectedText", myComparisonFailureExpectedText); } + /** + * @deprecated use {@link #getExpectedFilePath()} instead + */ public String getFilePath() { - return myFilePath; + return myExpectedFilePath; } + @Nullable + public String getExpectedFilePath() { + return myExpectedFilePath; + } + + @Nullable public String getActualFilePath() { return myActualFilePath; } diff --git a/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java b/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java index 73a8eff7fa73..1d33cca4914b 100644 --- a/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java +++ b/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java @@ -3443,4 +3443,23 @@ public class StructuralSearchTest extends StructuralSearchTestCase { assertEquals("any variable can be final", 3, findMatchesCount(source, "@Modifier(\"final\") '_T '_a;")); assertEquals("parameters and local variables are not instance fields", 1, findMatchesCount(source, "@Modifier(\"Instance\") '_T '_a;")); } + + public void testFindParameterizedMethodCalls() { + String source = "interface Foo {" + + " T bar();" + + " void bar2(S, T);" + + "}" + + "class X {" + + " void x(Foo foo) {" + + " foo.bar();" + + " foo.bar();" + + " String s = foo.bar();" + + " foo.bar2(1, 2);" + + " }" + + "}"; + assertEquals("find parameterized method calls 1", 1, findMatchesCount(source, "foo.bar()")); + assertEquals("find parameterized method calls 2", 2, findMatchesCount(source, "foo.bar()")); + assertEquals("find parameterized method calls 3", 3, findMatchesCount(source, "'_a.<'_b>'_c('_d*)")); + assertEquals("find parameterized method calls 4", 4, findMatchesCount(source, "'_a.<'_b+>'_c('_d*)")); + } } diff --git a/platform/util/src/com/intellij/openapi/util/BuildNumber.java b/platform/util/src/com/intellij/openapi/util/BuildNumber.java index c736162a16e0..73a0bec3492a 100644 --- a/platform/util/src/com/intellij/openapi/util/BuildNumber.java +++ b/platform/util/src/com/intellij/openapi/util/BuildNumber.java @@ -96,7 +96,7 @@ public class BuildNumber implements Comparable { if (BUILD_NUMBER.equals(version) || SNAPSHOT.equals(version)) { final String productCode = name != null ? name : ""; - return new BuildNumber(productCode, Holder.CURRENT_VERSION.myComponents); + return new BuildNumber(productCode, currentVersion().myComponents); } String code = version; @@ -171,9 +171,17 @@ public class BuildNumber implements Comparable { } catch (IOException ignored) { } - return fallback(); + return fromString(FALLBACK_VERSION); } + /** + * This method is for internal platform use only. In regular code use {@link com.intellij.openapi.application.ApplicationInfo#getBuild()} instead. + */ + public static BuildNumber currentVersion() { + return Holder.CURRENT_VERSION; + } + + @Deprecated public static BuildNumber fallback() { return fromString(FALLBACK_VERSION); } diff --git a/platform/util/src/com/intellij/util/containers/JBIterator.java b/platform/util/src/com/intellij/util/containers/JBIterator.java index c14a398ce6fb..8caa1e2140d7 100644 --- a/platform/util/src/com/intellij/util/containers/JBIterator.java +++ b/platform/util/src/com/intellij/util/containers/JBIterator.java @@ -45,7 +45,7 @@ import java.util.NoSuchElementException; * * @author gregsh * - * @noinspection unchecked + * @noinspection unchecked, AssignmentToForLoopParameter */ public abstract class JBIterator implements Iterator { private static final Object NONE = new String("#none"); @@ -137,18 +137,14 @@ public abstract class JBIterator implements Iterator { private void peekNext() { if (myNext != NONE) return; - Object o = nextImpl(); - if (myNext == STOP) return; - Op op = myFirstOp.nextOp; - while (op != null) { - o = op.apply(o); + Object o = NONE; + for (Op op = myFirstOp; op != null; op = op == null ? myFirstOp : op.nextOp) { + o = op == myFirstOp ? nextImpl() : op.apply(o); if (myNext == SKIP) { - myNext = NONE; - o = nextImpl(); - op = myFirstOp; + o = myNext = NONE; + op = null; } if (myNext == STOP) return; - op = op.nextOp; } myNext = o; } diff --git a/platform/util/src/com/intellij/util/ui/ListTableModel.java b/platform/util/src/com/intellij/util/ui/ListTableModel.java index bdd04ec399ac..28386e00e89e 100644 --- a/platform/util/src/com/intellij/util/ui/ListTableModel.java +++ b/platform/util/src/com/intellij/util/ui/ListTableModel.java @@ -113,10 +113,23 @@ public class ListTableModel extends TableViewModel implements Editab @Override public void setValueAt(Object aValue, int rowIndex, int columnIndex) { + setValueAt(aValue, rowIndex, columnIndex, true); + } + + /** + * Sets the value in the cell at {@code columnIndex} and {@code rowIndex} to {@code aValue}. + * This method allows to choose will the model listeners notified or not. + * + * @param aValue the new value + * @param rowIndex the row whose value is to be changed + * @param columnIndex the column whose value is to be changed + * @param notifyListeners indicates whether the model listeners are notified + */ + public void setValueAt(Object aValue, int rowIndex, int columnIndex, boolean notifyListeners) { if (rowIndex < myItems.size()) { myColumnInfos[columnIndex].setValue(getItem(rowIndex), aValue); } - fireTableCellUpdated(rowIndex, columnIndex); + if (notifyListeners) fireTableCellUpdated(rowIndex, columnIndex); } /** diff --git a/platform/util/testSrc/com/intellij/openapi/util/BuildNumberTest.java b/platform/util/testSrc/com/intellij/openapi/util/BuildNumberTest.java index 2a49881972ec..cb91d33654c4 100644 --- a/platform/util/testSrc/com/intellij/openapi/util/BuildNumberTest.java +++ b/platform/util/testSrc/com/intellij/openapi/util/BuildNumberTest.java @@ -129,12 +129,12 @@ public class BuildNumberTest { } @Test - public void fallbackVersion() throws Exception { - assertParsed(BuildNumber.fallback(), 999, BuildNumber.SNAPSHOT_VALUE, "999.SNAPSHOT"); - assertTrue(BuildNumber.fallback().isSnapshot()); + public void currentVersion() throws Exception { + BuildNumber current = BuildNumber.currentVersion(); + assertTrue(current.isSnapshot()); - assertTrue(BuildNumber.fallback().compareTo(BuildNumber.fromString("7512")) > 0); - assertTrue(BuildNumber.fallback().compareTo(BuildNumber.fromString("145")) > 0); - assertTrue(BuildNumber.fallback().compareTo(BuildNumber.fromString("145.12")) > 0); + assertTrue(current.compareTo(BuildNumber.fromString("7512")) > 0); + assertTrue(current.compareTo(BuildNumber.fromString("145")) > 0); + assertTrue(current.compareTo(BuildNumber.fromString("145.12")) > 0); } } \ No newline at end of file diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/fixes/ConvertToVarargsMethodFix.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/fixes/ConvertToVarargsMethodFix.java index 2c0212fafc2f..384b63aa7545 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/fixes/ConvertToVarargsMethodFix.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/fixes/ConvertToVarargsMethodFix.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -20,8 +20,6 @@ import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.search.searches.ReferencesSearch; -import com.intellij.util.IncorrectOperationException; -import com.intellij.util.Query; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.InspectionGadgetsFix; import org.jetbrains.annotations.NotNull; @@ -90,7 +88,7 @@ public class ConvertToVarargsMethodFix extends InspectionGadgetsFix { final PsiArrayType arrayType = (PsiArrayType)type; final PsiType componentType = arrayType.getComponentType(); final PsiElementFactory factory = JavaPsiFacade.getElementFactory(method.getProject()); - final PsiType ellipsisType = PsiEllipsisType.createEllipsis(componentType, type.getAnnotations()); + final PsiType ellipsisType = new PsiEllipsisType(componentType, TypeAnnotationProvider.Static.create(type.getAnnotations())); final PsiTypeElement newTypeElement = factory.createTypeElement(ellipsisType); final PsiTypeElement typeElement = lastParameter.getTypeElement(); if (typeElement != null) { diff --git a/plugins/InspectionGadgets/src/com/intellij/codeInspection/TrivialFunctionalExpressionUsageInspection.java b/plugins/InspectionGadgets/src/com/intellij/codeInspection/TrivialFunctionalExpressionUsageInspection.java index 2e3814f14768..74d527aab46d 100644 --- a/plugins/InspectionGadgets/src/com/intellij/codeInspection/TrivialFunctionalExpressionUsageInspection.java +++ b/plugins/InspectionGadgets/src/com/intellij/codeInspection/TrivialFunctionalExpressionUsageInspection.java @@ -39,36 +39,35 @@ public class TrivialFunctionalExpressionUsageInspection extends BaseJavaBatchLoc @Override public void visitMethodReferenceExpression(final PsiMethodReferenceExpression expression) { - doCheckMethodCallOnFunctionalExpression(expression, new Condition() { - @Override - public boolean value(PsiElement element) { - return expression.resolve() != null; - } - }); + doCheckMethodCallOnFunctionalExpression(expression, element -> expression.resolve() != null); } @Override public void visitLambdaExpression(final PsiLambdaExpression expression) { - doCheckMethodCallOnFunctionalExpression(expression, new Condition() { - @Override - public boolean value(PsiElement ggParent) { - final PsiElement body = expression.getBody(); - if (!(body instanceof PsiCodeBlock) || - ((PsiCodeBlock)body).getStatements().length == 1) { - return true; - } - final List returnExpressions = LambdaUtil.getReturnExpressions(expression); - if (returnExpressions.size() > 1) { - return false; - } - final PsiElement callParent = ggParent.getParent(); - if (returnExpressions.isEmpty()) { - return callParent instanceof PsiStatement; - } - return callParent instanceof PsiStatement || - callParent instanceof PsiLocalVariable || - callParent instanceof PsiExpression && PsiTreeUtil.getParentOfType(callParent, PsiStatement.class) != null; + doCheckMethodCallOnFunctionalExpression(expression, ggParent -> { + final PsiElement callParent = ggParent.getParent(); + + final PsiElement body = expression.getBody(); + if (!(body instanceof PsiCodeBlock)) { + return callParent instanceof PsiStatement || callParent instanceof PsiLocalVariable || expression.isValueCompatible(); } + + if (((PsiCodeBlock)body).getStatements().length == 1) { + return callParent instanceof PsiStatement + || callParent instanceof PsiLocalVariable + || ((PsiCodeBlock)body).getStatements()[0] instanceof PsiReturnStatement && expression.isValueCompatible(); + } + + final List returnExpressions = LambdaUtil.getReturnExpressions(expression); + if (returnExpressions.size() > 1) { + return false; + } + + if (returnExpressions.isEmpty()) { + return callParent instanceof PsiStatement; + } + return callParent instanceof PsiStatement || + callParent instanceof PsiLocalVariable; }); } @@ -76,20 +75,17 @@ public class TrivialFunctionalExpressionUsageInspection extends BaseJavaBatchLoc public void visitAnonymousClass(final PsiAnonymousClass aClass) { if (AnonymousCanBeLambdaInspection.canBeConvertedToLambda(aClass, false, Collections.emptySet())) { final PsiElement newExpression = aClass.getParent(); - doCheckMethodCallOnFunctionalExpression(new Condition() { - @Override - public boolean value(PsiElement ggParent) { - final PsiMethod method = aClass.getMethods()[0]; - final PsiCodeBlock body = method.getBody(); - final PsiReturnStatement[] returnStatements = PsiUtil.findReturnStatements(body); - if (returnStatements.length > 1) { - return false; - } - final PsiElement callParent = ggParent.getParent(); - return callParent instanceof PsiStatement || - callParent instanceof PsiLocalVariable; + doCheckMethodCallOnFunctionalExpression(ggParent -> { + final PsiMethod method = aClass.getMethods()[0]; + final PsiCodeBlock body = method.getBody(); + final PsiReturnStatement[] returnStatements = PsiUtil.findReturnStatements(body); + if (returnStatements.length > 1) { + return false; } - }, newExpression, aClass.getBaseClassType(), "Replace call with method body"); + final PsiElement callParent = ggParent.getParent(); + return callParent instanceof PsiStatement || + callParent instanceof PsiLocalVariable; + }, newExpression, aClass.getBaseClassType(), new ReplaceAnonymousWithLambdaBodyFix()); } } @@ -99,16 +95,15 @@ public class TrivialFunctionalExpressionUsageInspection extends BaseJavaBatchLoc if (parent instanceof PsiTypeCastExpression) { final PsiType interfaceType = ((PsiTypeCastExpression)parent).getType(); doCheckMethodCallOnFunctionalExpression(elementContainerCondition, parent, interfaceType, - "Replace method call " + - (expression instanceof PsiLambdaExpression ? "on lambda with lambda body" - : "on method reference with corresponding method call")); + expression instanceof PsiLambdaExpression ? new ReplaceWithLambdaBodyFix() + : new ReplaceWithMethodReferenceFix()); } } private void doCheckMethodCallOnFunctionalExpression(Condition elementContainerCondition, PsiElement parent, PsiType interfaceType, - String quickFixName) { + LocalQuickFix fix) { final PsiElement gParent = PsiUtil.skipParenthesizedExprUp(parent.getParent()); if (gParent instanceof PsiReferenceExpression) { final PsiElement ggParent = gParent.getParent(); @@ -123,8 +118,6 @@ public class TrivialFunctionalExpressionUsageInspection extends BaseJavaBatchLoc final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(interfaceType); if (resolveMethod == interfaceMethod || interfaceMethod != null && MethodSignatureUtil.isSuperMethod(interfaceMethod, resolveMethod)) { - final ReplaceWithLambdaBodyFix fix = - new ReplaceWithLambdaBodyFix(quickFixName); holder.registerProblem(referenceNameElement, "Method call can be simplified", fix); } } @@ -134,25 +127,145 @@ public class TrivialFunctionalExpressionUsageInspection extends BaseJavaBatchLoc }; } - private static class ReplaceWithLambdaBodyFix implements LocalQuickFix { - private String myName; + private static void replaceWithLambdaBody(PsiMethodCallExpression callExpression, PsiLambdaExpression element) { + inlineCallArguments(callExpression, element); - public ReplaceWithLambdaBodyFix(String name) { - myName = name; + final PsiElement body = element.getBody(); + if (body instanceof PsiExpression) { + callExpression.replace(body); } + else if (body instanceof PsiCodeBlock) { + final PsiElement parent = callExpression.getParent(); + if (parent instanceof PsiStatement) { + final PsiElement gParent = parent.getParent(); + restoreComments(gParent, parent, body); + for (PsiStatement statement : ((PsiCodeBlock)body).getStatements()) { + PsiElement toInsert; + if (statement instanceof PsiReturnStatement) { + toInsert = ((PsiReturnStatement)statement).getReturnValue(); + } + else { + toInsert = statement; + } - @Nls - @NotNull - @Override - public String getName() { - return getFamilyName(); + if (toInsert != null) { + gParent.addBefore(toInsert, parent); + } + } + parent.delete(); + } + else { + final PsiStatement[] statements = ((PsiCodeBlock)body).getStatements(); + if (statements.length > 0) { + final PsiStatement anchor = PsiTreeUtil.getParentOfType(parent, PsiStatement.class); + if (anchor != null) { + final PsiElement gParent = anchor.getParent(); + restoreComments(gParent, anchor, body); + for (int i = 0; i < statements.length - 1; i++) { + gParent.addBefore(statements[i], anchor); + } + } + PsiStatement statement = statements[statements.length - 1]; + final PsiExpression returnValue = ((PsiReturnStatement)statement).getReturnValue(); + if (returnValue != null) { + callExpression.replace(returnValue); + } + } + } } + } + + private static void restoreComments(PsiElement gParent, PsiElement parent, PsiElement body) { + for (PsiElement comment : PsiTreeUtil.findChildrenOfType(body, PsiComment.class)) { + gParent.addBefore(comment, parent); + } + } + + private static void inlineCallArguments(PsiMethodCallExpression callExpression, PsiLambdaExpression element) { + final PsiExpression[] args = callExpression.getArgumentList().getExpressions(); + final PsiParameter[] parameters = element.getParameterList().getParameters(); + for (int i = 0; i < parameters.length; i++) { + final PsiParameter parameter = parameters[i]; + final PsiExpression initializer = args[i]; + for (PsiReference reference : ReferencesSearch.search(parameter)) { + final PsiElement referenceElement = reference.getElement(); + if (referenceElement instanceof PsiJavaCodeReferenceElement) { + InlineUtil.inlineVariable(parameter, initializer, (PsiJavaCodeReferenceElement)referenceElement); + } + } + } + } + + private static class ReplaceWithLambdaBodyFix extends ReplaceFix { @Nls @NotNull @Override public String getFamilyName() { - return myName; + return "Replace method call on lambda with lambda body"; + } + + @Override + protected void fixExpression(PsiMethodCallExpression callExpression, PsiExpression qualifierExpression) { + if (qualifierExpression instanceof PsiTypeCastExpression) { + final PsiExpression element = ((PsiTypeCastExpression)qualifierExpression).getOperand(); + if (element instanceof PsiLambdaExpression) { + replaceWithLambdaBody(callExpression, (PsiLambdaExpression)element); + } + } + } + } + + private static class ReplaceWithMethodReferenceFix extends ReplaceFix { + @Nls + @NotNull + @Override + public String getFamilyName() { + return "Replace method call on method reference with corresponding method call"; + } + + @Override + protected void fixExpression(PsiMethodCallExpression callExpression, PsiExpression qualifierExpression) { + if (qualifierExpression instanceof PsiTypeCastExpression) { + final PsiExpression element = ((PsiTypeCastExpression)qualifierExpression).getOperand(); + if (element instanceof PsiMethodReferenceExpression) { + final PsiLambdaExpression lambdaExpression = + LambdaRefactoringUtil.convertMethodReferenceToLambda((PsiMethodReferenceExpression)element, false, true); + if (lambdaExpression != null) { + replaceWithLambdaBody(callExpression, lambdaExpression); + } + } + } + } + } + + private static class ReplaceAnonymousWithLambdaBodyFix extends ReplaceFix { + + @Nls + @NotNull + @Override + public String getFamilyName() { + return "Replace call with method body"; + } + + @Override + protected void fixExpression(PsiMethodCallExpression callExpression, PsiExpression qualifierExpression) { + final PsiExpression cast = AnonymousCanBeLambdaInspection.replacePsiElementWithLambda(qualifierExpression, true, false); + if (cast instanceof PsiTypeCastExpression) { + final PsiExpression lambdaExpression = ((PsiTypeCastExpression)cast).getOperand(); + if (lambdaExpression instanceof PsiLambdaExpression) { + replaceWithLambdaBody(callExpression, (PsiLambdaExpression)lambdaExpression); + } + } + } + } + + private static abstract class ReplaceFix implements LocalQuickFix { + @Nls + @NotNull + @Override + public String getName() { + return getFamilyName(); } @Override @@ -161,98 +274,10 @@ public class TrivialFunctionalExpressionUsageInspection extends BaseJavaBatchLoc if (!FileModificationService.getInstance().preparePsiElementForWrite(psiElement)) return; final PsiMethodCallExpression callExpression = PsiTreeUtil.getParentOfType(psiElement, PsiMethodCallExpression.class); if (callExpression != null) { - final PsiExpression qualifierExpression = PsiUtil.skipParenthesizedExprDown(callExpression.getMethodExpression().getQualifierExpression()); - if (qualifierExpression instanceof PsiTypeCastExpression) { - final PsiExpression element = ((PsiTypeCastExpression)qualifierExpression).getOperand(); - if (element instanceof PsiLambdaExpression) { - replaceWithLambdaBody(callExpression, (PsiLambdaExpression)element); - } - else if (element instanceof PsiMethodReferenceExpression) { - final PsiLambdaExpression lambdaExpression = - LambdaRefactoringUtil.convertMethodReferenceToLambda((PsiMethodReferenceExpression)element, false, true); - if (lambdaExpression != null) { - replaceWithLambdaBody(callExpression, lambdaExpression); - } - } - } - else if (qualifierExpression instanceof PsiNewExpression) { - final PsiExpression cast = AnonymousCanBeLambdaInspection.replacePsiElementWithLambda(qualifierExpression, true, false); - if (cast instanceof PsiTypeCastExpression) { - final PsiExpression lambdaExpression = ((PsiTypeCastExpression)cast).getOperand(); - if (lambdaExpression instanceof PsiLambdaExpression) { - replaceWithLambdaBody(callExpression, (PsiLambdaExpression)lambdaExpression); - } - } - } + fixExpression(callExpression, PsiUtil.skipParenthesizedExprDown(callExpression.getMethodExpression().getQualifierExpression())); } } - private static void replaceWithLambdaBody(PsiMethodCallExpression callExpression, PsiLambdaExpression element) { - inlineCallArguments(callExpression, element); - - final PsiElement body = element.getBody(); - if (body instanceof PsiExpression) { - callExpression.replace(body); - } - else if (body instanceof PsiCodeBlock) { - final PsiElement parent = callExpression.getParent(); - if (parent instanceof PsiStatement) { - final PsiElement gParent = parent.getParent(); - restoreComments(gParent, parent, body); - for (PsiStatement statement : ((PsiCodeBlock)body).getStatements()) { - PsiElement toInsert; - if (statement instanceof PsiReturnStatement) { - toInsert = ((PsiReturnStatement)statement).getReturnValue(); - } - else { - toInsert = statement; - } - - if (toInsert != null) { - gParent.addBefore(toInsert, parent); - } - } - parent.delete(); - } - else { - final PsiStatement[] statements = ((PsiCodeBlock)body).getStatements(); - if (statements.length > 0) { - final PsiStatement anchor = PsiTreeUtil.getParentOfType(parent, PsiStatement.class); - if (anchor != null) { - final PsiElement gParent = anchor.getParent(); - restoreComments(gParent, anchor, body); - for (int i = 0; i < statements.length - 1; i++) { - gParent.addBefore(statements[i], anchor); - } - } - final PsiExpression returnValue = ((PsiReturnStatement)statements[statements.length - 1]).getReturnValue(); - if (returnValue != null) { - callExpression.replace(returnValue); - } - } - } - } - } - - private static void restoreComments(PsiElement gParent, PsiElement parent, PsiElement body) { - for (PsiElement comment : PsiTreeUtil.findChildrenOfType(body, PsiComment.class)) { - gParent.addBefore(comment, parent); - } - } - - private static void inlineCallArguments(PsiMethodCallExpression callExpression, PsiLambdaExpression element) { - final PsiExpression[] args = callExpression.getArgumentList().getExpressions(); - final PsiParameter[] parameters = element.getParameterList().getParameters(); - for (int i = 0; i < parameters.length; i++) { - final PsiParameter parameter = parameters[i]; - final PsiExpression initializer = args[i]; - for (PsiReference reference : ReferencesSearch.search(parameter)) { - final PsiElement referenceElement = reference.getElement(); - if (referenceElement instanceof PsiJavaCodeReferenceElement) { - InlineUtil.inlineVariable(parameter, initializer, (PsiJavaCodeReferenceElement)referenceElement); - } - } - } - } + protected abstract void fixExpression(PsiMethodCallExpression callExpression, PsiExpression qualifierExpression); } } diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/varargs/MakeMethodVarargsIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/varargs/MakeMethodVarargsIntention.java index 2b0617c673a6..24881155584b 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/varargs/MakeMethodVarargsIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/varargs/MakeMethodVarargsIntention.java @@ -48,7 +48,7 @@ public class MakeMethodVarargsIntention extends Intention { final PsiElementFactory factory = JavaPsiFacade.getInstance(element.getProject()).getElementFactory(); final PsiTypeElement typeElement = lastParameter.getTypeElement(); LOG.assertTrue(typeElement != null); - final PsiType ellipsisType = PsiEllipsisType.createEllipsis(((PsiArrayType)type).getComponentType(), type.getAnnotations()); + final PsiType ellipsisType = new PsiEllipsisType(((PsiArrayType)type).getComponentType(), TypeAnnotationProvider.Static.create(type.getAnnotations())); typeElement.replace(factory.createTypeElement(ellipsisType)); } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyPsiElementFactoryImpl.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyPsiElementFactoryImpl.java index 2a2db5ecaeb9..33003a77b739 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyPsiElementFactoryImpl.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyPsiElementFactoryImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -1131,6 +1131,7 @@ public class GroovyPsiElementFactoryImpl extends GroovyPsiElementFactory { return JavaPsiFacade.getElementFactory(myProject).createType(resolve, substitutor, languageLevel); } + @SuppressWarnings("deprecation") @NotNull @Override public PsiClassType createType(@NotNull PsiClass resolve, diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/changeSignature/GrChangeInfoImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/changeSignature/GrChangeInfoImpl.java index 63ac08423815..e8e1fe8f2d75 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/changeSignature/GrChangeInfoImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/changeSignature/GrChangeInfoImpl.java @@ -336,7 +336,7 @@ public class GrChangeInfoImpl implements JavaChangeInfo { @Override public PsiExpression getValue(int i, PsiCallExpression callExpression) { if (defaultValues[i] != null) return defaultValues[i]; - final PsiElement valueAtCallSite = parameters.get(i).getActualValue(callExpression); + final PsiElement valueAtCallSite = parameters.get(i).getActualValue(callExpression, PsiSubstitutor.EMPTY); return valueAtCallSite instanceof PsiExpression ? (PsiExpression)valueAtCallSite : null; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/changeSignature/GrChangeSignatureUsageProcessor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/changeSignature/GrChangeSignatureUsageProcessor.java index 63e9e67d663f..312a53febdeb 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/changeSignature/GrChangeSignatureUsageProcessor.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/changeSignature/GrChangeSignatureUsageProcessor.java @@ -529,7 +529,7 @@ public class GrChangeSignatureUsageProcessor implements ChangeSignatureUsageProc argsToDelete.removeAll(map[index].args); } else { - values[i] = createDefaultValue(factory, changeInfo, parameter, argumentList); + values[i] = createDefaultValue(factory, changeInfo, parameter, argumentList, substitutor); } } @@ -640,7 +640,8 @@ public class GrChangeSignatureUsageProcessor implements ChangeSignatureUsageProc private static GrExpression createDefaultValue(GroovyPsiElementFactory factory, JavaChangeInfo changeInfo, JavaParameterInfo info, - final GrArgumentList list) { + final GrArgumentList list, + PsiSubstitutor substitutor) { if (info.isUseAnySingleVariable()) { final PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(list.getProject()).getResolveHelper(); final PsiType type = info.getTypeWrapper().getType(changeInfo.getMethod(), list.getManager()); @@ -689,7 +690,7 @@ public class GrChangeSignatureUsageProcessor implements ChangeSignatureUsageProc } } - final PsiElement element = info.getActualValue(list.getParent()); + final PsiElement element = info.getActualValue(list.getParent(), substitutor); if (element instanceof GrExpression) { return (GrExpression)element; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduceParameterObject/GroovyIntroduceParameterObjectDelegate.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduceParameterObject/GroovyIntroduceParameterObjectDelegate.java index 63d8b3bdc52f..3985d2d2aa3f 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduceParameterObject/GroovyIntroduceParameterObjectDelegate.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduceParameterObject/GroovyIntroduceParameterObjectDelegate.java @@ -76,10 +76,10 @@ public class GroovyIntroduceParameterObjectDelegate return new GrParameterInfo(descriptor.getClassName(), null, null, classType, -1, false) { @Nullable @Override - public PsiElement getActualValue(PsiElement callExpression) { + public PsiElement getActualValue(PsiElement callExpression, Object substitutor) { final IntroduceParameterObjectDelegate> delegate = findDelegate(callExpression); - return delegate != null ? delegate.createNewParameterInitializerAtCallSite(callExpression, descriptor, oldMethodParameters) : null; + return delegate != null ? delegate.createNewParameterInitializerAtCallSite(callExpression, descriptor, oldMethodParameters, substitutor) : null; } }; } @@ -87,7 +87,8 @@ public class GroovyIntroduceParameterObjectDelegate @Override public PsiElement createNewParameterInitializerAtCallSite(PsiElement callExpression, IntroduceParameterObjectClassDescriptor descriptor, - List oldMethodParameters) { + List oldMethodParameters, + Object substitutor) { if (callExpression instanceof GrCallExpression) { final GrArgumentList list = ((GrCallExpression)callExpression).getArgumentList(); if (list == null) { diff --git a/plugins/ui-designer-core/src/META-INF/DesignerCorePlugin.xml b/plugins/ui-designer-core/src/META-INF/DesignerCorePlugin.xml index ede0d2b92f01..499619eea8eb 100644 --- a/plugins/ui-designer-core/src/META-INF/DesignerCorePlugin.xml +++ b/plugins/ui-designer-core/src/META-INF/DesignerCorePlugin.xml @@ -7,9 +7,11 @@ com.intellij.designer.DesignerToolWindowManager + com.intellij.designer.palette.PaletteToolWindowManager + diff --git a/plugins/xpath/xpath-view/src/org/intellij/plugins/xpathView/XPathEvalAction.java b/plugins/xpath/xpath-view/src/org/intellij/plugins/xpathView/XPathEvalAction.java index c1d5cb386ce0..53acb8928f79 100644 --- a/plugins/xpath/xpath-view/src/org/intellij/plugins/xpathView/XPathEvalAction.java +++ b/plugins/xpath/xpath-view/src/org/intellij/plugins/xpathView/XPathEvalAction.java @@ -550,14 +550,7 @@ public class XPathEvalAction extends XPathAction { public abstract static class EditExpressionAction implements Runnable { @Override public void run() { - Runnable runnable = new Runnable() { - @Override - public void run() { - execute(); - } - - }; - SwingUtilities.invokeLater(runnable); + execute(); } protected abstract void execute(); diff --git a/python/educational-core/course-creator/resources/META-INF/plugin.xml b/python/educational-core/course-creator/resources/META-INF/plugin.xml index 131d9f5b46d8..421b23fef48e 100644 --- a/python/educational-core/course-creator/resources/META-INF/plugin.xml +++ b/python/educational-core/course-creator/resources/META-INF/plugin.xml @@ -19,18 +19,17 @@ - - - - + + + + + + + + - - - - - com.jetbrains.edu.coursecreator.CCProjectComponent @@ -46,49 +45,40 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + \ No newline at end of file diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCEditorFactoryListener.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCEditorFactoryListener.java deleted file mode 100644 index 0ecea377388c..000000000000 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCEditorFactoryListener.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.jetbrains.edu.coursecreator; - -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.actionSystem.EditorActionManager; -import com.intellij.openapi.editor.colors.EditorColors; -import com.intellij.openapi.editor.event.EditorFactoryEvent; -import com.intellij.openapi.editor.event.EditorFactoryListener; -import com.intellij.openapi.fileEditor.FileDocumentManager; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; -import com.jetbrains.edu.learning.core.EduAnswerPlaceholderDeleteHandler; -import com.jetbrains.edu.learning.core.EduAnswerPlaceholderPainter; -import com.jetbrains.edu.learning.core.EduDocumentListener; -import com.jetbrains.edu.learning.core.EduNames; -import com.jetbrains.edu.learning.courseFormat.Course; -import com.jetbrains.edu.learning.courseFormat.TaskFile; -import org.jetbrains.annotations.NotNull; - -public class CCEditorFactoryListener implements EditorFactoryListener { - @Override - public void editorCreated(@NotNull EditorFactoryEvent event) { - Editor editor = event.getEditor(); - Project project = editor.getProject(); - if (project == null) { - return; - } - VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(editor.getDocument()); - if (virtualFile == null) { - return; - } - final CCProjectService service = CCProjectService.getInstance(project); - Course course = service.getCourse(); - if (course == null) { - return; - } - final VirtualFile taskDir = virtualFile.getParent(); - if (taskDir == null || !taskDir.getName().contains(EduNames.TASK)) { - return; - } - final VirtualFile lessonDir = taskDir.getParent(); - if (lessonDir == null) return; - final TaskFile taskFile = service.getTaskFile(virtualFile); - if (taskFile == null) { - return; - } - EduDocumentListener listener = new EduDocumentListener(taskFile, true, true); - CCProjectService.addDocumentListener(editor.getDocument(), listener); - editor.getDocument().addDocumentListener(listener); - EditorActionManager.getInstance() - .setReadonlyFragmentModificationHandler(editor.getDocument(), new EduAnswerPlaceholderDeleteHandler(editor)); - service.drawAnswerPlaceholders(virtualFile, editor); - editor.getColorsScheme().setColor(EditorColors.READONLY_FRAGMENT_BACKGROUND_COLOR, null); - EduAnswerPlaceholderPainter.createGuardedBlocks(editor, taskFile, false); - } - - @Override - public void editorReleased(@NotNull EditorFactoryEvent event) { - Editor editor = event.getEditor(); - Document document = editor.getDocument(); - EduDocumentListener listener = CCProjectService.getListener(document); - if (listener != null) { - document.removeDocumentListener(listener); - CCProjectService.removeListener(document); - } - editor.getMarkupModel().removeAllHighlighters(); - editor.getSelectionModel().removeSelection(); - } -} diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCLanguageManager.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCLanguageManager.java index 5ab5f5591ed8..b6c8fd4ecf53 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCLanguageManager.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCLanguageManager.java @@ -3,6 +3,7 @@ package com.jetbrains.edu.coursecreator; import com.intellij.ide.fileTemplates.FileTemplate; import com.intellij.lang.LanguageExtension; import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -21,4 +22,8 @@ public interface CCLanguageManager { FileTemplate getTestsTemplate(@NotNull final Project project); boolean doNotPackFile(File pathname); + + default boolean isTestFile(VirtualFile file) { + return false; + } } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCProjectComponent.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCProjectComponent.java index e5871848ecf1..b5a23ba917ed 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCProjectComponent.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCProjectComponent.java @@ -1,33 +1,40 @@ package com.jetbrains.edu.coursecreator; -import com.intellij.openapi.components.ProjectComponent; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.EditorFactory; -import com.intellij.openapi.editor.event.EditorFactoryEvent; -import com.intellij.openapi.editor.impl.EditorFactoryImpl; -import com.intellij.openapi.fileEditor.FileEditor; -import com.intellij.openapi.fileEditor.FileEditorManager; -import com.intellij.openapi.fileEditor.impl.text.PsiAwareTextEditorImpl; +import com.intellij.openapi.application.PathManager; +import com.intellij.openapi.components.AbstractProjectComponent; import com.intellij.openapi.project.Project; -import com.intellij.openapi.project.ProjectManager; -import com.intellij.openapi.startup.StartupManager; -import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; +import com.jetbrains.edu.learning.StudyProjectComponent; +import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.courseFormat.Course; import org.jetbrains.annotations.NotNull; -public class CCProjectComponent implements ProjectComponent { - private final Project myProject; - private CCFileDeletedListener myListener; +import java.io.File; - public CCProjectComponent(Project project) { +public class CCProjectComponent extends AbstractProjectComponent { + private final CCVirtualFileListener myTaskFileLifeListener = new CCVirtualFileListener(); + private final Project myProject; + + protected CCProjectComponent(Project project) { + super(project); myProject = project; } public void initComponent() { + VirtualFileManager.getInstance().addVirtualFileListener(myTaskFileLifeListener); } - public void disposeComponent() { + public void migrateIfNeeded() { + Course studyCourse = StudyTaskManager.getInstance(myProject).getCourse(); + Course course = CCProjectService.getInstance(myProject).getCourse(); + if (studyCourse == null && course != null) { + course.setCourseMode(CCUtils.COURSE_MODE); + File coursesDir = new File(PathManager.getConfigPath(), "courses"); + File courseDir = new File(coursesDir, course.getName() + "-" + myProject.getName()); + course.setCourseDirectory(courseDir.getPath()); + StudyTaskManager.getInstance(myProject).setCourse(course); + StudyProjectComponent.getInstance(myProject).registerStudyToolWindow(course); + } } @NotNull @@ -36,36 +43,11 @@ public class CCProjectComponent implements ProjectComponent { } public void projectOpened() { - StartupManager.getInstance(myProject).runWhenProjectIsInitialized(new Runnable() { - @Override - public void run() { - final Course course = CCProjectService.getInstance(myProject).getCourse(); - if (course != null) { - course.initCourse(true); - myListener = new CCFileDeletedListener(myProject); - VirtualFileManager.getInstance().addVirtualFileListener(myListener); - final CCEditorFactoryListener editorFactoryListener = new CCEditorFactoryListener(); - EditorFactory.getInstance().addEditorFactoryListener(editorFactoryListener, myProject); - VirtualFile[] files = FileEditorManager.getInstance(myProject).getOpenFiles(); - for (VirtualFile file : files) { - if (CCProjectService.getInstance(myProject).isTaskFile(file)) { - FileEditorManager.getInstance(myProject).closeFile(file); - continue; - } - FileEditor fileEditor = FileEditorManager.getInstance(myProject).getSelectedEditor(file); - if (fileEditor instanceof PsiAwareTextEditorImpl) { - Editor editor = ((PsiAwareTextEditorImpl)fileEditor).getEditor(); - editorFactoryListener.editorCreated(new EditorFactoryEvent(new EditorFactoryImpl(ProjectManager.getInstance()), editor)); - } - } - } - } - }); + migrateIfNeeded(); + VirtualFileManager.getInstance().addVirtualFileListener(myTaskFileLifeListener); } public void projectClosed() { - if (myListener != null) { - VirtualFileManager.getInstance().removeVirtualFileListener(myListener); - } + VirtualFileManager.getInstance().removeVirtualFileListener(myTaskFileLifeListener); } } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCProjectService.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCProjectService.java index ba9886692671..2ff6f8051033 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCProjectService.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCProjectService.java @@ -15,140 +15,24 @@ */ package com.jetbrains.edu.coursecreator; -import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.components.PersistentStateComponent; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.components.State; import com.intellij.openapi.components.Storage; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.ui.JBColor; import com.intellij.util.xmlb.XmlSerializerUtil; -import com.jetbrains.edu.learning.core.EduAnswerPlaceholderPainter; -import com.jetbrains.edu.learning.core.EduDocumentListener; -import com.jetbrains.edu.learning.core.EduNames; -import com.jetbrains.edu.learning.core.EduUtils; -import com.jetbrains.edu.learning.courseFormat.*; +import com.jetbrains.edu.learning.courseFormat.Course; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; @State(name = "CCProjectService", storages = @Storage("course_service.xml")) public class CCProjectService implements PersistentStateComponent { private Course myCourse; - private static final Map myDocumentListeners = new HashMap(); - - @Nullable - public TaskFile getTaskFile(@NotNull final VirtualFile virtualFile) { - VirtualFile taskDir = virtualFile.getParent(); - if (taskDir == null) { - return null; - } - String taskDirName = taskDir.getName(); - if (!taskDirName.contains(EduNames.TASK)) { - return null; - } - VirtualFile lessonDir = taskDir.getParent(); - if (lessonDir == null) { - return null; - } - String lessonDirName = lessonDir.getName(); - if (!lessonDirName.contains(EduNames.LESSON)) { - return null; - } - Lesson lesson = myCourse.getLesson(lessonDirName); - if (lesson == null) { - return null; - } - Task task = lesson.getTask(taskDir.getName()); - if (task == null) { - return null; - } - return task.getTaskFile(virtualFile.getName()); - } - - public void drawAnswerPlaceholders(@NotNull final VirtualFile virtualFile, @NotNull final Editor editor) { - TaskFile taskFile = getTaskFile(virtualFile); - if (taskFile == null) { - return; - } - List answerPlaceholders = taskFile.getAnswerPlaceholders(); - for (AnswerPlaceholder answerPlaceholder : answerPlaceholders) { - EduAnswerPlaceholderPainter.drawAnswerPlaceholder(editor, answerPlaceholder, false, JBColor.BLUE); - } - } - - public static void addDocumentListener(Document document, EduDocumentListener listener) { - myDocumentListeners.put(document, listener); - } - - public static EduDocumentListener getListener(Document document) { - return myDocumentListeners.get(document); - } - - public static void removeListener(Document document) { - myDocumentListeners.remove(document); - } - - @Nullable - public Task getTask(VirtualFile file) { - if (myCourse == null || file == null) { - return null; - } - VirtualFile taskDir = file.getParent(); - if (taskDir != null) { - String taskDirName = taskDir.getName(); - if (taskDirName.contains(EduNames.TASK)) { - VirtualFile lessonDir = taskDir.getParent(); - if (lessonDir != null) { - String lessonDirName = lessonDir.getName(); - int lessonIndex = EduUtils.getIndex(lessonDirName, EduNames.LESSON); - List lessons = myCourse.getLessons(); - if (!EduUtils.indexIsValid(lessonIndex, lessons)) { - return null; - } - Lesson lesson = lessons.get(lessonIndex); - int taskIndex = EduUtils.getIndex(taskDirName, EduNames.TASK); - List tasks = lesson.getTaskList(); - if (!EduUtils.indexIsValid(taskIndex, tasks)) { - return null; - } - return tasks.get(taskIndex); - } - } - } - return null; - } - - public boolean isTaskFile(VirtualFile file) { - Task task = getTask(file); - return task != null && task.isTaskFile(file.getName()); - } - - public static boolean setCCActionAvailable(@NotNull AnActionEvent e) { - final Project project = e.getProject(); - if (project == null) { - return false; - } - if (getInstance(project).getCourse() == null) { - EduUtils.enableAction(e, false); - return false; - } - EduUtils.enableAction(e, true); - return true; - } - public Course getCourse() { return myCourse; } - public void setCourse(@NotNull final Course course) { + public void setCourse(Course course) { myCourse = course; } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCRefactoringElementListenerProvider.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCRefactoringElementListenerProvider.java index ed9b404e04a5..7dbd894d8980 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCRefactoringElementListenerProvider.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCRefactoringElementListenerProvider.java @@ -16,24 +16,30 @@ package com.jetbrains.edu.coursecreator; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.refactoring.listeners.RefactoringElementAdapter; import com.intellij.refactoring.listeners.RefactoringElementListener; import com.intellij.refactoring.listeners.RefactoringElementListenerProvider; +import com.jetbrains.edu.learning.StudyTaskManager; +import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.Lesson; import com.jetbrains.edu.learning.courseFormat.Task; import com.jetbrains.edu.learning.courseFormat.TaskFile; -import com.jetbrains.edu.coursecreator.actions.CCRunTestsAction; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.IOException; import java.util.Map; public class CCRefactoringElementListenerProvider implements RefactoringElementListenerProvider { + private static final Logger LOG = Logger.getInstance(CCRefactoringElementListenerProvider.class); + @Nullable @Override public RefactoringElementListener getListener(PsiElement element) { @@ -62,8 +68,7 @@ public class CCRefactoringElementListenerProvider implements RefactoringElementL private static void tryToRenameTaskFile(PsiFile file, String oldName) { final PsiDirectory taskDir = file.getContainingDirectory(); - final CCProjectService service = CCProjectService.getInstance(file.getProject()); - Course course = service.getCourse(); + Course course = StudyTaskManager.getInstance(file.getProject()).getCourse(); if (course == null) { return; } @@ -82,16 +87,29 @@ public class CCRefactoringElementListenerProvider implements RefactoringElementL if (task == null) { return; } + Map taskFiles = task.getTaskFiles(); + TaskFile taskFile = task.getTaskFile(oldName); + if (taskFile == null) { + return; + } ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { - CCRunTestsAction.clearTestEnvironment(taskDir.getVirtualFile(), taskDir.getProject()); + VirtualFile patternFile = StudyUtils.getPatternFile(taskFile, oldName); + if (patternFile != null) { + try { + patternFile.delete(CCRefactoringElementListenerProvider.class); + } + catch (IOException e) { + LOG.info(e); + } + } } }); - Map taskFiles = task.getTaskFiles(); - TaskFile taskFile = task.getTaskFile(oldName); + taskFiles.remove(oldName); taskFiles.put(file.getName(), taskFile); + CCUtils.createResourceFile(file.getVirtualFile(), course, taskDir.getVirtualFile()); } @Override diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCStudyActionListener.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCStudyActionListener.java new file mode 100644 index 000000000000..945026bd1ff9 --- /dev/null +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCStudyActionListener.java @@ -0,0 +1,36 @@ +package com.jetbrains.edu.coursecreator; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.jetbrains.edu.learning.StudyActionListener; +import com.jetbrains.edu.learning.StudyUtils; +import com.jetbrains.edu.learning.courseFormat.Task; +import com.jetbrains.edu.learning.courseFormat.TaskFile; + +public class CCStudyActionListener implements StudyActionListener { + @Override + public void beforeCheck(AnActionEvent event) { + Project project = event.getProject(); + if (project == null) { + return; + } + VirtualFile virtualFile = CommonDataKeys.VIRTUAL_FILE.getData(event.getDataContext()); + if (virtualFile == null) { + return; + } + + TaskFile taskFile = StudyUtils.getTaskFile(project, virtualFile); + if (taskFile == null) { + return; + } + + Task task = taskFile.getTask(); + VirtualFile taskDir = StudyUtils.getTaskDir(virtualFile); + if (taskDir == null) { + return; + } + CCUtils.createResources(project, task, taskDir); + } +} diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCStudyActionsProvider.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCStudyActionsProvider.java new file mode 100644 index 000000000000..7ccdc075ebe3 --- /dev/null +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCStudyActionsProvider.java @@ -0,0 +1,12 @@ +package com.jetbrains.edu.coursecreator; + +import com.intellij.openapi.actionSystem.AnAction; +import com.jetbrains.edu.coursecreator.actions.CCEditTaskTextAction; +import com.jetbrains.edu.learning.StudyActionsProvider; + +public class CCStudyActionsProvider implements StudyActionsProvider{ + @Override + public AnAction[] getActions() { + return new AnAction[]{new CCEditTaskTextAction()}; + } +} diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCUtils.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCUtils.java index 65131d0b36ae..f6dd7417a32d 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCUtils.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCUtils.java @@ -6,6 +6,8 @@ import com.intellij.ide.projectView.actions.MarkRootActionBase; import com.intellij.lang.Language; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.DumbModePermission; import com.intellij.openapi.project.DumbService; @@ -14,27 +16,27 @@ import com.intellij.openapi.roots.ContentEntry; import com.intellij.openapi.roots.ModifiableRootModel; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileEvent; import com.intellij.psi.PsiDirectory; +import com.intellij.util.DocumentUtil; import com.intellij.util.Function; +import com.jetbrains.edu.learning.StudyTaskManager; +import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.core.EduUtils; -import com.jetbrains.edu.learning.courseFormat.Course; -import com.jetbrains.edu.learning.courseFormat.StudyItem; +import com.jetbrains.edu.learning.courseFormat.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.File; import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; +import java.util.*; public class CCUtils { private static final Logger LOG = Logger.getInstance(CCUtils.class); public static final String GENERATED_FILES_FOLDER = ".coursecreator"; - public static final String TESTS = "coursecreatortests"; - public static final String RESOURCES = "coursecreatorresources"; + public static final String COURSE_MODE = "Course Creator"; @Nullable public static CCLanguageManager getStudyLanguageManager(@NotNull final Course course) { @@ -100,9 +102,9 @@ public class CCUtils { if (sourceDirectory == null) { return false; } - CCProjectService service = CCProjectService.getInstance(sourceDirectory.getProject()); - Course course = service.getCourse(); - if (course != null && course.getLesson(sourceDirectory.getName()) != null) { + Project project = sourceDirectory.getProject(); + Course course = StudyTaskManager.getInstance(project).getCourse(); + if (course != null && isCourseCreator(project) && course.getLesson(sourceDirectory.getName()) != null) { return true; } return false; @@ -169,4 +171,74 @@ public class CCUtils { }); return folder.get(); } + + public static boolean isCourseCreator(@NotNull Project project) { + Course course = StudyTaskManager.getInstance(project).getCourse(); + if (course == null) { + return false; + } + + return COURSE_MODE.equals(course.getCourseMode()); + } + + public static boolean isTestsFile(@NotNull Project project, @NotNull VirtualFile file) { + Course course = StudyTaskManager.getInstance(project).getCourse(); + if (course == null) { + return false; + } + CCLanguageManager manager = getStudyLanguageManager(course); + if (manager == null) { + return false; + } + return manager.isTestFile(file); + } + + public static void createResourceFile(VirtualFile createdFile, Course course, VirtualFile taskVF) { + VirtualFile lessonVF = taskVF.getParent(); + if (lessonVF == null) { + return; + } + + String taskResourcesPath = FileUtil.join(course.getCourseDirectory(), lessonVF.getName(), taskVF.getName()); + File taskResourceFile = new File(taskResourcesPath); + if (!taskResourceFile.exists()) { + if (!taskResourceFile.mkdirs()) { + LOG.info("Failed to create resources for task " + taskResourcesPath); + } + } + try { + File toFile = new File(taskResourceFile, createdFile.getName()); + FileUtil.copy(new File(createdFile.getPath()), toFile); + } + catch (IOException e) { + LOG.info("Failed to copy created task file to resources " + createdFile.getPath()); + } + } + + + public static void createResources(Project project, Task task, VirtualFile taskDir) { + Map files = task.getTaskFiles(); + for (Map.Entry entry : files.entrySet()) { + String name = entry.getKey(); + VirtualFile child = taskDir.findChild(name); + if (child == null) { + continue; + } + Document patternDocument = StudyUtils.getPatternDocument(entry.getValue(), name); + Document document = FileDocumentManager.getInstance().getDocument(child); + if (document == null || patternDocument == null) { + return; + } + DocumentUtil.writeInRunUndoTransparentAction(() -> { + patternDocument.replaceString(0, patternDocument.getTextLength(), document.getCharsSequence()); + FileDocumentManager.getInstance().saveDocument(patternDocument); + }); + TaskFile target = new TaskFile(); + TaskFile.copy(entry.getValue(), target); + for (AnswerPlaceholder placeholder : target.getAnswerPlaceholders()) { + placeholder.setUseLength(false); + } + EduUtils.createStudentDocument(project, target, child, patternDocument); + } + } } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCFileDeletedListener.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCVirtualFileListener.java similarity index 54% rename from python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCFileDeletedListener.java rename to python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCVirtualFileListener.java index 1d840f0c5943..3c3be598ca7f 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCFileDeletedListener.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCVirtualFileListener.java @@ -1,54 +1,95 @@ package com.jetbrains.edu.coursecreator; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ProjectUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileAdapter; import com.intellij.openapi.vfs.VirtualFileEvent; import com.intellij.util.Function; +import com.jetbrains.edu.learning.StudyTaskManager; +import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.*; import org.jetbrains.annotations.NotNull; -class CCFileDeletedListener extends VirtualFileAdapter { +public class CCVirtualFileListener extends VirtualFileAdapter { - private final Project myProject; + private static final Logger LOG = Logger.getInstance(CCVirtualFileListener.class); - CCFileDeletedListener(@NotNull final Project project) { - myProject = project; + @Override + public void fileCreated(@NotNull VirtualFileEvent event) { + VirtualFile createdFile = event.getFile(); + Project project = ProjectUtil.guessProjectForFile(createdFile); + if (project == null) { + return; + } + Course course = StudyTaskManager.getInstance(project).getCourse(); + if (course == null || !CCUtils.isCourseCreator(project)) { + return; + } + TaskFile taskFile = StudyUtils.getTaskFile(project, createdFile); + if (taskFile != null) { + return; + } + + String name = createdFile.getName(); + if (CCUtils.isTestsFile(project, createdFile) + || EduNames.TASK_HTML.equals(name) + || name.contains(EduNames.WINDOW_POSTFIX) + || name.contains(EduNames.WINDOWS_POSTFIX) + || name.contains(EduNames.ANSWERS_POSTFIX)) { + return; + } + + VirtualFile taskVF = createdFile.getParent(); + if (taskVF == null) { + return; + } + Task task = StudyUtils.getTask(project, taskVF); + if (task == null) { + return; + } + + CCUtils.createResourceFile(createdFile, course, taskVF); + + task.addTaskFile(name, 1); } @Override public void fileDeleted(@NotNull VirtualFileEvent event) { - if (myProject.isDisposed() || !myProject.isOpen()) { - return; - } VirtualFile removedFile = event.getFile(); if (removedFile.getPath().contains(CCUtils.GENERATED_FILES_FOLDER)) { return; } - final TaskFile taskFile = CCProjectService.getInstance(myProject).getTaskFile(removedFile); + + Project project = ProjectUtil.guessProjectForFile(removedFile); + if (project == null) { + return; + } + Course course = StudyTaskManager.getInstance(project).getCourse(); + if (course == null) { + return; + } + final TaskFile taskFile = StudyUtils.getTaskFile(project, removedFile); if (taskFile != null) { deleteTaskFile(removedFile, taskFile); return; } - Course course = CCProjectService.getInstance(myProject).getCourse(); - if (course == null) { - return; - } if (removedFile.getName().contains(EduNames.TASK)) { - deleteTask(course, removedFile, myProject); + deleteTask(course, removedFile); } if (removedFile.getName().contains(EduNames.LESSON)) { - deleteLesson(course, removedFile); + deleteLesson(course, removedFile, project); } } - private void deleteLesson(@NotNull final Course course, @NotNull final VirtualFile removedLessonFile) { + private static void deleteLesson(@NotNull final Course course, @NotNull final VirtualFile removedLessonFile, Project project) { Lesson removedLesson = course.getLesson(removedLessonFile.getName()); if (removedLesson == null) { return; } - VirtualFile courseDir = myProject.getBaseDir(); + VirtualFile courseDir = project.getBaseDir(); CCUtils.updateHigherElements(courseDir.getChildren(), new Function() { @Override public StudyItem fun(VirtualFile file) { @@ -58,7 +99,7 @@ class CCFileDeletedListener extends VirtualFileAdapter { course.getLessons().remove(removedLesson); } - private static void deleteTask(@NotNull final Course course, @NotNull final VirtualFile removedTask, @NotNull final Project project) { + private static void deleteTask(@NotNull final Course course, @NotNull final VirtualFile removedTask) { VirtualFile lessonDir = removedTask.getParent(); if (lessonDir == null || !lessonDir.getName().contains(EduNames.LESSON)) { return; diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCAddAnswerPlaceholder.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCAddAnswerPlaceholder.java index 791d7fb0efae..5eae370909ee 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCAddAnswerPlaceholder.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCAddAnswerPlaceholder.java @@ -1,32 +1,32 @@ package com.jetbrains.edu.coursecreator.actions; import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.Presentation; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.SelectionModel; -import com.intellij.openapi.project.DumbAwareAction; +import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import com.intellij.ui.JBColor; -import com.jetbrains.edu.learning.core.EduAnswerPlaceholderPainter; -import com.jetbrains.edu.coursecreator.CCProjectService; +import com.intellij.util.DocumentUtil; import com.jetbrains.edu.coursecreator.ui.CCCreateAnswerPlaceholderDialog; -import com.jetbrains.edu.learning.courseFormat.*; +import com.jetbrains.edu.learning.StudyUtils; +import com.jetbrains.edu.learning.core.EduAnswerPlaceholderPainter; +import com.jetbrains.edu.learning.core.EduNames; +import com.jetbrains.edu.learning.core.EduUtils; +import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder; +import com.jetbrains.edu.learning.courseFormat.TaskFile; import org.jetbrains.annotations.NotNull; import java.util.List; -public class CCAddAnswerPlaceholder extends DumbAwareAction { - private static final Logger LOG = Logger.getInstance(CCAddAnswerPlaceholder.class); +public class CCAddAnswerPlaceholder extends CCAnswerPlaceholderAction { public CCAddAnswerPlaceholder() { - super("Add Answer Placeholder", "Add answer placeholder", null); + super("Add/Delete Answer Placeholder", "Add/Delete answer placeholder", null); } @@ -43,114 +43,131 @@ public class CCAddAnswerPlaceholder extends DumbAwareAction { return false; } - @Override - public void actionPerformed(@NotNull AnActionEvent e) { - final Project project = e.getData(CommonDataKeys.PROJECT); - if (project == null) { + private static void addPlaceholder(@NotNull CCState state) { + Editor editor = state.getEditor(); + Project project = state.getProject(); + PsiFile file = state.getFile(); + + final Document document = PsiDocumentManager.getInstance(project).getDocument(file); + if (document == null) { return; } - final PsiFile file = CommonDataKeys.PSI_FILE.getData(e.getDataContext()); - if (file == null) return; - final Editor editor = CommonDataKeys.EDITOR.getData(e.getDataContext()); - if (editor == null) return; + final SelectionModel model = editor.getSelectionModel(); - final Document document = PsiDocumentManager.getInstance(project).getDocument(file); - if (document == null) return; final int start = model.getSelectionStart(); - final int end = model.getSelectionEnd(); final int lineNumber = document.getLineNumber(start); int realStart = start - document.getLineStartOffset(lineNumber); - - final CCProjectService service = CCProjectService.getInstance(project); - final PsiDirectory taskDir = file.getContainingDirectory(); - final PsiDirectory lessonDir = taskDir.getParent(); - if (lessonDir == null) return; - - final TaskFile taskFile = service.getTaskFile(file.getVirtualFile()); - if (taskFile == null) { - return; - } - if (arePlaceholdersIntersect(taskFile, document, start, end)) { - return; - } final AnswerPlaceholder answerPlaceholder = new AnswerPlaceholder(); answerPlaceholder.setLine(lineNumber); answerPlaceholder.setStart(realStart); - answerPlaceholder.setPossibleAnswer(model.getSelectedText()); + answerPlaceholder.setUseLength(false); + String selectedText = model.getSelectedText(); + answerPlaceholder.setPossibleAnswer(selectedText); - CCCreateAnswerPlaceholderDialog dlg = new CCCreateAnswerPlaceholderDialog(project, answerPlaceholder - ); + CCCreateAnswerPlaceholderDialog dlg = new CCCreateAnswerPlaceholderDialog(project, answerPlaceholder); dlg.show(); if (dlg.getExitCode() != DialogWrapper.OK_EXIT_CODE) { return; } + + TaskFile taskFile = state.getTaskFile(); int index = taskFile.getAnswerPlaceholders().size() + 1; answerPlaceholder.setIndex(index); taskFile.addAnswerPlaceholder(answerPlaceholder); + answerPlaceholder.setTaskFile(taskFile); taskFile.sortAnswerPlaceholders(); - EduAnswerPlaceholderPainter.drawAnswerPlaceholder(editor, answerPlaceholder, false, JBColor.BLUE); - EduAnswerPlaceholderPainter.createGuardedBlocks(editor, answerPlaceholder, false); + + + computeInitialState(project, file, taskFile, document); + + EduAnswerPlaceholderPainter.drawAnswerPlaceholder(editor, answerPlaceholder, JBColor.BLUE); + EduAnswerPlaceholderPainter.createGuardedBlocks(editor, answerPlaceholder); + } + + private static void computeInitialState(Project project, PsiFile file, TaskFile taskFile, Document document) { + Document patternDocument = StudyUtils.getPatternDocument(taskFile, file.getName()); + if (patternDocument == null) { + return; + } + DocumentUtil.writeInRunUndoTransparentAction(() -> { + patternDocument.replaceString(0, patternDocument.getTextLength(), document.getCharsSequence()); + FileDocumentManager.getInstance().saveDocument(patternDocument); + }); + TaskFile target = new TaskFile(); + TaskFile.copy(taskFile, target); + List placeholders = target.getAnswerPlaceholders(); + for (AnswerPlaceholder placeholder : placeholders) { + placeholder.setUseLength(false); + } + EduUtils.createStudentDocument(project, target, file.getVirtualFile(), patternDocument); + + for (int i = 0; i < placeholders.size(); i++) { + AnswerPlaceholder fromPlaceholder = placeholders.get(i); + taskFile.getAnswerPlaceholders().get(i).setInitialState(fromPlaceholder); + } + } + + @Override + protected void performAnswerPlaceholderAction(@NotNull CCState state) { + if (canAddPlaceholder(state)) { + addPlaceholder(state); + return; + } + if (canDeletePlaceholder(state)) { + deletePlaceholder(state); + } + } + + private static void deletePlaceholder(@NotNull CCState state) { + Project project = state.getProject(); + PsiFile psiFile = state.getFile(); + final Document document = PsiDocumentManager.getInstance(project).getDocument(psiFile); + if (document == null) return; + TaskFile taskFile = state.getTaskFile(); + AnswerPlaceholder answerPlaceholder = state.getAnswerPlaceholder(); + final List answerPlaceholders = taskFile.getAnswerPlaceholders(); + if (answerPlaceholders.contains(answerPlaceholder)) { + answerPlaceholders.remove(answerPlaceholder); + final Editor editor = state.getEditor(); + editor.getMarkupModel().removeAllHighlighters(); + StudyUtils.drawAllWindows(editor, taskFile); + EduAnswerPlaceholderPainter.createGuardedBlocks(editor, taskFile); + } } @Override public void update(@NotNull AnActionEvent event) { - if (!CCProjectService.setCCActionAvailable(event)) { - return; - } final Presentation presentation = event.getPresentation(); - final Project project = event.getData(CommonDataKeys.PROJECT); - if (project == null) { - presentation.setVisible(false); - presentation.setEnabled(false); + presentation.setEnabledAndVisible(false); + + CCState state = getState(event); + if (state == null) { return; } - final Editor editor = CommonDataKeys.EDITOR.getData(event.getDataContext()); - final PsiFile file = CommonDataKeys.PSI_FILE.getData(event.getDataContext()); - if (editor == null || file == null) { - presentation.setVisible(false); - presentation.setEnabled(false); - return; + + presentation.setVisible(true); + if (canAddPlaceholder(state) || canDeletePlaceholder(state)) { + presentation.setEnabled(true); + presentation.setText((state.getAnswerPlaceholder() == null ? "Add " : "Delete ") + EduNames.PLACEHOLDER); } + } + + + private static boolean canAddPlaceholder(@NotNull CCState state) { + Editor editor = state.getEditor(); SelectionModel selectionModel = editor.getSelectionModel(); if (!selectionModel.hasSelection()) { - presentation.setVisible(false); - presentation.setEnabled(false); - return; + return false; } int start = selectionModel.getSelectionStart(); int end = selectionModel.getSelectionEnd(); + return !arePlaceholdersIntersect(state.getTaskFile(), editor.getDocument(), start, end); + } - final CCProjectService service = CCProjectService.getInstance(project); - final PsiDirectory taskDir = file.getContainingDirectory(); - final PsiDirectory lessonDir = taskDir.getParent(); - if (lessonDir == null) return; - - final Course course = service.getCourse(); - final Lesson lesson = course.getLesson(lessonDir.getName()); - if (lesson == null) { - presentation.setVisible(false); - presentation.setEnabled(false); - return; + private static boolean canDeletePlaceholder(@NotNull CCState state) { + if (state.getEditor().getSelectionModel().hasSelection()) { + return false; } - final Task task = lesson.getTask(taskDir.getName()); - if (task == null) { - presentation.setVisible(false); - presentation.setEnabled(false); - return; - } - TaskFile taskFile = service.getTaskFile(file.getVirtualFile()); - if (taskFile == null) { - LOG.info("could not find task file"); - presentation.setVisible(false); - presentation.setEnabled(false); - return; - } - if (arePlaceholdersIntersect(taskFile, editor.getDocument(), start, end)) { - presentation.setVisible(false); - presentation.setEnabled(false); - return; - } - presentation.setVisible(true); - presentation.setEnabled(true); + return state.getAnswerPlaceholder() != null; } } \ No newline at end of file diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCAddAsTaskFile.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCAddAsTaskFile.java index 867cc77440b6..c62bd5e1e069 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCAddAsTaskFile.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCAddAsTaskFile.java @@ -1,46 +1,20 @@ package com.jetbrains.edu.coursecreator.actions; -import com.intellij.ide.projectView.ProjectView; -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.CommonDataKeys; -import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; +import com.jetbrains.edu.coursecreator.CCUtils; +import com.jetbrains.edu.learning.StudyUtils; +import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.Task; -import com.jetbrains.edu.coursecreator.CCProjectService; -public class CCAddAsTaskFile extends AnAction { - @Override - public void actionPerformed(final AnActionEvent e) { - Project project = e.getProject(); - if (project == null) { - return; - } - final VirtualFile file = CommonDataKeys.VIRTUAL_FILE.getData(e.getDataContext()); - if (file == null) { - return; - } - Task task = CCProjectService.getInstance(project).getTask(file); - if (task == null) { - return; - } +public class CCAddAsTaskFile extends CCTaskFileActionBase { + + protected void performAction(VirtualFile file, Task task, Course course, Project project) { task.addTaskFile(file.getName(), task.getTaskFiles().size()); - ProjectView.getInstance(project).refresh(); + CCUtils.createResourceFile(file, course, StudyUtils.getTaskDir(file)); } - - @Override - public void update(AnActionEvent e) { - Project project = e.getProject(); - Presentation presentation = e.getPresentation(); - if (project == null) { - presentation.setEnabledAndVisible(false); - return; - } - VirtualFile file = CommonDataKeys.VIRTUAL_FILE.getData(e.getDataContext()); - if (file == null || file.isDirectory() || CCProjectService.getInstance(project).getTaskFile(file) != null) { - presentation.setEnabledAndVisible(false); - } + protected boolean isAvailable(Project project, VirtualFile file) { + return StudyUtils.getTaskFile(project, file) == null && !CCUtils.isTestsFile(project, file); } } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCAnswerPlaceholderAction.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCAnswerPlaceholderAction.java index 48425e247100..e1633044d5e6 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCAnswerPlaceholderAction.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCAnswerPlaceholderAction.java @@ -2,15 +2,15 @@ package com.jetbrains.edu.coursecreator.actions; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; -import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; +import com.jetbrains.edu.coursecreator.CCUtils; +import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder; import com.jetbrains.edu.learning.courseFormat.TaskFile; -import com.jetbrains.edu.coursecreator.CCProjectService; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -23,9 +23,9 @@ abstract public class CCAnswerPlaceholderAction extends DumbAwareAction { } @Nullable - private static CCState getState(@NotNull AnActionEvent e) { + protected static CCState getState(@NotNull AnActionEvent e) { final Project project = e.getProject(); - if (project == null || CCProjectService.getInstance(project).getCourse() == null) { + if (project == null || !CCUtils.isCourseCreator(project)) { return null; } final PsiFile psiFile = CommonDataKeys.PSI_FILE.getData(e.getDataContext()); @@ -40,27 +40,16 @@ abstract public class CCAnswerPlaceholderAction extends DumbAwareAction { if (editor == null) { return null; } - TaskFile taskFile = CCProjectService.getInstance(project).getTaskFile(virtualFile); + TaskFile taskFile = StudyUtils.getTaskFile(project, virtualFile); if (taskFile == null) { return null; } AnswerPlaceholder answerPlaceholder = taskFile.getAnswerPlaceholder(editor.getDocument(), editor.getCaretModel().getLogicalPosition(), true); - if (answerPlaceholder == null) { - return null; - } return new CCState(taskFile, answerPlaceholder, psiFile, editor, project); } - @Override - public void update(@NotNull AnActionEvent e) { - Presentation presentation = e.getPresentation(); - boolean isAvailable = getState(e) != null; - presentation.setEnabled(isAvailable); - presentation.setVisible(isAvailable); - } - @Override public void actionPerformed(@NotNull AnActionEvent e) { CCState state = getState(e); @@ -80,7 +69,7 @@ abstract public class CCAnswerPlaceholderAction extends DumbAwareAction { private Project myProject; public CCState(@NotNull final TaskFile taskFile, - @NotNull final AnswerPlaceholder answerPlaceholder, + @Nullable final AnswerPlaceholder answerPlaceholder, @NotNull final PsiFile file, @NotNull final Editor editor, @NotNull final Project project) { @@ -96,7 +85,7 @@ abstract public class CCAnswerPlaceholderAction extends DumbAwareAction { return myTaskFile; } - @NotNull + @Nullable public AnswerPlaceholder getAnswerPlaceholder() { return myAnswerPlaceholder; } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCChangeCourseInfo.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCChangeCourseInfo.java index 28706dae9a72..3423c822aae8 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCChangeCourseInfo.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCChangeCourseInfo.java @@ -2,39 +2,39 @@ package com.jetbrains.edu.coursecreator.actions; import com.intellij.ide.IdeView; import com.intellij.ide.projectView.ProjectView; -import com.intellij.ide.util.DirectoryChooserUtil; import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.LangDataKeys; import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.DialogBuilder; import com.intellij.psi.PsiDirectory; -import com.jetbrains.edu.learning.courseFormat.Course; -import com.jetbrains.edu.coursecreator.CCProjectService; +import com.intellij.util.ui.JBUI; +import com.jetbrains.edu.coursecreator.CCUtils; import com.jetbrains.edu.coursecreator.ui.CCNewProjectPanel; +import com.jetbrains.edu.learning.StudyTaskManager; +import com.jetbrains.edu.learning.courseFormat.Course; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import javax.swing.*; -import java.awt.*; public class CCChangeCourseInfo extends DumbAwareAction { + + private static final String ACTION_TEXT = "Change Course Information"; + public CCChangeCourseInfo() { - super("Change Course Information", "Change Course Information", null); + super(ACTION_TEXT, ACTION_TEXT, null); } @Override public void update(@NotNull AnActionEvent event) { - if (!CCProjectService.setCCActionAvailable(event)) { + final Project project = event.getProject(); + final Presentation presentation = event.getPresentation(); + if (project == null) { return; } - final Presentation presentation = event.getPresentation(); - presentation.setVisible(false); - presentation.setEnabled(false); - final Project project = event.getData(CommonDataKeys.PROJECT); - if (project == null) { + presentation.setEnabledAndVisible(false); + if (!CCUtils.isCourseCreator(project)) { return; } final IdeView view = event.getData(LangDataKeys.IDE_VIEW); @@ -45,45 +45,24 @@ public class CCChangeCourseInfo extends DumbAwareAction { if (directories.length == 0) { return; } - final PsiDirectory directory = DirectoryChooserUtil.getOrChooseDirectory(view); - if (directory != null && !project.getBaseDir().equals(directory.getVirtualFile())) { - return; - } - presentation.setVisible(true); - presentation.setEnabled(true); - + presentation.setEnabledAndVisible(true); } @Override public void actionPerformed(@NotNull AnActionEvent e) { - if (!CCProjectService.setCCActionAvailable(e)) { - return; - } - final Project project = e.getProject(); + Project project = e.getProject(); if (project == null) { return; } - Course course = CCProjectService.getInstance(project).getCourse(); + Course course = StudyTaskManager.getInstance(project).getCourse(); if (course == null) { return; } - final IdeView view = e.getData(LangDataKeys.IDE_VIEW); - if (view == null) { - return; - } - final PsiDirectory[] directories = view.getDirectories(); - if (directories.length == 0) { - return; - } - final PsiDirectory directory = DirectoryChooserUtil.getOrChooseDirectory(view); - if (directory != null && !project.getBaseDir().equals(directory.getVirtualFile())) { - return; - } - CCNewProjectPanel panel = new CCNewProjectPanel(course.getName(), Course.getAuthorsString(course.getAuthors()), course.getDescription()); - ChangeCourseInfoDialog changeCourseInfoDialog = - new ChangeCourseInfoDialog(project, panel); - changeCourseInfoDialog.show(); - if (changeCourseInfoDialog.getExitCode() == DialogWrapper.OK_EXIT_CODE) { + + CCNewProjectPanel panel = + new CCNewProjectPanel(course.getName(), Course.getAuthorsString(course.getAuthors()), course.getDescription()); + DialogBuilder builder = createChangeInfoDialog(project, panel); + if (builder.showAndGet()) { course.setAuthors(panel.getAuthors()); course.setName(panel.getName()); course.setDescription(panel.getDescription()); @@ -91,24 +70,14 @@ public class CCChangeCourseInfo extends DumbAwareAction { } } - static class ChangeCourseInfoDialog extends DialogWrapper { + private static DialogBuilder createChangeInfoDialog(Project project, @NotNull CCNewProjectPanel panel) { + DialogBuilder builder = new DialogBuilder(project); - private final JPanel myMainPanel; + builder.setTitle(ACTION_TEXT); + JPanel changeInfoPanel = panel.getMainPanel(); + changeInfoPanel.setMinimumSize(JBUI.size(400, 300)); + builder.setCenterPanel(changeInfoPanel); - public ChangeCourseInfoDialog(@Nullable Project project, CCNewProjectPanel panel) { - super(project); - setTitle("Change Course Information"); - myMainPanel = panel.getMainPanel(); - init(); - } - - @Nullable - @Override - protected JComponent createCenterPanel() { - myMainPanel.setPreferredSize(new Dimension(400, 300)); - myMainPanel.setSize(new Dimension(400, 300)); - myMainPanel.setMaximumSize(new Dimension(400, 300)); - return myMainPanel; - } + return builder; } } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateCourseArchive.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateCourseArchive.java index 486f3b247c5d..bbef6bc15515 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateCourseArchive.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateCourseArchive.java @@ -7,6 +7,7 @@ import com.intellij.ide.projectView.ProjectView; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.LangDataKeys; +import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; @@ -20,6 +21,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.util.containers.HashMap; import com.intellij.util.io.ZipUtil; +import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.core.EduUtils; import com.jetbrains.edu.learning.courseFormat.Course; @@ -27,7 +29,6 @@ import com.jetbrains.edu.learning.courseFormat.Lesson; import com.jetbrains.edu.learning.courseFormat.Task; import com.jetbrains.edu.learning.courseFormat.TaskFile; import com.jetbrains.edu.coursecreator.CCLanguageManager; -import com.jetbrains.edu.coursecreator.CCProjectService; import com.jetbrains.edu.coursecreator.CCUtils; import com.jetbrains.edu.coursecreator.ui.CreateCourseArchiveDialog; import org.jetbrains.annotations.NotNull; @@ -56,7 +57,9 @@ public class CCCreateCourseArchive extends DumbAwareAction { @Override public void update(@NotNull AnActionEvent e) { - CCProjectService.setCCActionAvailable(e); + Presentation presentation = e.getPresentation(); + Project project = e.getProject(); + presentation.setEnabledAndVisible(project != null && CCUtils.isCourseCreator(project)); } @Override @@ -70,8 +73,7 @@ public class CCCreateCourseArchive extends DumbAwareAction { } private void createCourseArchive(final Project project, Module module) { - final CCProjectService service = CCProjectService.getInstance(project); - final Course course = service.getCourse(); + final Course course = StudyTaskManager.getInstance(project).getCourse(); if (course == null) return; CreateCourseArchiveDialog dlg = new CreateCourseArchiveDialog(project, this); dlg.show(); @@ -195,8 +197,7 @@ public class CCCreateCourseArchive extends DumbAwareAction { @SuppressWarnings("IOResourceOpenedButNotSafelyClosed") private static void generateJson(@NotNull final Project project, VirtualFile parentDir) { - final CCProjectService service = CCProjectService.getInstance(project); - final Course course = service.getCourse(); + final Course course = StudyTaskManager.getInstance(project).getCourse(); final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation().create(); final String json = gson.toJson(course); final File courseJson = new File(parentDir.getPath(), EduNames.COURSE_META_FILE); diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateStudyItemActionBase.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateStudyItemActionBase.java index 520ce0961a6a..45310c7c0e8f 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateStudyItemActionBase.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateStudyItemActionBase.java @@ -14,12 +14,13 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; import com.intellij.util.Function; +import com.jetbrains.edu.coursecreator.CCUtils; +import com.jetbrains.edu.coursecreator.ui.CCCreateStudyItemDialog; +import com.jetbrains.edu.learning.StudyTaskManager; +import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.core.EduUtils; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.StudyItem; -import com.jetbrains.edu.coursecreator.CCProjectService; -import com.jetbrains.edu.coursecreator.CCUtils; -import com.jetbrains.edu.coursecreator.ui.CCCreateStudyItemDialog; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -33,18 +34,16 @@ public abstract class CCCreateStudyItemActionBase extends DumbAwareAction { super(text, description, icon); } - @Override public void actionPerformed(AnActionEvent e) { final IdeView view = e.getData(LangDataKeys.IDE_VIEW); - final Project project = e.getData(CommonDataKeys.PROJECT); + final Project project = e.getProject(); if (view == null || project == null) { return; } final PsiDirectory directory = DirectoryChooserUtil.getOrChooseDirectory(view); if (directory == null) return; - final CCProjectService service = CCProjectService.getInstance(project); - final Course course = service.getCourse(); + final Course course = StudyTaskManager.getInstance(project).getCourse(); if (course == null) { return; } @@ -54,47 +53,45 @@ public abstract class CCCreateStudyItemActionBase extends DumbAwareAction { @Override public void update(@NotNull AnActionEvent event) { - if (!CCProjectService.setCCActionAvailable(event)) { - return; - } final Presentation presentation = event.getPresentation(); + presentation.setEnabledAndVisible(false); final Project project = event.getData(CommonDataKeys.PROJECT); final IdeView view = event.getData(LangDataKeys.IDE_VIEW); if (project == null || view == null) { presentation.setEnabledAndVisible(false); return; } + if (!StudyUtils.isStudyProject(project)) { + return; + } final PsiDirectory[] directories = view.getDirectories(); if (directories.length == 0) { - presentation.setEnabledAndVisible(false); return; } final PsiDirectory sourceDirectory = DirectoryChooserUtil.getOrChooseDirectory(view); - final CCProjectService service = CCProjectService.getInstance(project); - final Course course = service.getCourse(); + final Course course = StudyTaskManager.getInstance(project).getCourse(); if (course == null || sourceDirectory == null) { - presentation.setEnabledAndVisible(false); return; } - if (!isAddedAsLast(sourceDirectory, project, course) && - getThresholdItem(course, sourceDirectory) == null) { - presentation.setEnabledAndVisible(false); + if (!isAddedAsLast(sourceDirectory, project, course) && getThresholdItem(course, sourceDirectory) == null) { + return; } if (CommonDataKeys.PSI_FILE.getData(event.getDataContext()) != null) { - presentation.setEnabledAndVisible(false); + return; } + presentation.setEnabledAndVisible(true); } @Nullable protected abstract PsiDirectory getParentDir(@NotNull final Project project, - @NotNull final Course course, - @NotNull final PsiDirectory directory); + @NotNull final Course course, + @NotNull final PsiDirectory directory); @Nullable public PsiDirectory createItem(@Nullable final IdeView view, @NotNull final Project project, - @NotNull final PsiDirectory sourceDirectory, @NotNull final Course course) { + @NotNull final PsiDirectory sourceDirectory, @NotNull final Course course) { StudyItem parentItem = getParentItem(course, sourceDirectory); final StudyItem item = getItem(sourceDirectory, project, course, view, parentItem); if (item == null) { diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateTask.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateTask.java index 316b06d77c04..844fc9ad3796 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateTask.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateTask.java @@ -9,19 +9,19 @@ import com.intellij.ide.util.EditorHelper; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.Ref; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; import com.intellij.util.Function; +import com.jetbrains.edu.coursecreator.CCLanguageManager; +import com.jetbrains.edu.coursecreator.CCUtils; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.core.EduUtils; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.Lesson; import com.jetbrains.edu.learning.courseFormat.StudyItem; import com.jetbrains.edu.learning.courseFormat.Task; -import com.jetbrains.edu.coursecreator.CCLanguageManager; -import com.jetbrains.edu.coursecreator.CCUtils; import icons.InteractiveLearningIcons; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -38,15 +38,15 @@ public class CCCreateTask extends CCCreateStudyItemActionBase { } - private static void createFromTemplateAndOpen(@NotNull final PsiDirectory taskDirectory, - @Nullable final FileTemplate template, - @Nullable IdeView view) { + private static void createFromTemplate(@NotNull final PsiDirectory taskDirectory, + @Nullable final FileTemplate template, + @Nullable IdeView view, boolean open) { if (template == null) { return; } try { final PsiElement file = FileTemplateUtil.createFromTemplate(template, template.getName(), null, taskDirectory); - if (view != null) { + if (view != null && open) { EditorHelper.openInEditor(file, false); view.selectElement(file); } @@ -91,31 +91,29 @@ public class CCCreateTask extends CCCreateStudyItemActionBase { protected PsiDirectory createItemDir(@NotNull final Project project, @NotNull final StudyItem item, @Nullable final IdeView view, @NotNull final PsiDirectory parentDirectory, @NotNull final Course course) { - final PsiDirectory[] taskDirectory = new PsiDirectory[1]; + + final Ref taskDirectory = new Ref<>(); ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { - taskDirectory[0] = DirectoryUtil.createSubdirectories(EduNames.TASK + item.getIndex(), parentDirectory, "\\/"); - if (taskDirectory[0] != null) { + String taskDirName = EduNames.TASK + item.getIndex(); + taskDirectory.set(DirectoryUtil.createSubdirectories(taskDirName, parentDirectory, "\\/")); + if (taskDirectory.get() != null) { CCLanguageManager manager = CCUtils.getStudyLanguageManager(course); if (manager == null) { return; } - createFromTemplateAndOpen(taskDirectory[0], manager.getTestsTemplate(project), view); - createFromTemplateAndOpen(taskDirectory[0], FileTemplateManager.getInstance(project).getInternalTemplate("task.html"), view); + createFromTemplate(taskDirectory.get(), manager.getTestsTemplate(project), view, false); + createFromTemplate(taskDirectory.get(), FileTemplateManager.getInstance(project).getInternalTemplate(EduNames.TASK_HTML), view, false); String defaultExtension = manager.getDefaultTaskFileExtension(); if (defaultExtension != null) { FileTemplate taskFileTemplate = manager.getTaskFileTemplateForExtension(project, defaultExtension); - createFromTemplateAndOpen(taskDirectory[0], taskFileTemplate, view); - if (taskFileTemplate != null) { - String taskFileName = FileUtil.getNameWithoutExtension(taskFileTemplate.getName()); - ((Task)item).addTaskFile(taskFileName + "." + defaultExtension, 1); - } + createFromTemplate(taskDirectory.get(), taskFileTemplate, view, true); } } } }); - return taskDirectory[0]; + return taskDirectory.get(); } @Override diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateTaskFile.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateTaskFile.java deleted file mode 100644 index eb5598625cce..000000000000 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateTaskFile.java +++ /dev/null @@ -1,143 +0,0 @@ -package com.jetbrains.edu.coursecreator.actions; - -import com.intellij.icons.AllIcons; -import com.intellij.ide.IdeView; -import com.intellij.ide.fileTemplates.FileTemplate; -import com.intellij.ide.fileTemplates.FileTemplateUtil; -import com.intellij.ide.projectView.ProjectView; -import com.intellij.ide.util.DirectoryChooserUtil; -import com.intellij.ide.util.EditorHelper; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.CommonDataKeys; -import com.intellij.openapi.actionSystem.LangDataKeys; -import com.intellij.openapi.actionSystem.Presentation; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.fileEditor.FileEditorManager; -import com.intellij.openapi.fileTypes.FileType; -import com.intellij.openapi.project.DumbAwareAction; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiDirectory; -import com.intellij.psi.PsiElement; -import com.jetbrains.edu.learning.core.EduNames; -import com.jetbrains.edu.learning.courseFormat.Course; -import com.jetbrains.edu.learning.courseFormat.Lesson; -import com.jetbrains.edu.learning.courseFormat.Task; -import com.jetbrains.edu.coursecreator.CCLanguageManager; -import com.jetbrains.edu.coursecreator.CCProjectService; -import com.jetbrains.edu.coursecreator.CCUtils; -import com.jetbrains.edu.coursecreator.ui.CreateTaskFileDialog; -import org.jetbrains.annotations.NotNull; - -import static com.intellij.openapi.ui.DialogWrapper.OK_EXIT_CODE; - -public class CCCreateTaskFile extends DumbAwareAction { - - public CCCreateTaskFile() { - super("Task File", "Create new Task File", AllIcons.FileTypes.Text); - } - - @Override - public void actionPerformed(final AnActionEvent e) { - final IdeView view = e.getData(LangDataKeys.IDE_VIEW); - final Project project = e.getData(CommonDataKeys.PROJECT); - - if (view == null || project == null) { - return; - } - final PsiDirectory taskDir = DirectoryChooserUtil.getOrChooseDirectory(view); - if (taskDir == null) return; - PsiDirectory lessonDir = taskDir.getParent(); - if (lessonDir == null) { - return; - } - final CCProjectService service = CCProjectService.getInstance(project); - final Course course = service.getCourse(); - final Lesson lesson = course.getLesson(lessonDir.getName()); - final Task task = lesson.getTask(taskDir.getName()); - - final int index = task.getTaskFiles().size() + 1; - String generatedName = "file" + index; - CreateTaskFileDialog dialog = new CreateTaskFileDialog(project, generatedName, course); - dialog.show(); - if (dialog.getExitCode() != OK_EXIT_CODE) { - return; - } - final String name = dialog.getFileName(); - if (name == null) return; - FileType type = dialog.getFileType(); - if (type == null) { - return; - } - final CCLanguageManager CCLanguageManager = CCUtils.getStudyLanguageManager(course); - if (CCLanguageManager == null) { - return; - } - final String extension = type.getDefaultExtension(); - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - final FileTemplate taskTemplate = CCLanguageManager.getTaskFileTemplateForExtension(project, extension); - final String taskFileName = name + "." + extension; - try { - if (taskTemplate == null) { - VirtualFile file = taskDir.getVirtualFile().createChildData(this, taskFileName); - ProjectView.getInstance(project).select(file, file, false); - FileEditorManager.getInstance(project).openFile(file, true); - } - else { - final PsiElement taskFile = FileTemplateUtil.createFromTemplate(taskTemplate, taskFileName, null, taskDir); - ApplicationManager.getApplication().invokeLater(new Runnable() { - @Override - public void run() { - EditorHelper.openInEditor(taskFile, false); - view.selectElement(taskFile); - } - }); - } - task.addTaskFile(taskFileName, index); - } - catch (Exception ignored) { - } - } - }); - } - - @Override - public void update(@NotNull AnActionEvent event) { - if (!CCProjectService.setCCActionAvailable(event)) { - return; - } - final Presentation presentation = event.getPresentation(); - final Project project = event.getData(CommonDataKeys.PROJECT); - if (project == null) { - presentation.setVisible(false); - presentation.setEnabled(false); - return; - } - - final IdeView view = event.getData(LangDataKeys.IDE_VIEW); - if (view == null) { - presentation.setVisible(false); - presentation.setEnabled(false); - return; - } - - final PsiDirectory[] directories = view.getDirectories(); - if (directories.length == 0) { - presentation.setVisible(false); - presentation.setEnabled(false); - return; - } - final PsiDirectory directory = DirectoryChooserUtil.getOrChooseDirectory(view); - final CCProjectService service = CCProjectService.getInstance(project); - final Course course = service.getCourse(); - if (course != null && directory != null && !directory.getName().contains(EduNames.TASK)) { - presentation.setVisible(false); - presentation.setEnabled(false); - return; - } - presentation.setVisible(true); - presentation.setEnabled(true); - } -} diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCDeleteAllAnswerPlaceholdersAction.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCDeleteAllAnswerPlaceholdersAction.java index 55915d05bae4..0916e1b678f4 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCDeleteAllAnswerPlaceholdersAction.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCDeleteAllAnswerPlaceholdersAction.java @@ -3,6 +3,7 @@ package com.jetbrains.edu.coursecreator.actions; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.application.Result; import com.intellij.openapi.command.UndoConfirmationPolicy; import com.intellij.openapi.command.WriteCommandAction; @@ -14,10 +15,11 @@ import com.intellij.openapi.fileEditor.TextEditor; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; -import com.jetbrains.edu.learning.core.EduUtils; +import com.jetbrains.edu.coursecreator.CCUtils; +import com.jetbrains.edu.learning.StudyUtils; +import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder; import com.jetbrains.edu.learning.courseFormat.TaskFile; -import com.jetbrains.edu.coursecreator.CCProjectService; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -26,7 +28,7 @@ import java.util.List; public class CCDeleteAllAnswerPlaceholdersAction extends DumbAwareAction { - public static final String ACTION_NAME = "Delete All Answer Placeholders"; + public static final String ACTION_NAME = "Delete All " + EduNames.PLACEHOLDER + "s"; public CCDeleteAllAnswerPlaceholdersAction() { super(ACTION_NAME); @@ -40,7 +42,7 @@ public class CCDeleteAllAnswerPlaceholdersAction extends DumbAwareAction { if (file == null || project == null) { return; } - final TaskFile taskFile = CCProjectService.getInstance(project).getTaskFile(file); + final TaskFile taskFile = StudyUtils.getTaskFile(project, file); if (taskFile == null) { return; } @@ -57,7 +59,7 @@ public class CCDeleteAllAnswerPlaceholdersAction extends DumbAwareAction { editor = ((TextEditor)fileEditor).getEditor(); } List placeholders = new ArrayList(taskFile.getAnswerPlaceholders()); - final ClearPlaceholders action = new ClearPlaceholders(taskFile, placeholders, editor, file, project); + final ClearPlaceholders action = new ClearPlaceholders(taskFile, placeholders, editor); new WriteCommandAction(project, ACTION_NAME) { protected void run(@NotNull final Result result) throws Throwable { action.redo(); @@ -72,60 +74,57 @@ public class CCDeleteAllAnswerPlaceholdersAction extends DumbAwareAction { } private static void updateView(@NotNull final Editor editor, - @NotNull final VirtualFile file, - @NotNull final Project project) { + @NotNull final TaskFile taskFile) { editor.getMarkupModel().removeAllHighlighters(); - CCProjectService.getInstance(project).drawAnswerPlaceholders(file, editor); + StudyUtils.drawAllWindows(editor, taskFile); } @Override public void update(AnActionEvent e) { - if (!CCProjectService.setCCActionAvailable(e)) { + Presentation presentation = e.getPresentation(); + presentation.setEnabledAndVisible(false); + + Project project = e.getProject(); + if (project == null) { + return; + } + if (!CCUtils.isCourseCreator(project)) { return; } DataContext context = e.getDataContext(); VirtualFile file = CommonDataKeys.VIRTUAL_FILE.getData(context); - final Project project = e.getProject(); - if (file == null || project == null) { - EduUtils.enableAction(e, false); + if (file == null ) { return; } - TaskFile taskFile = CCProjectService.getInstance(project).getTaskFile(file); - if (taskFile == null) { - EduUtils.enableAction(e, false); + TaskFile taskFile = StudyUtils.getTaskFile(project, file); + if (taskFile == null || taskFile.getAnswerPlaceholders().isEmpty()) { return; } - if (taskFile.getAnswerPlaceholders().isEmpty()) { - EduUtils.enableAction(e, false); - } + presentation.setEnabledAndVisible(true); } private static class ClearPlaceholders implements UndoableAction { private final List myPlaceholders; private final Editor myEditor; - private final VirtualFile myFile; - private final Project myProject; TaskFile myTaskFile; - public ClearPlaceholders(TaskFile taskFile, List placeholders, Editor editor, VirtualFile file, Project project) { + public ClearPlaceholders(TaskFile taskFile, List placeholders, Editor editor) { myTaskFile = taskFile; myPlaceholders = placeholders; myEditor = editor; - myFile = file; - myProject = project; } @Override public void undo() throws UnexpectedUndoException { myTaskFile.getAnswerPlaceholders().addAll(myPlaceholders); - updateView(myEditor, myFile, myProject); + updateView(myEditor, myTaskFile); } @Override public void redo() throws UnexpectedUndoException { myTaskFile.getAnswerPlaceholders().clear(); - updateView(myEditor, myFile, myProject); + updateView(myEditor, myTaskFile); } @Nullable diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCDeleteAnswerPlaceholder.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCDeleteAnswerPlaceholder.java deleted file mode 100644 index e385589b32bd..000000000000 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCDeleteAnswerPlaceholder.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.jetbrains.edu.coursecreator.actions; - -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.project.Project; -import com.intellij.psi.PsiDocumentManager; -import com.intellij.psi.PsiFile; -import com.jetbrains.edu.learning.core.EduAnswerPlaceholderPainter; -import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder; -import com.jetbrains.edu.learning.courseFormat.TaskFile; -import com.jetbrains.edu.coursecreator.CCProjectService; -import org.jetbrains.annotations.NotNull; - -import java.util.List; - -public class CCDeleteAnswerPlaceholder extends CCAnswerPlaceholderAction { - - public CCDeleteAnswerPlaceholder() { - super("Delete Answer Placeholder","Delete answer placeholder", null); - } - - @Override - protected void performAnswerPlaceholderAction(@NotNull CCState state) { - Project project = state.getProject(); - PsiFile psiFile = state.getFile(); - final Document document = PsiDocumentManager.getInstance(project).getDocument(psiFile); - if (document == null) return; - TaskFile taskFile = state.getTaskFile(); - AnswerPlaceholder answerPlaceholder = state.getAnswerPlaceholder(); - final List answerPlaceholders = taskFile.getAnswerPlaceholders(); - if (answerPlaceholders.contains(answerPlaceholder)) { - answerPlaceholders.remove(answerPlaceholder); - final Editor editor = state.getEditor(); - editor.getMarkupModel().removeAllHighlighters(); - CCProjectService.getInstance(project).drawAnswerPlaceholders(psiFile.getVirtualFile(), editor); - EduAnswerPlaceholderPainter.createGuardedBlocks(editor, taskFile, false); - } - } -} \ No newline at end of file diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCShowAnswerPlaceholderDetails.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCEditAnswerPlaceholder.java similarity index 62% rename from python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCShowAnswerPlaceholderDetails.java rename to python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCEditAnswerPlaceholder.java index 7068c5e1c572..622ddacf1cac 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCShowAnswerPlaceholderDetails.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCEditAnswerPlaceholder.java @@ -1,5 +1,7 @@ package com.jetbrains.edu.coursecreator.actions; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiFile; @@ -7,9 +9,9 @@ import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder; import com.jetbrains.edu.coursecreator.ui.CCCreateAnswerPlaceholderDialog; import org.jetbrains.annotations.NotNull; -public class CCShowAnswerPlaceholderDetails extends CCAnswerPlaceholderAction { +public class CCEditAnswerPlaceholder extends CCAnswerPlaceholderAction { - public CCShowAnswerPlaceholderDetails() { + public CCEditAnswerPlaceholder() { super("Edit Answer Placeholder", "Edit answer placeholder", null); } @@ -21,9 +23,23 @@ public class CCShowAnswerPlaceholderDetails extends CCAnswerPlaceholderAction { final PsiDirectory lessonDir = taskDir.getParent(); if (lessonDir == null) return; AnswerPlaceholder answerPlaceholder = state.getAnswerPlaceholder(); + if (answerPlaceholder == null) { + return; + } CCCreateAnswerPlaceholderDialog dlg = new CCCreateAnswerPlaceholderDialog(project, answerPlaceholder ); dlg.setTitle("Edit Answer Placeholder"); dlg.show(); } + + @Override + public void update(AnActionEvent e) { + Presentation presentation = e.getPresentation(); + presentation.setEnabledAndVisible(false); + CCState state = getState(e); + if (state == null || state.getAnswerPlaceholder() == null) { + return; + } + presentation.setEnabledAndVisible(true); + } } \ No newline at end of file diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCEditTaskTextAction.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCEditTaskTextAction.java new file mode 100644 index 000000000000..d86a0ae61754 --- /dev/null +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCEditTaskTextAction.java @@ -0,0 +1,81 @@ +package com.jetbrains.edu.coursecreator.actions; + +import com.intellij.icons.AllIcons; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.ToggleAction; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.jetbrains.edu.coursecreator.CCUtils; +import com.jetbrains.edu.learning.StudyState; +import com.jetbrains.edu.learning.StudyTaskManager; +import com.jetbrains.edu.learning.StudyUtils; +import com.jetbrains.edu.learning.core.EduNames; +import com.jetbrains.edu.learning.editor.StudyEditor; +import com.jetbrains.edu.learning.ui.StudyToolWindow; +import org.jetbrains.annotations.NotNull; + +public class CCEditTaskTextAction extends ToggleAction implements DumbAware { + private static final Logger LOG = Logger.getInstance(CCEditTaskTextAction.class); + + public CCEditTaskTextAction() { + super("Editing Mode", "Editing Mode", AllIcons.Modules.Edit); + } + + @Override + public boolean isSelected(AnActionEvent e) { + Project project = e.getProject(); + if (project == null) { + return false; + } + return StudyTaskManager.getInstance(project).getToolWindowMode() == StudyToolWindow.StudyToolWindowMode.EDITING; + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + Project project = e.getProject(); + if (project == null) { + return; + } + StudyToolWindow window = StudyUtils.getStudyToolWindow(project); + if (window == null) { + return; + } + + final StudyEditor selectedEditor = StudyUtils.getSelectedStudyEditor(project); + if (selectedEditor == null) { + StudyTaskManager.getInstance(project).setTurnEditingMode(true); + return; + } + final StudyState studyState = new StudyState(selectedEditor); + VirtualFile taskTextFile = studyState.getTaskDir().findChild(EduNames.TASK_HTML); + if (taskTextFile == null) { + LOG.info("Failed to find task.html"); + return; + } + Document document = FileDocumentManager.getInstance().getDocument(taskTextFile); + if (!state) { + if (document != null) { + FileDocumentManager.getInstance().saveDocument(document); + } + window.leaveEditingMode(project); + return; + } + window.enterEditingMode(taskTextFile, project); + } + + @Override + public void update(@NotNull AnActionEvent e) { + Project project = e.getProject(); + if (project == null) { + return; + } + + if (!CCUtils.isCourseCreator(project)) { + e.getPresentation().setEnabledAndVisible(false); + } + } +} diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCFromCourseArchive.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCFromCourseArchive.java index a7c6a91dc52e..97fdb1ec2e57 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCFromCourseArchive.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCFromCourseArchive.java @@ -22,10 +22,11 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.platform.templates.github.ZipUtil; +import com.jetbrains.edu.coursecreator.CCUtils; +import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.core.EduDocumentListener; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.core.EduUtils; -import com.jetbrains.edu.coursecreator.CCProjectService; import com.jetbrains.edu.learning.courseFormat.*; import com.jetbrains.edu.learning.oldCourseFormat.OldCourse; import org.jetbrains.annotations.NotNull; @@ -58,7 +59,6 @@ public class CCFromCourseArchive extends DumbAwareAction { } final String basePath = project.getBasePath(); if (basePath == null) return; - final CCProjectService service = CCProjectService.getInstance(project); Reader reader = null; try { ZipUtil.unzip(null, new File(basePath), new File(virtualFile.getPath()), null, null, true); @@ -77,7 +77,8 @@ public class CCFromCourseArchive extends DumbAwareAction { course = EduUtils.transformOldCourse(oldCourse); } - service.setCourse(course); + StudyTaskManager.getInstance(project).setCourse(course); + course.setCourseMode(CCUtils.COURSE_MODE); project.getBaseDir().refresh(false, true); int index = 1; int taskIndex = 1; @@ -94,7 +95,7 @@ public class CCFromCourseArchive extends DumbAwareAction { ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { - createAnswerFile(project, taskDir, taskDir, entry); + createAnswerFile(project, taskDir, entry); } }); } @@ -103,6 +104,7 @@ public class CCFromCourseArchive extends DumbAwareAction { index += 1; taskIndex = 1; } + course.initCourse(true); } catch (FileNotFoundException e) { LOG.error(e.getMessage()); @@ -128,7 +130,6 @@ public class CCFromCourseArchive extends DumbAwareAction { public static void createAnswerFile(@NotNull final Project project, @NotNull final VirtualFile userFileDir, - @NotNull final VirtualFile answerFileDir, @NotNull final Map.Entry taskFileEntry) { final String name = taskFileEntry.getKey(); final TaskFile taskFile = taskFileEntry.getValue(); @@ -183,8 +184,9 @@ public class CCFromCourseArchive extends DumbAwareAction { ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { - final String text = document.getText(TextRange.create(offset, offset + answerPlaceholder.getLength())); + final String text = document.getText(TextRange.create(offset, offset + answerPlaceholder.getRealLength())); answerPlaceholder.setTaskText(text); + answerPlaceholder.init(); final VirtualFile hints = project.getBaseDir().findChild(EduNames.HINTS); if (hints != null) { final String hintFile = answerPlaceholder.getHint(); @@ -198,7 +200,7 @@ public class CCFromCourseArchive extends DumbAwareAction { } } - document.replaceString(offset, offset + answerPlaceholder.getLength(), answerPlaceholder.getPossibleAnswer()); + document.replaceString(offset, offset + answerPlaceholder.getRealLength(), answerPlaceholder.getPossibleAnswer()); FileDocumentManager.getInstance().saveDocument(document); } }); diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCHideFromStudent.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCHideFromStudent.java new file mode 100644 index 000000000000..07cd46cfdf20 --- /dev/null +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCHideFromStudent.java @@ -0,0 +1,45 @@ +package com.jetbrains.edu.coursecreator.actions; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.jetbrains.edu.learning.StudyUtils; +import com.jetbrains.edu.learning.courseFormat.Course; +import com.jetbrains.edu.learning.courseFormat.Task; +import com.jetbrains.edu.learning.courseFormat.TaskFile; + +import java.io.IOException; +import java.util.Map; + +public class CCHideFromStudent extends CCTaskFileActionBase { + + private static final Logger LOG = Logger.getInstance(CCHideFromStudent.class); + + @Override + protected void performAction(VirtualFile file, Task task, Course course, Project project) { + Map taskFiles = task.getTaskFiles(); + TaskFile taskFile = StudyUtils.getTaskFile(project, file); + if (taskFile == null) { + return; + } + String name = file.getName(); + VirtualFile patternFile = StudyUtils.getPatternFile(taskFile, name); + ApplicationManager.getApplication().runWriteAction(() -> { + if (patternFile != null) { + try { + patternFile.delete(CCHideFromStudent.class); + } + catch (IOException e) { + LOG.info(e); + } + } + }); + taskFiles.remove(name); + } + + @Override + protected boolean isAvailable(Project project, VirtualFile file) { + return StudyUtils.getTaskFile(project, file) != null; + } +} diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushCourse.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushCourse.java index 78410c9ad3a1..f1a1f3006bf6 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushCourse.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushCourse.java @@ -4,10 +4,12 @@ import com.intellij.ide.IdeView; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.LangDataKeys; +import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; +import com.jetbrains.edu.coursecreator.CCUtils; +import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.courseFormat.Course; -import com.jetbrains.edu.coursecreator.CCProjectService; import com.jetbrains.edu.learning.stepic.EduStepicConnector; import org.jetbrains.annotations.NotNull; @@ -18,7 +20,9 @@ public class CCPushCourse extends DumbAwareAction { @Override public void update(@NotNull AnActionEvent e) { - CCProjectService.setCCActionAvailable(e); + Presentation presentation = e.getPresentation(); + Project project = e.getProject(); + presentation.setEnabledAndVisible(project != null && CCUtils.isCourseCreator(project)); } @Override @@ -28,7 +32,7 @@ public class CCPushCourse extends DumbAwareAction { if (view == null || project == null) { return; } - final Course course = CCProjectService.getInstance(project).getCourse(); + final Course course = StudyTaskManager.getInstance(project).getCourse(); if (course == null) { return; } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushLesson.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushLesson.java index 068b82bf0b1a..4e3f7ecc7414 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushLesson.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushLesson.java @@ -11,9 +11,9 @@ import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiDirectory; +import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.Lesson; -import com.jetbrains.edu.coursecreator.CCProjectService; import com.jetbrains.edu.learning.stepic.EduStepicConnector; import org.jetbrains.annotations.NotNull; @@ -30,7 +30,7 @@ public class CCPushLesson extends DumbAwareAction { if (view == null || project == null) { return; } - final Course course = CCProjectService.getInstance(project).getCourse(); + final Course course = StudyTaskManager.getInstance(project).getCourse(); if (course == null) { return; } @@ -51,7 +51,7 @@ public class CCPushLesson extends DumbAwareAction { if (view == null || project == null) { return; } - final Course course = CCProjectService.getInstance(project).getCourse(); + final Course course = StudyTaskManager.getInstance(project).getCourse(); if (course == null) { return; } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCRunTestsAction.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCRunTestsAction.java deleted file mode 100644 index f6982733db5b..000000000000 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCRunTestsAction.java +++ /dev/null @@ -1,222 +0,0 @@ -/* - * 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.jetbrains.edu.coursecreator.actions; - -import com.intellij.execution.Location; -import com.intellij.execution.actions.ConfigurationContext; -import com.intellij.icons.AllIcons; -import com.intellij.ide.fileTemplates.FileTemplate; -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.Presentation; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.fileEditor.FileDocumentManager; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.jetbrains.edu.learning.core.EduNames; -import com.jetbrains.edu.learning.core.EduUtils; -import com.jetbrains.edu.learning.courseFormat.Course; -import com.jetbrains.edu.learning.courseFormat.Lesson; -import com.jetbrains.edu.learning.courseFormat.Task; -import com.jetbrains.edu.learning.courseFormat.TaskFile; -import com.jetbrains.edu.coursecreator.CCLanguageManager; -import com.jetbrains.edu.coursecreator.CCProjectService; -import com.jetbrains.edu.coursecreator.CCUtils; -import org.jetbrains.annotations.NotNull; - -import java.io.IOException; -import java.util.Map; - -public abstract class CCRunTestsAction extends AnAction { - private static final Logger LOG = Logger.getInstance(CCRunTestsAction.class.getName()); - - public CCRunTestsAction() { - getTemplatePresentation().setIcon(AllIcons.Actions.Lightning); - } - - @Override - public void update(@NotNull AnActionEvent e) { - final Presentation presentation = e.getPresentation(); - if (!CCProjectService.setCCActionAvailable(e)) { - presentation.setEnabledAndVisible(false); - return; - } - - presentation.setEnabledAndVisible(false); - - final ConfigurationContext context = ConfigurationContext.getFromContext(e.getDataContext()); - Location location = context.getLocation(); - if (location == null) { - return; - } - PsiElement psiElement = location.getPsiElement(); - PsiFile psiFile = psiElement.getContainingFile(); - Project project = e.getProject(); - if (project == null || psiFile == null) { - return; - } - TaskFile taskFile = CCProjectService.getInstance(project).getTaskFile(psiFile.getVirtualFile()); - if (taskFile == null) { - return; - } - presentation.setEnabledAndVisible(true); - presentation.setText("Run tests from '" + FileUtil.getNameWithoutExtension(psiFile.getName()) + "'"); - } - - public void actionPerformed(@NotNull AnActionEvent e) { - final ConfigurationContext context = ConfigurationContext.getFromContext(e.getDataContext()); - run(context); - } - - private void run(final @NotNull ConfigurationContext context) { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - final Project project = context.getProject(); - PsiElement location = context.getPsiLocation(); - final Course course = CCProjectService.getInstance(project).getCourse(); - if (course == null || location == null) { - return; - } - PsiFile psiFile = location.getContainingFile(); - final VirtualFile virtualFile = psiFile.getVirtualFile(); - final VirtualFile taskDir = virtualFile.getParent(); - if (taskDir == null) { - return; - } - VirtualFile lessonDir = taskDir.getParent(); - Lesson lesson = course.getLesson(lessonDir.getName()); - if (lesson == null) { - return; - } - final Task task = lesson.getTask(taskDir.getName()); - if (task == null) { - return; - } - clearTestEnvironment(taskDir, project); - CCLanguageManager manager = CCUtils.getStudyLanguageManager(course); - if (manager == null) { - return; - } - for (final Map.Entry entry : task.getTaskFiles().entrySet()) { - final String name = entry.getKey(); - createTaskFileForTest(taskDir, name, entry.getValue(), project); - } - FileTemplate testsTemplate = manager.getTestsTemplate(project); - if (testsTemplate == null) { - return; - } - VirtualFile testFile = taskDir.findChild(testsTemplate.getName() + "." + testsTemplate.getExtension()); - if (testFile == null) { - return; - } - executeTests(project, virtualFile, taskDir, testFile); - } - }); - } - - private static void createTaskFileForTest(@NotNull final VirtualFile taskDir, final String fileName, @NotNull final TaskFile taskFile, - @NotNull final Project project) { - final VirtualFile answerFile = taskDir.findChild(fileName); - if (answerFile == null) { - LOG.debug("could not find answer file " + fileName); - return; - } - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - final FileDocumentManager documentManager = FileDocumentManager.getInstance(); - documentManager.saveAllDocuments(); - } - }); - EduUtils.flushWindows(taskFile, answerFile, false); - createResourceFiles(answerFile, project); - } - - public static void clearTestEnvironment(@NotNull final VirtualFile taskDir, @NotNull final Project project) { - try { - VirtualFile ideaDir = project.getBaseDir().findChild(".idea"); - if (ideaDir == null) { - LOG.debug("idea directory doesn't exist"); - return; - } - VirtualFile courseResourceDir = ideaDir.findChild(EduNames.COURSE); - if (courseResourceDir != null) { - courseResourceDir.delete(project); - } - VirtualFile[] taskDirChildren = taskDir.getChildren(); - for (VirtualFile file : taskDirChildren) { - if (file.getName().contains(EduNames.WINDOWS_POSTFIX)) { - file.delete(project); - } - } - } - catch (IOException e) { - LOG.error(e); - } - } - - protected abstract void executeTests(@NotNull final Project project, - @NotNull final VirtualFile virtualFile, - @NotNull final VirtualFile taskDir, - @NotNull final VirtualFile testFile); - - //some tests could compare task files after user modifications with initial task files - private static void createResourceFiles(@NotNull final VirtualFile file, @NotNull final Project project) { - VirtualFile taskDir = file.getParent(); - int index = EduUtils.getIndex(taskDir.getName(), EduNames.TASK); - VirtualFile lessonDir = taskDir.getParent(); - int lessonIndex = EduUtils.getIndex(lessonDir.getName(), EduNames.LESSON); - Course course = CCProjectService.getInstance(project).getCourse(); - if (course == null) { - return; - } - VirtualFile ideaDir = project.getBaseDir().findChild(".idea"); - assert ideaDir != null; - try { - VirtualFile courseResourceDir = findOrCreateDir(project, ideaDir, EduNames.COURSE); - VirtualFile lessonResourceDir = findOrCreateDir(project, courseResourceDir, lessonDir.getName()); - VirtualFile taskResourceDir = findOrCreateDir(project, lessonResourceDir, taskDir.getName()); - if (EduUtils.indexIsValid(lessonIndex, course.getLessons())) { - Lesson lesson = course.getLessons().get(lessonIndex); - if (EduUtils.indexIsValid(index, lesson.getTaskList())) { - Task task = lesson.getTaskList().get(index); - for (Map.Entry entry : task.getTaskFiles().entrySet()) { - TaskFile taskFile = new TaskFile(); - TaskFile.copy(entry.getValue(), taskFile); - EduUtils.createStudentFileFromAnswer(project, taskResourceDir, taskDir, entry.getKey(), taskFile); - } - } - } - } - catch (IOException e) { - LOG.error(e); - } - } - - private static VirtualFile findOrCreateDir(@NotNull final Project project, @NotNull final VirtualFile dir, String name) - throws IOException { - VirtualFile targetDir = dir.findChild(name); - if (targetDir == null) { - targetDir = dir.createChildDirectory(project, name); - } - return targetDir; - } -} diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCShowPreview.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCShowPreview.java index f0662db25052..39a2ce7f4e0d 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCShowPreview.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCShowPreview.java @@ -37,12 +37,13 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiFile; import com.intellij.ui.JBColor; +import com.jetbrains.edu.learning.StudyTaskManager; +import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.core.EduAnswerPlaceholderPainter; import com.jetbrains.edu.learning.core.EduUtils; import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.TaskFile; -import com.jetbrains.edu.coursecreator.CCProjectService; import com.jetbrains.edu.coursecreator.CCUtils; import org.jetbrains.annotations.NotNull; @@ -61,24 +62,23 @@ public class CCShowPreview extends DumbAwareAction { @Override public void update(@NotNull AnActionEvent e) { - if (!CCProjectService.setCCActionAvailable(e)) { - return; - } + Presentation presentation = e.getPresentation(); + presentation.setEnabledAndVisible(false); Project project = e.getProject(); if (project == null) { return; } - Presentation presentation = e.getPresentation(); - presentation.setEnabledAndVisible(false); + if (!CCUtils.isCourseCreator(project)) { + return; + } final PsiFile file = CommonDataKeys.PSI_FILE.getData(e.getDataContext()); - if (file != null && CCProjectService.getInstance(project).getTaskFile(file.getVirtualFile()) != null) { + if (file != null && StudyUtils.getTaskFile(project, file.getVirtualFile()) != null) { presentation.setEnabledAndVisible(true); } } @Override public void actionPerformed(@NotNull AnActionEvent e) { - //TODO: need to rewrite this action using new GENERATED_ROOT_FOLDER final Project project = e.getProject(); Module module = LangDataKeys.MODULE.getData(e.getDataContext()); if (project == null || module == null) { @@ -88,13 +88,12 @@ public class CCShowPreview extends DumbAwareAction { if (file == null) { return; } - final CCProjectService service = CCProjectService.getInstance(project); - Course course = service.getCourse(); + Course course = StudyTaskManager.getInstance(project).getCourse(); if (course == null) { return; } VirtualFile virtualFile = file.getVirtualFile(); - TaskFile taskFile = service.getTaskFile(virtualFile); + TaskFile taskFile = StudyUtils.getTaskFile(project, virtualFile); if (taskFile == null) { return; } @@ -149,7 +148,7 @@ public class CCShowPreview extends DumbAwareAction { } }); for (AnswerPlaceholder answerPlaceholder : taskFileCopy.getAnswerPlaceholders()) { - EduAnswerPlaceholderPainter.drawAnswerPlaceholder(createdEditor, answerPlaceholder, true, JBColor.BLUE); + EduAnswerPlaceholderPainter.drawAnswerPlaceholder(createdEditor, answerPlaceholder, JBColor.BLUE); } JPanel header = new JPanel(); header.setLayout(new BoxLayout(header, BoxLayout.Y_AXIS)); diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCTaskFileActionBase.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCTaskFileActionBase.java new file mode 100644 index 000000000000..4f0632e82ee8 --- /dev/null +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCTaskFileActionBase.java @@ -0,0 +1,59 @@ +package com.jetbrains.edu.coursecreator.actions; + +import com.intellij.ide.projectView.ProjectView; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.actionSystem.Presentation; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.jetbrains.edu.coursecreator.CCUtils; +import com.jetbrains.edu.learning.StudyTaskManager; +import com.jetbrains.edu.learning.StudyUtils; +import com.jetbrains.edu.learning.courseFormat.Course; +import com.jetbrains.edu.learning.courseFormat.Task; + +public abstract class CCTaskFileActionBase extends AnAction { + + @Override + public void actionPerformed(final AnActionEvent e) { + Project project = e.getProject(); + if (project == null) { + return; + } + final VirtualFile file = CommonDataKeys.VIRTUAL_FILE.getData(e.getDataContext()); + if (file == null) { + return; + } + VirtualFile taskVF = file.getParent(); + if (taskVF == null) { + return; + } + Task task = StudyUtils.getTask(project, taskVF); + if (task == null) { + return; + } + Course course = StudyTaskManager.getInstance(project).getCourse(); + performAction(file, task, course, project); + ProjectView.getInstance(project).refresh(); + } + + protected abstract void performAction(VirtualFile file, Task task, Course course, Project project); + + + @Override + public void update(AnActionEvent e) { + Project project = e.getProject(); + Presentation presentation = e.getPresentation(); + if (project == null || !CCUtils.isCourseCreator(project)) { + presentation.setEnabledAndVisible(false); + return; + } + VirtualFile file = CommonDataKeys.VIRTUAL_FILE.getData(e.getDataContext()); + if (file == null || file.isDirectory() || !isAvailable(project, file)) { + presentation.setEnabledAndVisible(false); + } + } + + protected abstract boolean isAvailable(Project project, VirtualFile file); +} diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCLessonMoveHandlerDelegate.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCLessonMoveHandlerDelegate.java similarity index 93% rename from python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCLessonMoveHandlerDelegate.java rename to python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCLessonMoveHandlerDelegate.java index 685ac3741785..18ca9364c96f 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCLessonMoveHandlerDelegate.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCLessonMoveHandlerDelegate.java @@ -1,4 +1,4 @@ -package com.jetbrains.edu.coursecreator; +package com.jetbrains.edu.coursecreator.handlers; import com.intellij.ide.IdeView; import com.intellij.ide.util.DirectoryChooserUtil; @@ -18,7 +18,10 @@ import com.intellij.psi.PsiReference; import com.intellij.refactoring.move.MoveCallback; import com.intellij.refactoring.move.MoveHandlerDelegate; import com.intellij.util.Function; +import com.jetbrains.edu.coursecreator.CCUtils; import com.jetbrains.edu.coursecreator.ui.CCMoveStudyItemDialog; +import com.jetbrains.edu.learning.StudyTaskManager; +import com.jetbrains.edu.coursecreator.CCUtils; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.core.EduUtils; import com.jetbrains.edu.learning.courseFormat.Course; @@ -67,7 +70,10 @@ public class CCLessonMoveHandlerDelegate extends MoveHandlerDelegate { if (targetContainer == null || !(targetContainer instanceof PsiDirectory)) { return; } - final Course course = CCProjectService.getInstance(project).getCourse(); + final Course course = StudyTaskManager.getInstance(project).getCourse(); + if (course == null) { + return; + } final PsiDirectory sourceDirectory = (PsiDirectory)elements[0]; final Lesson sourceLesson = course.getLesson(sourceDirectory.getName()); final Lesson targetLesson = course.getLesson(((PsiDirectory)targetContainer).getName()); @@ -129,6 +135,6 @@ public class CCLessonMoveHandlerDelegate extends MoveHandlerDelegate { DataContext dataContext, @Nullable PsiReference reference, Editor editor) { - return CCProjectService.getInstance(project).getCourse() != null; + return CCUtils.isCourseCreator(project); } } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCLessonRenameHandler.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCLessonRenameHandler.java similarity index 94% rename from python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCLessonRenameHandler.java rename to python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCLessonRenameHandler.java index 9b29cbafec2e..0efe1110a8ca 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCLessonRenameHandler.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCLessonRenameHandler.java @@ -1,4 +1,4 @@ -package com.jetbrains.edu.coursecreator; +package com.jetbrains.edu.coursecreator.handlers; import com.intellij.ide.TitledHandler; import com.intellij.openapi.project.Project; diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCRenameHandler.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCRenameHandler.java similarity index 88% rename from python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCRenameHandler.java rename to python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCRenameHandler.java index 27bd42ca2484..e98ba1ab133f 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCRenameHandler.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCRenameHandler.java @@ -1,4 +1,4 @@ -package com.jetbrains.edu.coursecreator; +package com.jetbrains.edu.coursecreator.handlers; import com.intellij.ide.projectView.ProjectView; import com.intellij.openapi.actionSystem.CommonDataKeys; @@ -12,6 +12,8 @@ import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.refactoring.rename.RenameHandler; +import com.jetbrains.edu.coursecreator.CCUtils; +import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.StudyItem; import org.jetbrains.annotations.NotNull; @@ -23,9 +25,7 @@ public abstract class CCRenameHandler implements RenameHandler { if (element == null || !(element instanceof PsiDirectory)) { return false; } - CCProjectService instance = CCProjectService.getInstance(element.getProject()); - Course course = instance.getCourse(); - if (course == null) { + if (!CCUtils.isCourseCreator(element.getProject())) { return false; } VirtualFile directory = ((PsiDirectory)element).getVirtualFile(); @@ -44,8 +44,10 @@ public abstract class CCRenameHandler implements RenameHandler { PsiElement element = CommonDataKeys.PSI_ELEMENT.getData(dataContext); assert element != null; PsiDirectory directory = (PsiDirectory)element; - CCProjectService instance = CCProjectService.getInstance(project); - Course course = instance.getCourse(); + Course course = StudyTaskManager.getInstance(project).getCourse(); + if (course == null) { + return; + } rename(project, course, directory); ProjectView.getInstance(project).refresh(); } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCTaskMoveHandlerDelegate.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCTaskMoveHandlerDelegate.java similarity index 94% rename from python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCTaskMoveHandlerDelegate.java rename to python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCTaskMoveHandlerDelegate.java index 380e0556112b..91083e9f9edb 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCTaskMoveHandlerDelegate.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCTaskMoveHandlerDelegate.java @@ -1,4 +1,4 @@ -package com.jetbrains.edu.coursecreator; +package com.jetbrains.edu.coursecreator.handlers; import com.intellij.ide.IdeView; import com.intellij.ide.util.DirectoryChooserUtil; @@ -18,13 +18,15 @@ import com.intellij.psi.PsiReference; import com.intellij.refactoring.move.MoveCallback; import com.intellij.refactoring.move.MoveHandlerDelegate; import com.intellij.util.Function; +import com.jetbrains.edu.coursecreator.CCUtils; +import com.jetbrains.edu.coursecreator.ui.CCMoveStudyItemDialog; +import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.core.EduUtils; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.Lesson; import com.jetbrains.edu.learning.courseFormat.StudyItem; import com.jetbrains.edu.learning.courseFormat.Task; -import com.jetbrains.edu.coursecreator.ui.CCMoveStudyItemDialog; import org.jetbrains.annotations.Nullable; import java.io.IOException; @@ -59,9 +61,9 @@ public class CCTaskMoveHandlerDelegate extends MoveHandlerDelegate { if (sourceDirectory == null) { return false; } - CCProjectService service = CCProjectService.getInstance(sourceDirectory.getProject()); - Course course = service.getCourse(); - if (course == null) { + Project project = sourceDirectory.getProject(); + Course course = StudyTaskManager.getInstance(project).getCourse(); + if (course == null || !CCUtils.isCourseCreator(project)) { return false; } return EduUtils.getTask(sourceDirectory, course) != null; @@ -86,7 +88,7 @@ public class CCTaskMoveHandlerDelegate extends MoveHandlerDelegate { Messages.showInfoMessage("Tasks can be moved only to other lessons or inside lesson", "Incorrect Target For Move"); return; } - final Course course = CCProjectService.getInstance(project).getCourse(); + final Course course = StudyTaskManager.getInstance(project).getCourse(); final PsiDirectory sourceDirectory = (PsiDirectory)elements[0]; final Task taskToMove = EduUtils.getTask(sourceDirectory, course); @@ -192,6 +194,6 @@ public class CCTaskMoveHandlerDelegate extends MoveHandlerDelegate { DataContext dataContext, @Nullable PsiReference reference, Editor editor) { - return CCProjectService.getInstance(project).getCourse() != null; + return CCUtils.isCourseCreator(project); } } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCTaskRenameHandler.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCTaskRenameHandler.java similarity index 96% rename from python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCTaskRenameHandler.java rename to python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCTaskRenameHandler.java index 0f30a1d689f5..98bcba87c6c8 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCTaskRenameHandler.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCTaskRenameHandler.java @@ -1,4 +1,4 @@ -package com.jetbrains.edu.coursecreator; +package com.jetbrains.edu.coursecreator.handlers; import com.intellij.ide.TitledHandler; import com.intellij.openapi.project.Project; diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/projectView/CCDirectoryNode.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/projectView/CCDirectoryNode.java deleted file mode 100644 index 551d344e3772..000000000000 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/projectView/CCDirectoryNode.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.jetbrains.edu.coursecreator.projectView; - -import com.intellij.ide.projectView.PresentationData; -import com.intellij.ide.projectView.ViewSettings; -import com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode; -import com.intellij.openapi.project.Project; -import com.intellij.psi.PsiDirectory; -import com.intellij.ui.SimpleTextAttributes; -import com.jetbrains.edu.learning.core.EduNames; -import com.jetbrains.edu.learning.core.EduUtils; -import com.jetbrains.edu.learning.courseFormat.Course; -import com.jetbrains.edu.learning.courseFormat.Lesson; -import com.jetbrains.edu.learning.courseFormat.Task; -import com.jetbrains.edu.coursecreator.CCProjectService; -import icons.InteractiveLearningIcons; -import org.jetbrains.annotations.NotNull; - -public class CCDirectoryNode extends PsiDirectoryNode { - private final PsiDirectory myValue; - private final Project myProject; - - public CCDirectoryNode(@NotNull final Project project, - PsiDirectory value, - ViewSettings viewSettings) { - super(project, value, viewSettings); - myValue = value; - myProject = project; - } - - @Override - protected void updateImpl(PresentationData data) { - String valueName = myValue.getName(); - final CCProjectService service = CCProjectService.getInstance(myProject); - final Course course = service.getCourse(); - if (course == null) return; - if (myProject.getBaseDir().equals(myValue.getVirtualFile())) { - data.clearText(); - data.setIcon(InteractiveLearningIcons.Course); - data.addText(course.getName(), SimpleTextAttributes.REGULAR_ATTRIBUTES); - data.addText(" (" + valueName + ")", SimpleTextAttributes.GRAYED_ATTRIBUTES); - return; - } - final Lesson lesson = course.getLesson(valueName); - if (lesson != null) { - data.clearText(); - data.setIcon(InteractiveLearningIcons.Lesson); - data.addText(lesson.getName(), SimpleTextAttributes.REGULAR_ATTRIBUTES); - return; - } - else { - final PsiDirectory parentDir = myValue.getParentDirectory(); - if (parentDir != null) { - final Lesson parentLesson = course.getLesson(parentDir.getName()); - if (parentLesson != null) { - final Task task = parentLesson.getTask(valueName); - if (task != null) { - data.clearText(); - data.setIcon(InteractiveLearningIcons.Task); - data.addText(task.getName(), SimpleTextAttributes.REGULAR_ATTRIBUTES); - return; - } - } - } - } - data.setPresentableText(valueName); - } - - @Override - public int getTypeSortWeight(boolean sortByType) { - String name = myValue.getName(); - if (name.startsWith(EduNames.LESSON) || name.startsWith(EduNames.TASK)) { - String logicalName = name.contains(EduNames.LESSON) ? EduNames.LESSON : EduNames.TASK; - int index = EduUtils.getIndex(name, logicalName) + 1; - return index != -1 ? index + 1: 0; - } - return 0; - } - - @Override - public String getNavigateActionText(boolean focusEditor) { - return null; - } -} diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/projectView/CCTreeStructureProvider.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/projectView/CCTreeStructureProvider.java index d4f39fbefd2f..2702e3d4cf56 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/projectView/CCTreeStructureProvider.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/projectView/CCTreeStructureProvider.java @@ -1,23 +1,22 @@ package com.jetbrains.edu.coursecreator.projectView; -import com.intellij.ide.projectView.TreeStructureProvider; import com.intellij.ide.projectView.ViewSettings; import com.intellij.ide.projectView.impl.nodes.PsiFileNode; import com.intellij.ide.util.treeView.AbstractTreeNode; -import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.io.FileUtilRt; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; -import com.jetbrains.edu.learning.core.EduNames; -import com.jetbrains.edu.coursecreator.CCProjectService; import com.jetbrains.edu.coursecreator.CCUtils; +import com.jetbrains.edu.learning.StudyUtils; +import com.jetbrains.edu.learning.core.EduNames; +import com.jetbrains.edu.learning.projectView.StudyTreeStructureProvider; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collection; -public class CCTreeStructureProvider implements TreeStructureProvider, DumbAware { +public class CCTreeStructureProvider extends StudyTreeStructureProvider { @NotNull @Override public Collection modify(@NotNull AbstractTreeNode parent, @@ -26,18 +25,16 @@ public class CCTreeStructureProvider implements TreeStructureProvider, DumbAware if (!needModify(parent)) { return children; } - Collection nodes = new ArrayList(); + Collection modifiedChildren = new ArrayList(super.modify(parent, children, settings)); + for (AbstractTreeNode node : children) { Project project = node.getProject(); - if (project != null) { if (node.getValue() instanceof PsiDirectory) { String name = ((PsiDirectory)node.getValue()).getName(); - if (CCUtils.GENERATED_FILES_FOLDER.equals(name)) { + if ("zip".equals(FileUtilRt.getExtension(name))) { + modifiedChildren.add(node); continue; } - PsiDirectory directory = (PsiDirectory)node.getValue(); - nodes.add(new CCDirectoryNode(project, directory, settings)); - continue; } if (node instanceof PsiFileNode) { PsiFileNode fileNode = (PsiFileNode)node; @@ -45,34 +42,19 @@ public class CCTreeStructureProvider implements TreeStructureProvider, DumbAware if (virtualFile == null) { continue; } - if (virtualFile.getName().contains(EduNames.WINDOWS_POSTFIX)) { - continue; - } - - if (virtualFile.getParent().getName().contains(EduNames.TASK) && !CCProjectService.getInstance(project).isTaskFile(virtualFile)) { - nodes.add(new CCStudentInvisibleFileNode(project, ((PsiFileNode)node).getValue(), settings)); - continue; + if (StudyUtils.getTaskFile(project, virtualFile) == null && !EduNames.TASK_HTML.equals(virtualFile.getName())) { + modifiedChildren.add(new CCStudentInvisibleFileNode(project, ((PsiFileNode)node).getValue(), settings)); } } - nodes.add(node); - } } - return nodes; + return modifiedChildren; } - private static boolean needModify(@NotNull final AbstractTreeNode parent) { + protected boolean needModify(@NotNull final AbstractTreeNode parent) { Project project = parent.getProject(); - if (project != null) { - if (CCProjectService.getInstance(project).getCourse() == null) { - return false; - } + if (project == null) { + return false; } - return true; - } - - @Nullable - @Override - public Object getData(Collection selected, String dataName) { - return null; + return CCUtils.isCourseCreator(project); } } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/ui/CreateCourseArchiveDialog.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/ui/CreateCourseArchiveDialog.java index d1bc4fca3469..538665e54864 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/ui/CreateCourseArchiveDialog.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/ui/CreateCourseArchiveDialog.java @@ -2,8 +2,8 @@ package com.jetbrains.edu.coursecreator.ui; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; +import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.courseFormat.Course; -import com.jetbrains.edu.coursecreator.CCProjectService; import com.jetbrains.edu.coursecreator.actions.CCCreateCourseArchive; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -17,7 +17,7 @@ public class CreateCourseArchiveDialog extends DialogWrapper { public CreateCourseArchiveDialog(@NotNull final Project project, CCCreateCourseArchive action) { super(project); - Course course = CCProjectService.getInstance(project).getCourse(); + Course course = StudyTaskManager.getInstance(project).getCourse(); assert course != null; setTitle("Create Course Archive"); myPanel = new CreateCourseArchivePanel(project, this, course.getName()); diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/ui/CreateCourseArchivePanel.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/ui/CreateCourseArchivePanel.java index c84a5cdf2b26..1eeb2edd0b14 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/ui/CreateCourseArchivePanel.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/ui/CreateCourseArchivePanel.java @@ -33,7 +33,7 @@ public class CreateCourseArchivePanel extends JPanel { myNameField.setText(sanitizedName.startsWith("_") ? EduNames.COURSE : sanitizedName); myLocationField.setText(project.getBasePath()); FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor(); - myLocationField.addBrowseFolderListener("Choose location folder", null, project, descriptor); + myLocationField.addBrowseFolderListener("Choose Location Folder", null, project, descriptor); myLocationField.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { diff --git a/python/educational-core/student/resources/META-INF/plugin.xml b/python/educational-core/student/resources/META-INF/plugin.xml index 296fd98bd005..1ebb41dc0aed 100644 --- a/python/educational-core/student/resources/META-INF/plugin.xml +++ b/python/educational-core/student/resources/META-INF/plugin.xml @@ -36,7 +36,9 @@ + + > diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyActionListener.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyActionListener.java new file mode 100644 index 000000000000..525adc2eddd3 --- /dev/null +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyActionListener.java @@ -0,0 +1,10 @@ +package com.jetbrains.edu.learning; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.extensions.ExtensionPointName; + +public interface StudyActionListener { + ExtensionPointName EP_NAME = ExtensionPointName.create("Edu.studyActionListener"); + + void beforeCheck(AnActionEvent event); +} diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyActionsProvider.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyActionsProvider.java new file mode 100644 index 000000000000..4aca599efea7 --- /dev/null +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyActionsProvider.java @@ -0,0 +1,10 @@ +package com.jetbrains.edu.learning; + +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.extensions.ExtensionPointName; + +public interface StudyActionsProvider { + ExtensionPointName EP_NAME = ExtensionPointName.create("Edu.studyActionsProvider"); + + AnAction[] getActions(); +} diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyAnswerPlaceholderExtendWordHandler.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyAnswerPlaceholderExtendWordHandler.java index 8ebc8f4dadd6..ad364dfc02f2 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyAnswerPlaceholderExtendWordHandler.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyAnswerPlaceholderExtendWordHandler.java @@ -59,6 +59,6 @@ public class StudyAnswerPlaceholderExtendWordHandler implements ExtendWordSelect Document document = FileDocumentManager.getInstance().getDocument(file); assert document != null; int startOffset = placeholder.getRealStartOffset(document); - return Collections.singletonList(new TextRange(startOffset, startOffset + placeholder.getLength())); + return Collections.singletonList(new TextRange(startOffset, startOffset + placeholder.getRealLength())); } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyBasePluginConfigurator.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyBasePluginConfigurator.java index 74410d74ce84..4fde0a889748 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyBasePluginConfigurator.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyBasePluginConfigurator.java @@ -48,9 +48,6 @@ public abstract class StudyBasePluginConfigurator implements StudyPluginConfigur public FileEditorManagerListener getFileEditorManagerListener(@NotNull Project project, @NotNull StudyToolWindow toolWindow) { return new FileEditorManagerListener() { - - private static final String EMPTY_TASK_TEXT = "Please, open any task to see task description"; - @Override public void fileOpened(@NotNull FileEditorManager source, @NotNull VirtualFile file) { Task task = getTask(file); @@ -59,7 +56,7 @@ public abstract class StudyBasePluginConfigurator implements StudyPluginConfigur @Override public void fileClosed(@NotNull FileEditorManager source, @NotNull VirtualFile file) { - toolWindow.setTaskText(EMPTY_TASK_TEXT); + toolWindow.setEmptyText(project); } @Override @@ -85,10 +82,10 @@ public abstract class StudyBasePluginConfigurator implements StudyPluginConfigur private void setTaskText(@Nullable final Task task, @Nullable final VirtualFile taskDirectory) { String text = StudyUtils.getTaskTextFromTask(task, taskDirectory); if (text == null) { - toolWindow.setTaskText(EMPTY_TASK_TEXT); + toolWindow.setEmptyText(project); return; } - toolWindow.setTaskText(text); + toolWindow.setTaskText(text, taskDirectory, project); } }; } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyMoveDelegate.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyMoveDelegate.java index 7cd21a26b510..abac20c57afe 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyMoveDelegate.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyMoveDelegate.java @@ -8,6 +8,7 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiReference; import com.intellij.refactoring.move.MoveCallback; import com.intellij.refactoring.move.MoveHandlerDelegate; +import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.Course; import org.jetbrains.annotations.Nullable; @@ -22,7 +23,7 @@ public class StudyMoveDelegate extends MoveHandlerDelegate{ if (elements.length == 1) { Project project = elements[0].getProject(); Course course = StudyTaskManager.getInstance(project).getCourse(); - if (course == null) { + if (course == null || !EduNames.STUDY.equals(course.getCourseMode())) { return false; } return !StudyUtils.isRenameableOrMoveable(project, course, elements[0]); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyPluginConfigurator.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyPluginConfigurator.java index 61d9e78570d6..db5ee3b39143 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyPluginConfigurator.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyPluginConfigurator.java @@ -40,6 +40,6 @@ public interface StudyPluginConfigurator { StudyAfterCheckAction[] getAfterCheckActions(); @NotNull String getLanguageScriptUrl(); - + boolean accept(@NotNull final Project project); } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java index 205b7929960a..ac816c7ebde5 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java @@ -25,13 +25,13 @@ import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowAnchor; import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.util.containers.hash.HashMap; -import com.jetbrains.edu.learning.actions.StudyToolbarAction; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.core.EduUtils; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.Lesson; import com.jetbrains.edu.learning.courseFormat.Task; import com.jetbrains.edu.learning.courseFormat.TaskFile; +import com.jetbrains.edu.learning.actions.*; import com.jetbrains.edu.learning.editor.StudyEditorFactoryListener; import com.jetbrains.edu.learning.ui.StudyProgressToolWindowFactory; import com.jetbrains.edu.learning.ui.StudyToolWindow; @@ -110,9 +110,9 @@ public class StudyProjectComponent implements ProjectComponent { List actionsOnToolbar = window.getActions(true); if (actionsOnToolbar != null) { for (AnAction action : actionsOnToolbar) { - if (action instanceof StudyToolbarAction) { - String id = ((StudyToolbarAction)action).getActionId(); - String[] shortcuts = ((StudyToolbarAction)action).getShortcuts(); + if (action instanceof StudyActionWithShortcut) { + String id = ((StudyActionWithShortcut)action).getActionId(); + String[] shortcuts = ((StudyActionWithShortcut)action).getShortcuts(); if (shortcuts != null) { addShortcut(id, shortcuts); } @@ -227,7 +227,7 @@ public class StudyProjectComponent implements ProjectComponent { keymap.addShortcut(actionShortcut.first, new KeyboardShortcut(KeyStroke.getKeyStroke(actionShortcut.second), null)); } } - } + } } myListener = null; } @@ -287,30 +287,30 @@ public class StudyProjectComponent implements ProjectComponent { final VirtualFile createdFile = event.getFile(); final VirtualFile taskDir = createdFile.getParent(); final Course course = StudyTaskManager.getInstance(myProject).getCourse(); + if (course == null || !EduNames.STUDY.equals(course.getCourseMode())) { + return; + } if (taskDir != null && taskDir.getName().contains(EduNames.TASK)) { int taskIndex = EduUtils.getIndex(taskDir.getName(), EduNames.TASK); final VirtualFile lessonDir = taskDir.getParent(); if (lessonDir != null && lessonDir.getName().contains(EduNames.LESSON)) { int lessonIndex = EduUtils.getIndex(lessonDir.getName(), EduNames.LESSON); - if (course != null) { - List lessons = course.getLessons(); - if (StudyUtils.indexIsValid(lessonIndex, lessons)) { - final Lesson lesson = lessons.get(lessonIndex); - final List tasks = lesson.getTaskList(); - if (StudyUtils.indexIsValid(taskIndex, tasks)) { - final Task task = tasks.get(taskIndex); - final TaskFile taskFile = new TaskFile(); - taskFile.initTaskFile(task, false); - taskFile.setUserCreated(true); - final String name = createdFile.getName(); - taskFile.name = name; - task.getTaskFiles().put(name, taskFile); - } + List lessons = course.getLessons(); + if (StudyUtils.indexIsValid(lessonIndex, lessons)) { + final Lesson lesson = lessons.get(lessonIndex); + final List tasks = lesson.getTaskList(); + if (StudyUtils.indexIsValid(taskIndex, tasks)) { + final Task task = tasks.get(taskIndex); + final TaskFile taskFile = new TaskFile(); + taskFile.initTaskFile(task, false); + taskFile.setUserCreated(true); + final String name = createdFile.getName(); + taskFile.name = name; + task.getTaskFiles().put(name, taskFile); } } } } } } - } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyTaskManager.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyTaskManager.java index b534bba9118b..024cd09e9245 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyTaskManager.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyTaskManager.java @@ -14,6 +14,7 @@ import com.intellij.util.xmlb.XmlSerializer; import com.jetbrains.edu.learning.core.EduUtils; import com.jetbrains.edu.learning.courseFormat.*; import com.jetbrains.edu.learning.oldCourseFormat.OldCourse; +import com.jetbrains.edu.learning.ui.StudyToolWindow; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -38,6 +39,8 @@ public class StudyTaskManager implements PersistentStateComponent, Dumb public Map> myUserTests = new HashMap<>(); public List myInvisibleFiles = new ArrayList<>(); public boolean myShouldUseJavaFx = StudyUtils.hasJavaFx(); + private StudyToolWindow.StudyToolWindowMode myToolWindowMode = StudyToolWindow.StudyToolWindowMode.TEXT; + private boolean myTurnEditingMode = false; private StudyTaskManager() { } @@ -71,7 +74,7 @@ public class StudyTaskManager implements PersistentStateComponent, Dumb @NotNull public List getUserTests(@NotNull final Task task) { final List userTests = myUserTests.get(task); - return userTests != null ? userTests : Collections.emptyList(); + return userTests != null ? userTests : Collections.emptyList(); } public void removeUserTest(@NotNull final Task task, @NotNull final UserTest userTest) { @@ -260,4 +263,20 @@ public class StudyTaskManager implements PersistentStateComponent, Dumb public void setShouldUseJavaFx(boolean shouldUseJavaFx) { this.myShouldUseJavaFx = shouldUseJavaFx; } + + public StudyToolWindow.StudyToolWindowMode getToolWindowMode() { + return myToolWindowMode; + } + + public void setToolWindowMode(StudyToolWindow.StudyToolWindowMode toolWindowMode) { + myToolWindowMode = toolWindowMode; + } + + public boolean isTurnEditingMode() { + return myTurnEditingMode; + } + + public void setTurnEditingMode(boolean turnEditingMode) { + myTurnEditingMode = turnEditingMode; + } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java index 73d7843efcc8..2c41cda8d574 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java @@ -128,7 +128,7 @@ public class StudyUtils { } public static void updateToolWindows(@NotNull final Project project) { - update(project); + updateStudyToolWindow(project); final ToolWindowManager windowManager = ToolWindowManager.getInstance(project); createProgressToolWindowContent(project, windowManager); @@ -300,7 +300,6 @@ public class StudyUtils { return null; } - public static void drawAllWindows(Editor editor, TaskFile taskFile) { editor.getMarkupModel().removeAllHighlighters(); final Project project = editor.getProject(); @@ -308,12 +307,12 @@ public class StudyUtils { final StudyTaskManager taskManager = StudyTaskManager.getInstance(project); for (AnswerPlaceholder answerPlaceholder : taskFile.getAnswerPlaceholders()) { final JBColor color = taskManager.getColor(answerPlaceholder); - EduAnswerPlaceholderPainter.drawAnswerPlaceholder(editor, answerPlaceholder, true, color); + EduAnswerPlaceholderPainter.drawAnswerPlaceholder(editor, answerPlaceholder, color); } final Document document = editor.getDocument(); EditorActionManager.getInstance() .setReadonlyFragmentModificationHandler(document, new EduAnswerPlaceholderDeleteHandler(editor)); - EduAnswerPlaceholderPainter.createGuardedBlocks(editor, taskFile, true); + EduAnswerPlaceholderPainter.createGuardedBlocks(editor, taskFile); editor.getColorsScheme().setColor(EditorColors.READONLY_FRAGMENT_BACKGROUND_COLOR, null); } @@ -353,21 +352,31 @@ public class StudyUtils { } } + @Nullable - public static Document getPatternDocument(@NotNull final TaskFile taskFile, String name) { + public static VirtualFile getPatternFile(@NotNull TaskFile taskFile, String name) { Task task = taskFile.getTask(); String lessonDir = EduNames.LESSON + String.valueOf(task.getLesson().getIndex()); String taskDir = EduNames.TASK + String.valueOf(task.getIndex()); Course course = task.getLesson().getCourse(); File resourceFile = new File(course.getCourseDirectory()); if (!resourceFile.exists()) { - return null; + return null; } String patternPath = FileUtil.join(resourceFile.getPath(), lessonDir, taskDir, name); VirtualFile patternFile = VfsUtil.findFileByIoFile(new File(patternPath), true); if (patternFile == null) { return null; } + return patternFile; + } + + @Nullable + public static Document getPatternDocument(@NotNull final TaskFile taskFile, String name) { + VirtualFile patternFile = getPatternFile(taskFile, name); + if (patternFile == null) { + return null; + } return FileDocumentManager.getInstance().getDocument(patternFile); } @@ -411,9 +420,10 @@ public class StudyUtils { return false; } Course course = StudyTaskManager.getInstance(project).getCourse(); - if (course == null) { + if (course == null || !EduNames.STUDY.equals(course.getCourseMode())) { return false; } + if (!isRenameableOrMoveable(project, course, element)) { return true; } @@ -473,14 +483,7 @@ public class StudyUtils { public static String getTaskText(@NotNull final Project project) { - VirtualFile[] files = FileEditorManager.getInstance(project).getSelectedFiles(); - TaskFile taskFile = null; - for (VirtualFile file : files) { - taskFile = getTaskFile(project, file); - if (taskFile != null) { - break; - } - } + TaskFile taskFile = getSelectedTaskFile(project); if (taskFile == null) { return EMPTY_TASK_TEXT; } @@ -490,19 +493,31 @@ public class StudyUtils { } return null; } + @Nullable + public static TaskFile getSelectedTaskFile(@NotNull Project project) { + VirtualFile[] files = FileEditorManager.getInstance(project).getSelectedFiles(); + TaskFile taskFile = null; + for (VirtualFile file : files) { + taskFile = getTaskFile(project, file); + if (taskFile != null) { + break; + } + } + return taskFile; + } - public static void update(Project project) { + public static void updateStudyToolWindow(Project project) { final StudyToolWindow studyToolWindow = getStudyToolWindow(project); if (studyToolWindow != null) { String taskText = getTaskText(project); - studyToolWindow.setTaskText(taskText); + studyToolWindow.setTaskText(taskText, null, project); } } public static boolean isStudyProject(@NotNull Project project) { return StudyTaskManager.getInstance(project).getCourse() != null; } - + public static boolean hasJavaFx() { try { Class.forName("javafx.application.Platform"); @@ -512,4 +527,37 @@ public class StudyUtils { return false; } } + + @Nullable + public static Task getTask(@NotNull Project project, @NotNull VirtualFile taskVF) { + Course course = StudyTaskManager.getInstance(project).getCourse(); + if (course == null) { + return null; + } + VirtualFile lessonVF = taskVF.getParent(); + if (lessonVF == null) { + return null; + } + Lesson lesson = course.getLesson(lessonVF.getName()); + if (lesson == null) { + return null; + } + return lesson.getTask(taskVF.getName()); + } + + @Nullable + public static VirtualFile getTaskDir(@NotNull VirtualFile taskFile) { + VirtualFile parent = taskFile.getParent(); + if (parent == null) { + return null; + } + String name = parent.getName(); + if (name.contains(EduNames.TASK)) { + return parent; + } + if (EduNames.SRC.equals(name)) { + return parent.getParent(); + } + return null; + } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyToolbarAction.java b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyActionWithShortcut.java similarity index 58% rename from python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyToolbarAction.java rename to python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyActionWithShortcut.java index a74e547984ef..ae1fa651031c 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyToolbarAction.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyActionWithShortcut.java @@ -6,8 +6,9 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; -public abstract class StudyToolbarAction extends DumbAwareAction { - public StudyToolbarAction(@Nullable String text, @Nullable String description, @Nullable Icon icon) { +public abstract class StudyActionWithShortcut extends DumbAwareAction { + + public StudyActionWithShortcut(@Nullable String text, @Nullable String description, @Nullable Icon icon) { super(text, description, icon); } @@ -15,5 +16,5 @@ public abstract class StudyToolbarAction extends DumbAwareAction { public abstract String getActionId(); @Nullable - public abstract String[] getShortcuts(); -} + public abstract String[] getShortcuts(); +} \ No newline at end of file diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyCheckAction.java b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyCheckAction.java index be3695794db6..623c7326972a 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyCheckAction.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyCheckAction.java @@ -3,11 +3,13 @@ package com.jetbrains.edu.learning.actions; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.KeyboardShortcut; import com.intellij.openapi.actionSystem.Presentation; +import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.keymap.KeymapUtil; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.util.Ref; +import com.jetbrains.edu.learning.StudyActionListener; import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.checker.StudyCheckUtils; import icons.InteractiveLearningIcons; @@ -15,7 +17,7 @@ import org.jetbrains.annotations.NotNull; import javax.swing.*; -public abstract class StudyCheckAction extends StudyToolbarAction { +public abstract class StudyCheckAction extends StudyActionWithShortcut { public static final String SHORTCUT = "ctrl alt pressed ENTER"; protected Ref myCheckInProgress = new Ref<>(false); @@ -36,6 +38,9 @@ public abstract class StudyCheckAction extends StudyToolbarAction { StudyCheckUtils.showTestResultPopUp("Checking is not available while indexing is in progress", MessageType.WARNING.getPopupBackground(), project); return; } + for (StudyActionListener listener : Extensions.getExtensions(StudyActionListener.EP_NAME)) { + listener.beforeCheck(e); + } check(project); } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyFillPlaceholdersAction.java b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyFillPlaceholdersAction.java index e65c8a02bd6d..54c8e2c4ac08 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyFillPlaceholdersAction.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyFillPlaceholdersAction.java @@ -36,7 +36,7 @@ public class StudyFillPlaceholdersAction extends AnAction { continue; } int offset = placeholder.getRealStartOffset(document); - document.deleteString(offset, offset + placeholder.getLength()); + document.deleteString(offset, offset + placeholder.getRealLength()); document.insertString(offset, answer); } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyRefreshAnswerPlaceholder.java b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyRefreshAnswerPlaceholder.java index 42a7674c384b..7daa54735398 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyRefreshAnswerPlaceholder.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyRefreshAnswerPlaceholder.java @@ -6,9 +6,11 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; +import com.jetbrains.edu.learning.StudyActionListener; import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder; import com.jetbrains.edu.learning.courseFormat.TaskFile; import com.jetbrains.edu.learning.StudyState; @@ -30,6 +32,9 @@ public class StudyRefreshAnswerPlaceholder extends DumbAwareAction { if (project == null) { return; } + for (StudyActionListener listener : Extensions.getExtensions(StudyActionListener.EP_NAME)) { + listener.beforeCheck(e); + } final AnswerPlaceholder answerPlaceholder = getAnswerPlaceholder(e); if (answerPlaceholder == null) { return; @@ -51,7 +56,7 @@ public class StudyRefreshAnswerPlaceholder extends DumbAwareAction { public void run() { Document document = studyState.getEditor().getDocument(); int offset = answerPlaceholder.getRealStartOffset(document); - document.deleteString(offset, offset + answerPlaceholder.getLength()); + document.deleteString(offset, offset + answerPlaceholder.getRealLength()); document.insertString(offset, text); } }); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyRefreshTaskFileAction.java b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyRefreshTaskFileAction.java index ef52729f7740..3c8057c75dea 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyRefreshTaskFileAction.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyRefreshTaskFileAction.java @@ -9,6 +9,7 @@ import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.keymap.KeymapUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.MessageType; @@ -19,13 +20,14 @@ import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.problems.WolfTheProblemSolver; +import com.jetbrains.edu.learning.StudyActionListener; +import com.jetbrains.edu.learning.StudyState; +import com.jetbrains.edu.learning.StudyTaskManager; +import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.core.EduAnswerPlaceholderPainter; import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder; import com.jetbrains.edu.learning.courseFormat.StudyStatus; import com.jetbrains.edu.learning.courseFormat.TaskFile; -import com.jetbrains.edu.learning.StudyState; -import com.jetbrains.edu.learning.StudyTaskManager; -import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.editor.StudyEditor; import com.jetbrains.edu.learning.navigation.StudyNavigator; import org.jetbrains.annotations.NotNull; @@ -33,7 +35,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; -public class StudyRefreshTaskFileAction extends StudyToolbarAction { +public class StudyRefreshTaskFileAction extends StudyActionWithShortcut { public static final String ACTION_ID = "RefreshTaskAction"; public static final String SHORTCUT = "ctrl shift pressed X"; private static final Logger LOG = Logger.getInstance(StudyRefreshTaskFileAction.class.getName()); @@ -73,7 +75,7 @@ public class StudyRefreshTaskFileAction extends StudyToolbarAction { WolfTheProblemSolver.getInstance(project).clearProblems(studyState.getVirtualFile()); taskFile.setHighlightErrors(false); StudyUtils.drawAllWindows(editor, taskFile); - EduAnswerPlaceholderPainter.createGuardedBlocks(editor, taskFile, true); + EduAnswerPlaceholderPainter.createGuardedBlocks(editor, taskFile); ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { @@ -148,6 +150,9 @@ public class StudyRefreshTaskFileAction extends StudyToolbarAction { public void actionPerformed(@NotNull AnActionEvent event) { final Project project = event.getProject(); if (project != null) { + for (StudyActionListener listener : Extensions.getExtensions(StudyActionListener.EP_NAME)) { + listener.beforeCheck(event); + } refresh(project); } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyShowHintAction.java b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyShowHintAction.java index 25642dc0cf87..75a1277c88d9 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyShowHintAction.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyShowHintAction.java @@ -25,7 +25,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; -public class StudyShowHintAction extends StudyToolbarAction { +public class StudyShowHintAction extends StudyActionWithShortcut { public static final String ACTION_ID = "ShowHintAction"; public static final String SHORTCUT = "ctrl pressed 7"; private static final String ourWarningMessage = "Put the caret in the answer placeholder to get hint"; diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyTaskNavigationAction.java b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyTaskNavigationAction.java index 580bc3557d64..308a423d68f6 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyTaskNavigationAction.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyTaskNavigationAction.java @@ -27,7 +27,7 @@ import javax.swing.tree.TreePath; import java.util.Map; -abstract public class StudyTaskNavigationAction extends StudyToolbarAction { +abstract public class StudyTaskNavigationAction extends StudyActionWithShortcut { public StudyTaskNavigationAction(@Nullable String text, @Nullable String description, @Nullable Icon icon) { super(text, description, icon); } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyWindowNavigationAction.java b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyWindowNavigationAction.java index 50be6069aa36..cd0b5ccf1dbe 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyWindowNavigationAction.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyWindowNavigationAction.java @@ -16,7 +16,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; -abstract public class StudyWindowNavigationAction extends StudyToolbarAction implements DumbAware { +abstract public class StudyWindowNavigationAction extends StudyActionWithShortcut implements DumbAware { protected StudyWindowNavigationAction(String actionId, String description, Icon icon) { super(actionId, description, icon); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckUtils.java b/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckUtils.java index 9fe0ae84ae6b..33c626a05bcb 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckUtils.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckUtils.java @@ -21,6 +21,7 @@ import com.intellij.openapi.wm.WindowManager; import com.intellij.openapi.wm.ex.StatusBarEx; import com.intellij.openapi.wm.ex.WindowManagerEx; import com.jetbrains.edu.learning.core.EduDocumentListener; +import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.core.EduUtils; import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder; import com.jetbrains.edu.learning.courseFormat.Task; @@ -38,7 +39,6 @@ import java.util.List; import java.util.Map; public class StudyCheckUtils { - private static final String ANSWERS_POSTFIX = "_answers"; private static final Logger LOG = Logger.getInstance(StudyCheckUtils.class); private StudyCheckUtils() { @@ -143,7 +143,7 @@ public class StudyCheckUtils { VirtualFile copy = null; try { - copy = file.copy(taskDir, taskDir, file.getNameWithoutExtension() + ANSWERS_POSTFIX + "." + file.getExtension()); + copy = file.copy(taskDir, taskDir, file.getNameWithoutExtension() + EduNames.ANSWERS_POSTFIX + "." + file.getExtension()); final FileDocumentManager documentManager = FileDocumentManager.getInstance(); final Document document = documentManager.getDocument(copy); if (document != null) { @@ -155,7 +155,7 @@ public class StudyCheckUtils { continue; } final int start = answerPlaceholder.getRealStartOffset(document); - final int end = start + answerPlaceholder.getLength(); + final int end = start + answerPlaceholder.getRealLength(); final String text = answerPlaceholder.getPossibleAnswer(); document.replaceString(start, end, text); } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudySmartChecker.java b/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudySmartChecker.java index 7cd660635d84..14870203abac 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudySmartChecker.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudySmartChecker.java @@ -54,10 +54,10 @@ public class StudySmartChecker { EduDocumentListener listener = new EduDocumentListener(windowTaskFile); windowDocument.addDocumentListener(listener); int start = placeholder.getRealStartOffset(windowDocument); - int end = start + placeholder.getLength(); + int end = start + placeholder.getRealLength(); final AnswerPlaceholder userAnswerPlaceholder = usersTaskFile.getAnswerPlaceholders().get(placeholder.getIndex()); int userStart = userAnswerPlaceholder.getRealStartOffset(usersDocument); - int userEnd = userStart + userAnswerPlaceholder.getLength(); + int userEnd = userStart + userAnswerPlaceholder.getRealLength(); String text = usersDocument.getText(new TextRange(userStart, userEnd)); windowDocument.replaceString(start, end, text); ApplicationManager.getApplication().runWriteAction(() -> { diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduAnswerPlaceholderPainter.java b/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduAnswerPlaceholderPainter.java index 528b58e26102..81fcf85fb989 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduAnswerPlaceholderPainter.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduAnswerPlaceholderPainter.java @@ -26,9 +26,9 @@ public class EduAnswerPlaceholderPainter { } public static void drawAnswerPlaceholder(@NotNull final Editor editor, @NotNull final AnswerPlaceholder placeholder, - boolean useLength, @NotNull final JBColor color) { + @NotNull final JBColor color) { final Document document = editor.getDocument(); - if (useLength && !placeholder.isValid(document)) { + if (!placeholder.isValid(document)) { return; } EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme(); @@ -37,10 +37,8 @@ public class EduAnswerPlaceholderPainter { final Project project = editor.getProject(); assert project != null; final int startOffset = placeholder.getRealStartOffset(document); - final int length = placeholder.getLength(); - final int replacementLength = placeholder.getPossibleAnswerLength(); - int highlighterLength = useLength ? length : replacementLength; - final int endOffset = startOffset + highlighterLength; + final int length = placeholder.getRealLength(); + final int endOffset = startOffset + length; textAttributes.setEffectColor(color); RangeHighlighter highlighter = editor.getMarkupModel().addRangeHighlighter(startOffset, endOffset, PLACEHOLDERS_LAYER, @@ -56,20 +54,20 @@ public class EduAnswerPlaceholderPainter { } - public static void createGuardedBlocks(@NotNull final Editor editor, TaskFile taskFile, boolean useLength) { + public static void createGuardedBlocks(@NotNull final Editor editor, TaskFile taskFile) { for (AnswerPlaceholder answerPlaceholder : taskFile.getAnswerPlaceholders()) { - createGuardedBlocks(editor, answerPlaceholder, useLength); + createGuardedBlocks(editor, answerPlaceholder); } } - public static void createGuardedBlocks(@NotNull final Editor editor, AnswerPlaceholder placeholder, boolean useLength) { + public static void createGuardedBlocks(@NotNull final Editor editor, AnswerPlaceholder placeholder) { Document document = editor.getDocument(); if (document instanceof DocumentImpl) { DocumentImpl documentImpl = (DocumentImpl)document; List blocks = documentImpl.getGuardedBlocks(); - if (useLength && !placeholder.isValid(document)) return; + if (!placeholder.isValid(document)) return; int start = placeholder.getRealStartOffset(document); - final int length = useLength ? placeholder.getLength() : placeholder.getPossibleAnswerLength(); + final int length = placeholder.getRealLength(); int end = start + length; if (start != 0) { createGuardedBlock(editor, blocks, start - 1, start); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduDocumentListener.java b/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduDocumentListener.java index 77e02bb15154..8e43bf57b26b 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduDocumentListener.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduDocumentListener.java @@ -18,29 +18,19 @@ import java.util.List; public class EduDocumentListener extends DocumentAdapter { private final TaskFile myTaskFile; private final boolean myTrackLength; - private final boolean usePossibleAnswerLength; private final List myAnswerPlaceholders = new ArrayList(); public EduDocumentListener(TaskFile taskFile) { myTaskFile = taskFile; myTrackLength = true; - usePossibleAnswerLength = false; } public EduDocumentListener(TaskFile taskFile, boolean trackLength) { myTaskFile = taskFile; myTrackLength = trackLength; - usePossibleAnswerLength = false; } - public EduDocumentListener(TaskFile taskFile, boolean trackLength, boolean usePossibleAnswerLength) { - myTaskFile = taskFile; - myTrackLength = trackLength; - this.usePossibleAnswerLength = usePossibleAnswerLength; - } - - //remembering old end before document change because of problems // with fragments containing "\n" @Override @@ -53,7 +43,7 @@ public class EduDocumentListener extends DocumentAdapter { myAnswerPlaceholders.clear(); for (AnswerPlaceholder answerPlaceholder : myTaskFile.getAnswerPlaceholders()) { int twStart = answerPlaceholder.getRealStartOffset(document); - int length = usePossibleAnswerLength ? answerPlaceholder.getPossibleAnswerLength() : answerPlaceholder.getLength(); + int length = answerPlaceholder.getRealLength(); int twEnd = twStart + length; myAnswerPlaceholders.add(new AnswerPlaceholderWrapper(answerPlaceholder, twStart, twEnd)); } @@ -85,7 +75,7 @@ public class EduDocumentListener extends DocumentAdapter { int length = twEnd - twStart; answerPlaceholder.setLine(line); answerPlaceholder.setStart(start); - if (usePossibleAnswerLength) { + if (!answerPlaceholder.getUseLength()) { answerPlaceholder.setPossibleAnswer(document.getText(TextRange.create(twStart, twStart + length))); } else if (myTrackLength) { diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduNames.java b/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduNames.java index 03c06d19b30d..a6062cb25339 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduNames.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduNames.java @@ -32,14 +32,19 @@ public class EduNames { public static final String USER_TEST_OUTPUT = "output"; public static final String WINDOW_POSTFIX = "_window."; public static final String WINDOWS_POSTFIX = "_windows"; + public static final String ANSWERS_POSTFIX = "_answers"; public static final String USER_TESTS = "userTests"; public static final String TESTS_FILE = "tests.py"; public static final String TEST_HELPER = "test_helper.py"; public static final String SANDBOX_DIR = "Sandbox"; public static final String COURSE_META_FILE = "course.json"; + public static final String STUDY = "Study"; public static String PYCHARM_ADDITIONAL = "PyCharm additional materials"; + public static final String PYCHARM = "PyCharm"; + public static String PLACEHOLDER = "Answer Placeholder"; + public static String SRC = "src"; private EduNames() { } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduUtils.java b/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduUtils.java index 4b4870673a23..b095bc94815e 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduUtils.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduUtils.java @@ -95,7 +95,7 @@ public class EduUtils { fileWindows = taskDir.createChildData(taskFile, name); printWriter = new PrintWriter(new FileOutputStream(fileWindows.getPath())); for (AnswerPlaceholder answerPlaceholder : taskFile.getAnswerPlaceholders()) { - int length = useLength ? answerPlaceholder.getLength() : answerPlaceholder.getPossibleAnswerLength(); + int length = answerPlaceholder.getRealLength(); if (!answerPlaceholder.isValid(document, length)) { printWriter.println("#educational_plugin_window = "); continue; @@ -192,6 +192,13 @@ public class EduUtils { }); } }, "Create Student File", "Create Student File"); + createStudentDocument(project, taskFile, file, document); + } + + public static void createStudentDocument(@NotNull Project project, + @NotNull TaskFile taskFile, + VirtualFile file, + final Document document) { EduDocumentListener listener = new EduDocumentListener(taskFile, false); document.addDocumentListener(listener); taskFile.sortAnswerPlaceholders(); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/AnswerPlaceholder.java b/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/AnswerPlaceholder.java index e086091d6d8c..72f2b422b7f8 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/AnswerPlaceholder.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/AnswerPlaceholder.java @@ -25,6 +25,7 @@ public class AnswerPlaceholder { private MyInitialState myInitialState; private StudyStatus myStatus = StudyStatus.Uninitialized; private boolean mySelected = false; + private boolean myUseLength = true; @Transient private TaskFile myTaskFile; @@ -45,6 +46,9 @@ public class AnswerPlaceholder { myIndex = index; } + /** + * in actions {@link AnswerPlaceholder#getRealLength()} should be used + */ public int getLength() { return length; } @@ -138,6 +142,9 @@ public class AnswerPlaceholder { line = myInitialState.myLine; start = myInitialState.myStart; length = myInitialState.myLength; + if (!getUseLength()) { + possibleAnswer = myTaskText; + } } public StudyStatus getStatus() { @@ -156,6 +163,29 @@ public class AnswerPlaceholder { mySelected = selected; } + public void init() { + setInitialState(new MyInitialState(line, myTaskText.length(), start)); + } + + public boolean getUseLength() { + return myUseLength; + } + + /** + * @return length or possible answer length + */ + public int getRealLength() { + return myUseLength ? getLength() : getPossibleAnswerLength(); + } + + public void setUseLength(boolean useLength) { + myUseLength = useLength; + } + + public void setInitialState(AnswerPlaceholder placeholder) { + setInitialState(new MyInitialState(placeholder.line, placeholder.length, placeholder.start)); + } + public static class MyInitialState { public int myLine = -1; public int myLength = -1; diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Course.java b/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Course.java index 5dfbebf0f609..e643d9470cc9 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Course.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Course.java @@ -24,9 +24,14 @@ public class Course { private boolean myUpToDate; @Expose @SerializedName("language") - private String myLanguage="Python"; + private String myLanguage = "Python"; - private String courseType="PyCharm"; + //this field is used to distinguish ordinary and CheckIO projects + //"PyCharm" is used here for historical reasons + private String courseType = EduNames.PYCHARM; + + //this field is used to distinguish study and course creator modes + private String courseMode = EduNames.STUDY; /** * Initializes state of course @@ -145,4 +150,12 @@ public class Course { public void setCourseType(String courseType) { this.courseType = courseType; } + + public String getCourseMode() { + return courseMode; + } + + public void setCourseMode(String courseMode) { + this.courseMode = courseMode; + } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/TaskFile.java b/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/TaskFile.java index a235d9eb3a54..33e00d574325 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/TaskFile.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/TaskFile.java @@ -95,7 +95,7 @@ public class TaskFile { for (AnswerPlaceholder placeholder : myAnswerPlaceholders) { if (placeholder.getLine() <= line) { int realStartOffset = placeholder.getRealStartOffset(document); - int placeholderLength = useAnswerLength ? placeholder.getPossibleAnswerLength() : placeholder.getLength(); + int placeholderLength = placeholder.getRealLength(); final int length = placeholderLength > 0 ? placeholderLength : 0; int endOffset = realStartOffset + length; if (realStartOffset <= offset && offset <= endOffset) { diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/editor/StudyEditorFactoryListener.java b/python/educational-core/student/src/com/jetbrains/edu/learning/editor/StudyEditorFactoryListener.java index 4678c7e0ba3e..8a9c17d0ef24 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/editor/StudyEditorFactoryListener.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/editor/StudyEditorFactoryListener.java @@ -14,10 +14,13 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.problems.WolfTheProblemSolver; -import com.jetbrains.edu.learning.core.EduDocumentListener; -import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder; -import com.jetbrains.edu.learning.courseFormat.TaskFile; +import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.StudyUtils; +import com.jetbrains.edu.learning.core.EduDocumentListener; +import com.jetbrains.edu.learning.core.EduNames; +import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder; +import com.jetbrains.edu.learning.courseFormat.Course; +import com.jetbrains.edu.learning.courseFormat.TaskFile; import com.jetbrains.edu.learning.navigation.StudyNavigator; import com.jetbrains.edu.learning.ui.StudyToolWindowFactory; import org.jetbrains.annotations.NotNull; @@ -44,7 +47,7 @@ public class StudyEditorFactoryListener implements EditorFactoryListener { return; } int startOffset = answerPlaceholder.getRealStartOffset(editor.getDocument()); - editor.getSelectionModel().setSelection(startOffset, startOffset + answerPlaceholder.getLength()); + editor.getSelectionModel().setSelection(startOffset, startOffset + answerPlaceholder.getRealLength()); answerPlaceholder.setSelected(true); } } @@ -57,6 +60,7 @@ public class StudyEditorFactoryListener implements EditorFactoryListener { return; } + final Document document = editor.getDocument(); final VirtualFile openedFile = FileDocumentManager.getInstance().getFile(document); if (openedFile != null) { @@ -68,11 +72,20 @@ public class StudyEditorFactoryListener implements EditorFactoryListener { StudyUtils.updateToolWindows(project); studyToolWindow.show(null); } + Course course = StudyTaskManager.getInstance(project).getCourse(); + if (course == null) { + return; + } + + StudyEditor.addDocumentListener(document, new EduDocumentListener(taskFile, true)); + if (!taskFile.getAnswerPlaceholders().isEmpty()) { StudyNavigator.navigateToFirstAnswerPlaceholder(editor, taskFile); - StudyEditor.addDocumentListener(document, new EduDocumentListener(taskFile)); + boolean isStudyProject = EduNames.STUDY.equals(course.getCourseType()); StudyUtils.drawAllWindows(editor, taskFile); - editor.addEditorMouseListener(new WindowSelectionListener(taskFile)); + if (isStudyProject) { + editor.addEditorMouseListener(new WindowSelectionListener(taskFile)); + } } } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/projectView/StudyTreeStructureProvider.java b/python/educational-core/student/src/com/jetbrains/edu/learning/projectView/StudyTreeStructureProvider.java index 6fd47eaece84..0a3a3e841feb 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/projectView/StudyTreeStructureProvider.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/projectView/StudyTreeStructureProvider.java @@ -9,6 +9,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; import com.jetbrains.edu.learning.core.EduNames; +import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.TaskFile; import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.StudyUtils; @@ -24,7 +25,7 @@ public class StudyTreeStructureProvider implements TreeStructureProvider, DumbAw public Collection modify(@NotNull AbstractTreeNode parent, @NotNull Collection children, ViewSettings settings) { - if (!isCourseBasedProject(parent)) { + if (!needModify(parent)) { return children; } Collection nodes = new ArrayList<>(); @@ -78,21 +79,23 @@ public class StudyTreeStructureProvider implements TreeStructureProvider, DumbAw if (!StudyTaskManager.getInstance(project).isInvisibleFile(virtualFile.getPath())) { String fileName = virtualFile.getName(); if (!fileName.contains(EduNames.WINDOW_POSTFIX) && !fileName.contains(EduNames.WINDOWS_POSTFIX) - && !StudyUtils.isTestsFile(project, fileName) && !EduNames.TASK_HTML.equals(fileName) && !fileName.contains(".answer")) { + && !StudyUtils.isTestsFile(project, fileName) && !EduNames.TASK_HTML.equals(fileName)) { nodes.add(node); } } } - protected boolean isCourseBasedProject(@NotNull final AbstractTreeNode parent) { + protected boolean needModify(@NotNull final AbstractTreeNode parent) { final Project project = parent.getProject(); - if (project != null) { - final StudyTaskManager studyTaskManager = StudyTaskManager.getInstance(project); - if (studyTaskManager.getCourse() == null) { - return false; - } + if (project == null) { + return false; } - return true; + final StudyTaskManager studyTaskManager = StudyTaskManager.getInstance(project); + Course course = studyTaskManager.getCourse(); + if (course == null) { + return false; + } + return EduNames.STUDY.equals(course.getCourseMode()); } @Nullable diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyJavaFxToolWindow.java b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyJavaFxToolWindow.java index 98bec3d50589..058bd633a29b 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyJavaFxToolWindow.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyJavaFxToolWindow.java @@ -29,10 +29,9 @@ public class StudyJavaFxToolWindow extends StudyToolWindow { } @Override - public JComponent createTaskInfoPanel(String taskText, Project project) { + public JComponent createTaskInfoPanel(Project project) { myBrowserWindow = new StudyBrowserWindow(true, false); myBrowserWindow.addBackAndOpenButtons(); - myBrowserWindow.loadContent(taskText, StudyUtils.getConfigurator(project)); JPanel panel = new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS)); panel.add(myBrowserWindow.getPanel()); @@ -40,7 +39,7 @@ public class StudyJavaFxToolWindow extends StudyToolWindow { } @Override - public void setTaskText(String text) { + public void setText(String text) { StudyPluginConfigurator configurator = StudyUtils.getConfigurator(ProjectUtil.guessCurrentProject(this)); myBrowserWindow.loadContent(text, configurator); } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudySwingToolWindow.java b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudySwingToolWindow.java index ef66218d3398..ebaff662ecb8 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudySwingToolWindow.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudySwingToolWindow.java @@ -37,7 +37,7 @@ public class StudySwingToolWindow extends StudyToolWindow { } @Override - public JComponent createTaskInfoPanel(String taskText, Project project) { + public JComponent createTaskInfoPanel(Project project) { myTaskTextPane = new JTextPane(); final JBScrollPane scrollPane = new JBScrollPane(myTaskTextPane); myTaskTextPane.setContentType(new HTMLEditorKit().getContentType()); @@ -57,12 +57,11 @@ public class StudySwingToolWindow extends StudyToolWindow { myTaskTextPane.setBackground(EditorColorsManager.getInstance().getGlobalScheme().getDefaultBackground()); } myTaskTextPane.setBorder(new EmptyBorder(20, 20, 0, 10)); - myTaskTextPane.setText(taskText); myTaskTextPane.addHyperlinkListener(BrowserHyperlinkListener.INSTANCE); return scrollPane; } - public void setTaskText(String text) { + public void setText(String text) { myTaskTextPane.setText(text); } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyToolWindow.java b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyToolWindow.java index 7d24d27e6783..feb06b72159a 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyToolWindow.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyToolWindow.java @@ -15,23 +15,27 @@ */ package com.jetbrains.edu.learning.ui; +import com.intellij.ide.browsers.WebBrowserManager; import com.intellij.openapi.Disposable; -import com.intellij.openapi.actionSystem.ActionManager; -import com.intellij.openapi.actionSystem.ActionToolbar; -import com.intellij.openapi.actionSystem.DataProvider; -import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.EditorFactory; +import com.intellij.openapi.editor.ex.EditorEx; +import com.intellij.openapi.extensions.Extensions; +import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileEditor.FileEditorManagerListener; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.SimpleToolWindowPanel; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.JBCardLayout; import com.intellij.ui.OnePixelSplitter; import com.intellij.util.ui.JBUI; -import com.jetbrains.edu.learning.StudyBasePluginConfigurator; -import com.jetbrains.edu.learning.StudyPluginConfigurator; -import com.jetbrains.edu.learning.StudyTaskManager; -import com.jetbrains.edu.learning.StudyUtils; +import com.jetbrains.edu.learning.*; +import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.Course; +import com.jetbrains.edu.learning.courseFormat.TaskFile; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -41,6 +45,7 @@ import java.util.Map; public abstract class StudyToolWindow extends SimpleToolWindowPanel implements DataProvider, Disposable { private static final Logger LOG = Logger.getInstance(StudyToolWindow.class); private static final String TASK_INFO_ID = "taskInfo"; + private static final String EMPTY_TASK_TEXT = "Please, open any task to see task description"; private final JBCardLayout myCardLayout; private final JPanel myContentPanel; private final OnePixelSplitter mySplitPane; @@ -49,17 +54,17 @@ public abstract class StudyToolWindow extends SimpleToolWindowPanel implements D super(true, true); myCardLayout = new JBCardLayout(); myContentPanel = new JPanel(myCardLayout); - mySplitPane = new OnePixelSplitter(myVertical=true); + mySplitPane = new OnePixelSplitter(myVertical = true); } public void init(Project project) { String taskText = StudyUtils.getTaskText(project); if (taskText == null) return; - JPanel toolbarPanel = createToolbarPanel(project); + JPanel toolbarPanel = createToolbarPanel(getActionGroup(project)); setToolbar(toolbarPanel); - myContentPanel.add(TASK_INFO_ID, createTaskInfoPanel(taskText, project)); + myContentPanel.add(TASK_INFO_ID, createTaskInfoPanel(project)); mySplitPane.setFirstComponent(myContentPanel); addAdditionalPanels(project); myCardLayout.show(myContentPanel, TASK_INFO_ID); @@ -71,6 +76,17 @@ public abstract class StudyToolWindow extends SimpleToolWindowPanel implements D final FileEditorManagerListener listener = configurator.getFileEditorManagerListener(project, this); project.getMessageBus().connect().subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, listener); } + + if (StudyTaskManager.getInstance(project).isTurnEditingMode() || StudyTaskManager.getInstance(project).getToolWindowMode() == StudyToolWindowMode.EDITING) { + TaskFile file = StudyUtils.getSelectedTaskFile(project); + if (file != null) { + VirtualFile taskDir = file.getTask().getTaskDir(project); + setTaskText(taskText, taskDir, project); + + } + } else { + setTaskText(taskText, null, project); + } } private void addAdditionalPanels(Project project) { @@ -85,7 +101,7 @@ public abstract class StudyToolWindow extends SimpleToolWindowPanel implements D public void dispose() { } - + //used in checkiO plugin. @SuppressWarnings("unused") public void showPanelById(@NotNull final String panelId) { @@ -117,24 +133,25 @@ public abstract class StudyToolWindow extends SimpleToolWindowPanel implements D } - public abstract JComponent createTaskInfoPanel(String taskText, Project project); - - private static JPanel createToolbarPanel(@NotNull final Project project) { - final DefaultActionGroup group = getActionGroup(project); + public abstract JComponent createTaskInfoPanel(Project project); + public static JPanel createToolbarPanel(ActionGroup group) { final ActionToolbar actionToolBar = ActionManager.getInstance().createActionToolbar("Study", group, true); return JBUI.Panels.simplePanel(actionToolBar.getComponent()); } - private static DefaultActionGroup getActionGroup(@NotNull final Project project) { + public static DefaultActionGroup getActionGroup(@NotNull final Project project) { + DefaultActionGroup group = new DefaultActionGroup(); Course course = StudyTaskManager.getInstance(project).getCourse(); if (course == null) { LOG.warn("Course is null"); - return new DefaultActionGroup(); + return group; } StudyPluginConfigurator configurator = StudyUtils.getConfigurator(project); if (configurator != null) { - return configurator.getActionGroup(project); + group.addAll(configurator.getActionGroup(project)); + addAdditionalActions(group); + return group; } else { LOG.warn("No configurator is provided for plugin"); @@ -142,5 +159,68 @@ public abstract class StudyToolWindow extends SimpleToolWindowPanel implements D } } - public abstract void setTaskText(String text) ; + private static void addAdditionalActions(DefaultActionGroup group) { + StudyActionsProvider[] providers = Extensions.getExtensions(StudyActionsProvider.EP_NAME); + for (StudyActionsProvider provider : providers) { + group.addAll(provider.getActions()); + } + } + + public void setTaskText(String text, VirtualFile taskDirectory, Project project) { + if (StudyTaskManager.getInstance(project).isTurnEditingMode()) { + if (taskDirectory == null) { + LOG.info("Failed to enter editing mode for StudyToolWindow"); + return; + } + VirtualFile taskTextFile = taskDirectory.findChild(EduNames.TASK_HTML); + enterEditingMode(taskTextFile, project); + StudyTaskManager.getInstance(project).setTurnEditingMode(false); + } + else { + setText(text); + } + } + + protected abstract void setText(String text); + + public void setEmptyText(@NotNull Project project) { + if (StudyTaskManager.getInstance(project).getToolWindowMode() == StudyToolWindowMode.EDITING) { + mySplitPane.setFirstComponent(myContentPanel); + StudyTaskManager.getInstance(project).setTurnEditingMode(true); + } + setTaskText(EMPTY_TASK_TEXT, null, project); + } + + public enum StudyToolWindowMode { + TEXT, EDITING + } + + + public void enterEditingMode(VirtualFile taskFile, Project project) { + final EditorFactory factory = EditorFactory.getInstance(); + Document document = FileDocumentManager.getInstance().getDocument(taskFile); + if (document == null) { + return; + } + WebBrowserManager.getInstance().setShowBrowserHover(false); + final EditorEx createdEditor = (EditorEx)factory.createEditor(document, project, taskFile, false); + Disposer.register(project, new Disposable() { + public void dispose() { + factory.releaseEditor(createdEditor); + } + }); + JComponent editorComponent = createdEditor.getComponent(); + mySplitPane.setFirstComponent(editorComponent); + mySplitPane.repaint(); + + StudyTaskManager.getInstance(project).setToolWindowMode(StudyToolWindowMode.EDITING); + } + + + public void leaveEditingMode(Project project) { + WebBrowserManager.getInstance().setShowBrowserHover(true); + mySplitPane.setFirstComponent(myContentPanel); + StudyTaskManager.getInstance(project).setToolWindowMode(StudyToolWindowMode.TEXT); + StudyUtils.updateStudyToolWindow(project); + } } diff --git a/python/educational-core/student/student.iml b/python/educational-core/student/student.iml index e16fc06deca8..8702c7849f52 100644 --- a/python/educational-core/student/student.iml +++ b/python/educational-core/student/student.iml @@ -23,5 +23,6 @@ + \ No newline at end of file diff --git a/python/educational-python/course-creator-python/resources/META-INF/plugin.xml b/python/educational-python/course-creator-python/resources/META-INF/plugin.xml index e87961c59b40..2399560d07b7 100644 --- a/python/educational-python/course-creator-python/resources/META-INF/plugin.xml +++ b/python/educational-python/course-creator-python/resources/META-INF/plugin.xml @@ -21,27 +21,16 @@ + + - - - - - - - - - - - - - \ No newline at end of file diff --git a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCLanguageManager.java b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCLanguageManager.java index f66ecff6ddd3..8628bbdecc41 100644 --- a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCLanguageManager.java +++ b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCLanguageManager.java @@ -3,6 +3,7 @@ package com.jetbrains.edu.coursecreator; import com.intellij.ide.fileTemplates.FileTemplate; import com.intellij.ide.fileTemplates.FileTemplateManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; import com.jetbrains.edu.learning.core.EduNames; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -40,4 +41,9 @@ public class PyCCLanguageManager implements CCLanguageManager { private static FileTemplate getInternalTemplateByName(@NotNull final Project project, String name) { return FileTemplateManager.getInstance(project).getInternalTemplate(name); } + + @Override + public boolean isTestFile(VirtualFile file) { + return EduNames.TESTS_FILE.equals(file.getName()); + } } diff --git a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCProjectGenerator.java b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCProjectGenerator.java index ebffadfea5d2..4af7914d2da4 100644 --- a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCProjectGenerator.java +++ b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCProjectGenerator.java @@ -6,19 +6,25 @@ import com.intellij.facet.ui.ValidationResult; import com.intellij.ide.fileTemplates.FileTemplate; import com.intellij.ide.fileTemplates.FileTemplateManager; import com.intellij.ide.fileTemplates.FileTemplateUtil; +import com.intellij.openapi.application.PathManager; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.platform.DirectoryProjectGenerator; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiManager; -import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.coursecreator.actions.CCCreateLesson; import com.jetbrains.edu.coursecreator.actions.CCCreateTask; import com.jetbrains.edu.coursecreator.ui.CCNewProjectPanel; +import com.jetbrains.edu.learning.StudyProjectComponent; +import com.jetbrains.edu.learning.StudyTaskManager; +import com.jetbrains.edu.learning.core.EduNames; +import com.jetbrains.edu.learning.courseFormat.Course; +import com.jetbrains.python.PythonLanguage; import com.jetbrains.python.newProject.PythonProjectGenerator; import icons.CourseCreatorPythonIcons; import org.jetbrains.annotations.Nls; @@ -26,6 +32,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import java.io.File; public class PyCCProjectGenerator extends PythonProjectGenerator implements DirectoryProjectGenerator { @@ -55,25 +62,16 @@ public class PyCCProjectGenerator extends PythonProjectGenerator implements Dire public static void generateProject(@NotNull final Project project, @NotNull final VirtualFile baseDir, @NotNull final String name, @NotNull final String[] authors, @NotNull final String description) { - final CCProjectService service = CCProjectService.getInstance(project); - final Course course = new Course(); - course.setName(name); - course.setAuthors(authors); - course.setDescription(description); - course.setLanguage("Python"); - service.setCourse(course); + final Course course = getCourse(project, name, authors, description); final PsiDirectory projectDir = PsiManager.getInstance(project).findDirectory(baseDir); if (projectDir == null) return; new WriteCommandAction.Simple(project) { @Override protected void run() throws Throwable { - final FileTemplate template = FileTemplateManager.getInstance(project).getInternalTemplate("test_helper"); - try { - FileTemplateUtil.createFromTemplate(template, "test_helper.py", null, projectDir); - } - catch (Exception ignored) { - } + + createTestHelper(project, projectDir); + PsiDirectory lessonDir = new CCCreateLesson().createItem(null, project, projectDir, course); if (lessonDir == null) { LOG.error("Failed to create lesson"); @@ -84,6 +82,33 @@ public class PyCCProjectGenerator extends PythonProjectGenerator implements Dire }.execute(); } + private static void createTestHelper(@NotNull Project project, PsiDirectory projectDir) { + final FileTemplate template = FileTemplateManager.getInstance(project).getInternalTemplate(FileUtil.getNameWithoutExtension(EduNames.TEST_HELPER)); + try { + FileTemplateUtil.createFromTemplate(template, EduNames.TEST_HELPER, null, projectDir); + } + catch (Exception ignored) { + } + } + + @NotNull + private static Course getCourse(@NotNull Project project, @NotNull String name, @NotNull String[] authors, @NotNull String description) { + final Course course = new Course(); + course.setName(name); + course.setAuthors(authors); + course.setDescription(description); + course.setLanguage(PythonLanguage.getInstance().getID()); + course.setCourseMode(CCUtils.COURSE_MODE); + + File coursesDir = new File(PathManager.getConfigPath(), "courses"); + File courseDir = new File(coursesDir, name + "-" + project.getName()); + course.setCourseDirectory(courseDir.getPath()); + + StudyTaskManager.getInstance(project).setCourse(course); + StudyProjectComponent.getInstance(project).registerStudyToolWindow(course); + return course; + } + @NotNull @Override public ValidationResult validate(@NotNull String s) { diff --git a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCReferenceResolveProvider.java b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCReferenceResolveProvider.java index 95cf9321df3b..e1bb8ee267d6 100644 --- a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCReferenceResolveProvider.java +++ b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCReferenceResolveProvider.java @@ -1,5 +1,6 @@ package com.jetbrains.edu.coursecreator; +import com.intellij.openapi.project.Project; import com.jetbrains.python.edu.PyEduUtils; import com.jetbrains.python.psi.PyQualifiedExpression; import com.jetbrains.python.psi.resolve.PyReferenceResolveProvider; @@ -13,7 +14,8 @@ public class PyCCReferenceResolveProvider implements PyReferenceResolveProvider @NotNull @Override public List resolveName(@NotNull final PyQualifiedExpression element) { - if (CCProjectService.getInstance(element.getProject()).getCourse() == null) { + Project project = element.getProject(); + if (!CCUtils.isCourseCreator(project)) { return Collections.emptyList(); } return PyEduUtils.getResolveResultFromContainingDirectory(element); diff --git a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCRunTests.java b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCRunTests.java deleted file mode 100644 index 5af7f8b164e8..000000000000 --- a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCRunTests.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.jetbrains.edu.coursecreator; - -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.project.Project; -import com.jetbrains.edu.coursecreator.actions.PyCCRunTestsAction; -import com.jetbrains.python.edu.PyExecuteFileExtensionPoint; -import org.jetbrains.annotations.NotNull; - -public class PyCCRunTests implements PyExecuteFileExtensionPoint { - - @NotNull - public AnAction getRunAction() { - return new PyCCRunTestsAction(); - } - - @Override - public boolean accept(Project project) { - return CCProjectService.getInstance(project).getCourse() != null; - } -} \ No newline at end of file diff --git a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/actions/PyCCRunTestsAction.java b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/actions/PyCCRunTestsAction.java deleted file mode 100644 index ebda0f8976f1..000000000000 --- a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/actions/PyCCRunTestsAction.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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.jetbrains.edu.coursecreator.actions; - -import com.intellij.execution.Executor; -import com.intellij.execution.ProgramRunnerUtil; -import com.intellij.execution.RunManager; -import com.intellij.execution.RunnerAndConfigurationSettings; -import com.intellij.execution.configurations.ConfigurationFactory; -import com.intellij.execution.executors.DefaultRunExecutor; -import com.intellij.ide.projectView.ProjectView; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileManager; -import com.intellij.util.PathUtil; -import com.jetbrains.python.run.PythonConfigurationType; -import com.jetbrains.python.run.PythonRunConfiguration; -import org.jetbrains.annotations.NotNull; - -public class PyCCRunTestsAction extends CCRunTestsAction { - - @Override - protected void executeTests(@NotNull final Project project, - @NotNull final VirtualFile virtualFile, - @NotNull final VirtualFile taskDir, - @NotNull final VirtualFile testFile) { - final ConfigurationFactory factory = PythonConfigurationType.getInstance().getConfigurationFactories()[0]; - final RunnerAndConfigurationSettings settings = - RunManager.getInstance(project).createRunConfiguration("test", factory); - - final PythonRunConfiguration configuration = (PythonRunConfiguration)settings.getConfiguration(); - configuration.setScriptName(testFile.getPath()); - configuration.setWorkingDirectory(taskDir.getPath()); - String taskFileName = virtualFile.getName(); - VirtualFile userFile = taskDir.findChild(taskFileName); - if (userFile == null) { - return; - } - VirtualFile ideaDir = project.getBaseDir().findChild(".idea"); - if (ideaDir == null) { - return; - } - VirtualFileManager.getInstance().refreshWithoutFileWatcher(true); - ProjectView.getInstance(project).refresh(); - configuration.setScriptParameters(PathUtil.toSystemDependentName(project.getBasePath()) + " " + PathUtil.toSystemDependentName(userFile.getPath())); - Executor executor = DefaultRunExecutor.getRunExecutorInstance(); - ProgramRunnerUtil.executeConfiguration(project, settings, executor); - } -} diff --git a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCCommandLineState.java b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCCommandLineState.java new file mode 100644 index 000000000000..0bde8098b28f --- /dev/null +++ b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCCommandLineState.java @@ -0,0 +1,89 @@ +package com.jetbrains.edu.coursecreator.run; + +import com.intellij.execution.ExecutionException; +import com.intellij.execution.ExecutionResult; +import com.intellij.execution.Executor; +import com.intellij.execution.configurations.GeneralCommandLine; +import com.intellij.execution.configurations.ParamsGroup; +import com.intellij.execution.process.ProcessAdapter; +import com.intellij.execution.process.ProcessEvent; +import com.intellij.execution.process.ProcessHandler; +import com.intellij.execution.runners.ExecutionEnvironment; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.jetbrains.edu.coursecreator.CCUtils; +import com.jetbrains.edu.learning.StudyTaskManager; +import com.jetbrains.edu.learning.StudyUtils; +import com.jetbrains.edu.learning.checker.StudyCheckUtils; +import com.jetbrains.edu.learning.core.EduNames; +import com.jetbrains.edu.learning.core.EduUtils; +import com.jetbrains.edu.learning.courseFormat.Course; +import com.jetbrains.edu.learning.courseFormat.Task; +import com.jetbrains.python.run.CommandLinePatcher; +import com.jetbrains.python.run.PythonCommandLineState; +import org.jetbrains.annotations.NotNull; + +public class PyCCCommandLineState extends PythonCommandLineState { + private final PyCCRunTestConfiguration myRunConfiguration; + private final VirtualFile myTaskDir; + private final Task myTask; + + public PyCCCommandLineState(PyCCRunTestConfiguration runConfiguration, + ExecutionEnvironment env) { + super(runConfiguration, env); + myRunConfiguration = runConfiguration; + + VirtualFile testsFile = LocalFileSystem.getInstance().findFileByPath(myRunConfiguration.getPathToTest()); + assert testsFile != null; + myTaskDir = StudyUtils.getTaskDir(testsFile); + assert myTaskDir != null; + myTask = StudyUtils.getTask(myRunConfiguration.getProject(), myTaskDir); + assert myTask != null; + } + + @Override + protected void buildCommandLineParameters(GeneralCommandLine commandLine) { + ParamsGroup group = commandLine.getParametersList().getParamsGroup(GROUP_SCRIPT); + assert group != null; + + Project project = myRunConfiguration.getProject(); + Course course = StudyTaskManager.getInstance(project).getCourse(); + assert course != null; + + group.addParameter(myRunConfiguration.getPathToTest()); + group.addParameter(course.getCourseDirectory()); + + group.addParameter(getFirstTaskFilePath()); + } + + @NotNull + private String getFirstTaskFilePath() { + String firstTaskFileName = StudyUtils.getFirst(myTask.getTaskFiles().keySet()); + return myTaskDir.findChild(EduNames.SRC) != null ? + FileUtil.join(myTaskDir.getPath(), EduNames.SRC, firstTaskFileName) : + FileUtil.join(myTaskDir.getPath(), firstTaskFileName); + } + + @Override + public ExecutionResult execute(Executor executor, CommandLinePatcher... patchers) throws ExecutionException { + CCUtils.createResources(myRunConfiguration.getProject(), myTask, myTaskDir); + ApplicationManager.getApplication().runWriteAction(() -> StudyCheckUtils.flushWindows(myTask, myTaskDir)); + + return super.execute(executor, patchers); + } + + @Override + protected ProcessHandler doCreateProcess(GeneralCommandLine commandLine) throws ExecutionException { + ProcessHandler handler = super.doCreateProcess(commandLine); + handler.addProcessListener(new ProcessAdapter() { + @Override + public void processTerminated(ProcessEvent event) { + ApplicationManager.getApplication().invokeLater(() -> EduUtils.deleteWindowDescriptions(myTask, myTaskDir)); + } + }); + return handler; + } +} diff --git a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCRunTestConfiguration.java b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCRunTestConfiguration.java new file mode 100644 index 000000000000..aca074e73274 --- /dev/null +++ b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCRunTestConfiguration.java @@ -0,0 +1,80 @@ +package com.jetbrains.edu.coursecreator.run; + +import com.intellij.execution.ExecutionException; +import com.intellij.execution.Executor; +import com.intellij.execution.configurations.ConfigurationFactory; +import com.intellij.execution.configurations.RunProfileState; +import com.intellij.execution.configurations.RuntimeConfigurationException; +import com.intellij.execution.runners.ExecutionEnvironment; +import com.intellij.openapi.options.SettingsEditor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.InvalidDataException; +import com.intellij.openapi.util.JDOMExternalizerUtil; +import com.intellij.openapi.util.WriteExternalException; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.jetbrains.edu.learning.StudyUtils; +import com.jetbrains.python.run.AbstractPythonRunConfiguration; +import org.jdom.Element; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +class PyCCRunTestConfiguration extends AbstractPythonRunConfiguration { + + public static final String PATH_ATTR = "studyTest"; + private Project myProject; + private String myPathToTest; + + public PyCCRunTestConfiguration(Project project, ConfigurationFactory factory) { + super(project, factory); + myProject = project; + } + + @Nullable + @Override + public RunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment environment) throws ExecutionException { + return new PyCCCommandLineState(this, environment); + } + + @Override + protected SettingsEditor createConfigurationEditor() { + return new PyCCSettingsEditor(myProject); + } + + public String getPathToTest() { + return myPathToTest; + } + + public void setPathToTest(String pathToTest) { + myPathToTest = pathToTest; + } + + @Override + public void readExternal(Element element) throws InvalidDataException { + super.readExternal(element); + myPathToTest = JDOMExternalizerUtil.readField(element, PATH_ATTR); + } + + @Override + public void writeExternal(Element element) throws WriteExternalException { + super.writeExternal(element); + JDOMExternalizerUtil.writeField(element, PATH_ATTR, myPathToTest); + } + + @Override + public void checkConfiguration() throws RuntimeConfigurationException { + super.checkConfiguration(); + String message = "Select valid path to the file with tests"; + VirtualFile testsFile = LocalFileSystem.getInstance().findFileByPath(myPathToTest); + if (testsFile == null) { + throw new RuntimeConfigurationException(message); + } + VirtualFile taskDir = StudyUtils.getTaskDir(testsFile); + if (taskDir == null) { + throw new RuntimeConfigurationException(message); + } + if (StudyUtils.getTask(myProject, taskDir) == null) { + throw new RuntimeConfigurationException(message); + } + } +} diff --git a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCRunTestsConfigurationFactory.java b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCRunTestsConfigurationFactory.java new file mode 100644 index 000000000000..1d2de895d0a2 --- /dev/null +++ b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCRunTestsConfigurationFactory.java @@ -0,0 +1,19 @@ +package com.jetbrains.edu.coursecreator.run; + +import com.intellij.execution.configurations.ConfigurationFactory; +import com.intellij.execution.configurations.ConfigurationType; +import com.intellij.execution.configurations.RunConfiguration; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; + +public class PyCCRunTestsConfigurationFactory extends ConfigurationFactory { + protected PyCCRunTestsConfigurationFactory(@NotNull ConfigurationType type) { + super(type); + } + + @NotNull + @Override + public RunConfiguration createTemplateConfiguration(@NotNull Project project) { + return new PyCCRunTestConfiguration(project, this); + } +} diff --git a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCRunTestsConfigurationProducer.java b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCRunTestsConfigurationProducer.java new file mode 100644 index 000000000000..53ca0c996cc2 --- /dev/null +++ b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCRunTestsConfigurationProducer.java @@ -0,0 +1,94 @@ +package com.jetbrains.edu.coursecreator.run; + +import com.intellij.execution.Location; +import com.intellij.execution.actions.ConfigurationContext; +import com.intellij.execution.actions.RunConfigurationProducer; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiElement; +import com.jetbrains.edu.coursecreator.CCUtils; +import com.jetbrains.edu.learning.StudyUtils; +import com.jetbrains.edu.learning.core.EduNames; +import com.jetbrains.edu.learning.courseFormat.Task; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class PyCCRunTestsConfigurationProducer extends RunConfigurationProducer { + protected PyCCRunTestsConfigurationProducer() { + super(PyCCRunTestsConfigurationType.getInstance()); + } + + @Override + protected boolean setupConfigurationFromContext(PyCCRunTestConfiguration configuration, + ConfigurationContext context, + Ref sourceElement) { + Project project = context.getProject(); + if (!CCUtils.isCourseCreator(project)) { + return false; + } + + String testsPath = getTestPath(context); + if (testsPath == null) { + return false; + } + VirtualFile testsFile = LocalFileSystem.getInstance().findFileByPath(testsPath); + if (testsFile == null) { + return false; + } + + String generatedName = generateName(testsFile, project); + if (generatedName == null) { + return false; + } + + configuration.setPathToTest(testsPath); + configuration.setName(generatedName); + return true; + } + + @Nullable + private static String generateName(@NotNull VirtualFile testsFile, @NotNull Project project) { + VirtualFile taskDir = StudyUtils.getTaskDir(testsFile); + if (taskDir == null) { + return null; + } + Task task = StudyUtils.getTask(project, taskDir); + if (task == null) { + return null; + } + return task.getLesson().getName() + "/" + task.getName(); + } + + @Nullable + private static String getTestPath(@NotNull ConfigurationContext context) { + Location location = context.getLocation(); + if (location == null) { + return null; + } + VirtualFile file = location.getVirtualFile(); + if (file == null) { + return null; + } + VirtualFile taskDir = StudyUtils.getTaskDir(file); + if (taskDir == null) { + return null; + } + + String testsPath = taskDir.findChild(EduNames.SRC) != null ? + FileUtil.join(taskDir.getPath(), EduNames.SRC, EduNames.TESTS_FILE) : + FileUtil.join(taskDir.getPath(), EduNames.TESTS_FILE); + return file.getPath().equals(testsPath) ? testsPath : null; + } + + @Override + public boolean isConfigurationFromContext(PyCCRunTestConfiguration configuration, ConfigurationContext context) { + String path = getTestPath(context); + if (path == null) { + return false; + } + return path.equals(configuration.getPathToTest()); + } +} diff --git a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCRunTestsConfigurationType.java b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCRunTestsConfigurationType.java new file mode 100644 index 000000000000..ac05cf1d8ca4 --- /dev/null +++ b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCRunTestsConfigurationType.java @@ -0,0 +1,41 @@ +package com.jetbrains.edu.coursecreator.run; + +import com.intellij.execution.configurations.ConfigurationFactory; +import com.intellij.execution.configurations.ConfigurationType; +import com.intellij.execution.configurations.ConfigurationTypeUtil; +import com.intellij.icons.AllIcons; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +public class PyCCRunTestsConfigurationType implements ConfigurationType { + @Override + public String getDisplayName() { + return "Run Study Tests"; + } + + @Override + public String getConfigurationTypeDescription() { + return "Study Test Runner"; + } + + @Override + public Icon getIcon() { + return AllIcons.Actions.Lightning; + } + + @NotNull + @Override + public String getId() { + return "ccruntests"; + } + + @Override + public ConfigurationFactory[] getConfigurationFactories() { + return new ConfigurationFactory[]{new PyCCRunTestsConfigurationFactory(this)}; + } + + public static PyCCRunTestsConfigurationType getInstance() { + return ConfigurationTypeUtil.findConfigurationType(PyCCRunTestsConfigurationType.class); + } +} diff --git a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCSettingEditor.form b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCSettingEditor.form new file mode 100644 index 000000000000..c84b034ed12f --- /dev/null +++ b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCSettingEditor.form @@ -0,0 +1,34 @@ + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCSettingsEditor.java b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCSettingsEditor.java new file mode 100644 index 000000000000..806f6a02ad5e --- /dev/null +++ b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCSettingsEditor.java @@ -0,0 +1,69 @@ +package com.jetbrains.edu.coursecreator.run; + +import com.intellij.openapi.module.Module; +import com.intellij.openapi.options.ConfigurationException; +import com.intellij.openapi.options.SettingsEditor; +import com.intellij.openapi.project.Project; +import com.jetbrains.python.run.AbstractPyCommonOptionsForm; +import com.jetbrains.python.run.AbstractPythonRunConfiguration; +import com.jetbrains.python.run.PyCommonOptionsFormData; +import com.jetbrains.python.run.PyCommonOptionsFormFactory; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.*; +import java.util.List; + +public class PyCCSettingsEditor extends SettingsEditor { + private AbstractPyCommonOptionsForm myForm; + private Project myProject; + private JTextField myPathToTestFileField; + private JPanel myPanel; + + public PyCCSettingsEditor(Project project) { + myProject = project; + } + + @Override + protected void resetEditorFrom(PyCCRunTestConfiguration s) { + AbstractPythonRunConfiguration.copyParams(s, myForm); + myPathToTestFileField.setText(s.getPathToTest()); + } + + @Override + protected void applyEditorTo(PyCCRunTestConfiguration s) throws ConfigurationException { + AbstractPythonRunConfiguration.copyParams(myForm, s); + s.setPathToTest(myPathToTestFileField.getText()); + } + + @NotNull + @Override + protected JComponent createEditor() { + final JPanel mainPanel = new JPanel(new BorderLayout()); + mainPanel.add(myPanel, BorderLayout.NORTH); + myForm = createEnvPanel(); + mainPanel.add(myForm.getMainPanel(), BorderLayout.SOUTH); + return mainPanel; + } + + + @NotNull + private AbstractPyCommonOptionsForm createEnvPanel() { + return PyCommonOptionsFormFactory.getInstance().createForm(new PyCommonOptionsFormData() { + @Override + public Project getProject() { + return myProject; + } + + @Override + public List getValidModules() { + return AbstractPythonRunConfiguration.getValidModules(myProject); + } + + @Override + public boolean showConfigureInterpretersLink() { + return false; + } + }); + } +} diff --git a/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyCheckAction.java b/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyCheckAction.java index 43f5fc5645f4..fdb110718b34 100644 --- a/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyCheckAction.java +++ b/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyCheckAction.java @@ -16,6 +16,7 @@ import com.jetbrains.edu.learning.checker.StudyCheckTask; import com.jetbrains.edu.learning.checker.StudyCheckUtils; import com.jetbrains.edu.learning.checker.StudyTestRunner; import com.jetbrains.edu.learning.checker.StudyTestsOutputParser; +import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.StudyStatus; import com.jetbrains.edu.learning.courseFormat.Task; import com.jetbrains.edu.learning.courseFormat.TaskFile; @@ -103,9 +104,11 @@ public class PyStudyCheckAction extends StudyCheckAction { myTaskManger.setStatus(taskFile, StudyStatus.Failed); continue; } - CommandProcessor.getInstance().runUndoTransparentAction(() -> ApplicationManager.getApplication().runWriteAction(() -> { - StudyCheckUtils.runSmartTestProcess(myTaskDir, testRunner, name, taskFile, project); - })); + if (EduNames.STUDY.equals(myTaskManger.getCourse().getCourseMode())) { + CommandProcessor.getInstance().runUndoTransparentAction(() -> ApplicationManager.getApplication().runWriteAction(() -> { + StudyCheckUtils.runSmartTestProcess(myTaskDir, testRunner, name, taskFile, project); + })); + } } StudyCheckUtils.showTestResultPopUp(testsOutput.getMessage(), MessageType.ERROR.getPopupBackground(), project); StudyCheckUtils.navigateToFailedPlaceholder(myStudyState, myTask, myTaskDir, project); diff --git a/python/openapi/src/com/jetbrains/python/templateLanguages/ConfigureTemplateDirectoriesAction.java b/python/openapi/src/com/jetbrains/python/templateLanguages/ConfigureTemplateDirectoriesAction.java index 064ce6afc139..e813bf0d740c 100644 --- a/python/openapi/src/com/jetbrains/python/templateLanguages/ConfigureTemplateDirectoriesAction.java +++ b/python/openapi/src/com/jetbrains/python/templateLanguages/ConfigureTemplateDirectoriesAction.java @@ -18,8 +18,6 @@ package com.jetbrains.python.templateLanguages; import com.intellij.codeInsight.intention.LowPriorityAction; import com.intellij.codeInspection.LocalQuickFix; import com.intellij.codeInspection.ProblemDescriptor; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.options.ShowSettingsUtil; import com.intellij.openapi.project.Project; import org.jetbrains.annotations.NotNull; @@ -42,11 +40,11 @@ public class ConfigureTemplateDirectoriesAction implements LocalQuickFix, LowPri @Override public void applyFix(@NotNull final Project project, @NotNull ProblemDescriptor descriptor) { - ApplicationManager.getApplication().invokeLater(new Runnable() { - @Override - public void run() { - ShowSettingsUtil.getInstance().showSettingsDialog(project, "Project Structure"); - } - }, ModalityState.NON_MODAL); + ShowSettingsUtil.getInstance().showSettingsDialog(project, "Project Structure"); + } + + @Override + public boolean startInWriteAction() { + return false; } } diff --git a/python/setup-test-environment/build.gradle b/python/setup-test-environment/build.gradle index c4e4a9639afc..c32115b7c154 100644 --- a/python/setup-test-environment/build.gradle +++ b/python/setup-test-environment/build.gradle @@ -7,7 +7,7 @@ plugins { envs { bootstrapDirectory = new File(System.getenv().getOrDefault("PYCHARM_PYTHONS", new File(buildDir, 'pythons').path)) - envsDirectory = new File(System.getenv().getOrDefault("PYCHARM_PYTHON_ENVS", new File(buildDir, 'envs').path)) + envsDirectory = new File(System.getenv().getOrDefault("PYCHARM_PYTHON_VIRTUAL_ENVS", new File(buildDir, 'envs').path)) minicondaVersion = 'latest' packages = ["pip", "setuptools"] _64Bits = true diff --git a/python/src/com/jetbrains/python/BaseReference.java b/python/src/com/jetbrains/python/BaseReference.java new file mode 100644 index 000000000000..e6a385bd94db --- /dev/null +++ b/python/src/com/jetbrains/python/BaseReference.java @@ -0,0 +1,71 @@ +/* + * Copyright 2000-2016 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.jetbrains.python; + +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReference; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Alexei Orischenko + * Date: Dec 4, 2009 + */ +public abstract class BaseReference implements PsiReference, PyUserInitiatedResolvableReference { + protected final PsiElement myElement; + + protected BaseReference(@NotNull PsiElement element) { + myElement = element; + } + + public PsiElement getElement() { + return myElement; + } + + public TextRange getRangeInElement() { + return new TextRange(0, myElement.getTextLength()); + } + + @NotNull + public String getCanonicalText() { + return myElement.getText(); + } + + public PsiElement handleElementRename(@NotNull String newElementName) throws IncorrectOperationException { + throw new IncorrectOperationException(); + } + + public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException { + return null; + } + + public boolean isReferenceTo(PsiElement element) { + return resolve() == element; + } + + public boolean isSoft() { + return false; + } + + @Nullable + @Override + public PsiElement userInitiatedResolve() { + // Override this method if your reference may benefit from this knowledge + return resolve(); + } +} diff --git a/python/src/com/jetbrains/python/PyStringLiteralReference.java b/python/src/com/jetbrains/python/PyStringLiteralReference.java new file mode 100644 index 000000000000..46fd61466ac9 --- /dev/null +++ b/python/src/com/jetbrains/python/PyStringLiteralReference.java @@ -0,0 +1,50 @@ +/* + * Copyright 2000-2016 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.jetbrains.python; + +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; +import com.jetbrains.python.psi.PyElementGenerator; +import com.jetbrains.python.psi.StringLiteralExpression; +import org.jetbrains.annotations.NotNull; + +/** + * Some reference for string literal (handles rename and range correctly) + * + * @author Ilya.Kazakevich + */ +public abstract class PyStringLiteralReference extends BaseReference { + @NotNull + protected final StringLiteralExpression myStringLiteral; + + protected PyStringLiteralReference(@NotNull final StringLiteralExpression element) { + super(element); + myStringLiteral = element; + } + + @SuppressWarnings("RefusedBequest") // 1 instead of 1 in range and "-1" at the end because we do not need quotes + @Override + public final TextRange getRangeInElement() { + return PythonStringUtil.getTextRange(myElement); + } + + @Override + public PsiElement handleElementRename(@NotNull final String newElementName) { + final PsiElement newString = PyElementGenerator.getInstance(myElement.getProject()).createStringLiteralFromString(newElementName); + myStringLiteral.replace(newString); + return newString; + } +} diff --git a/spellchecker/src/com/intellij/spellchecker/jetbrains.dic b/spellchecker/src/com/intellij/spellchecker/jetbrains.dic index ae4e810d6df2..acdc832f0da1 100644 --- a/spellchecker/src/com/intellij/spellchecker/jetbrains.dic +++ b/spellchecker/src/com/intellij/spellchecker/jetbrains.dic @@ -92,6 +92,7 @@ codesign commandline committer commonjs +comparision concat cond conf diff --git a/xml/impl/src/com/intellij/xml/impl/schema/SchemaDefinitionsSearch.java b/xml/impl/src/com/intellij/xml/impl/schema/SchemaDefinitionsSearch.java index b3cc34ff57a1..b3a77fb15077 100644 --- a/xml/impl/src/com/intellij/xml/impl/schema/SchemaDefinitionsSearch.java +++ b/xml/impl/src/com/intellij/xml/impl/schema/SchemaDefinitionsSearch.java @@ -53,7 +53,7 @@ public class SchemaDefinitionsSearch implements QueryExecutor consumer) { if (queryParameters instanceof XmlTagImpl) { final XmlTagImpl xml = (XmlTagImpl) queryParameters; - if (isTypeElement(xml)) { + if (ApplicationManager.getApplication().runReadAction((Computable)() -> isTypeElement(xml))) { final Collection infos = ApplicationManager.getApplication().runReadAction(new Computable>() { @Override