diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java index 231e9d708539..0805d85319fc 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java @@ -17,12 +17,16 @@ package com.intellij.codeInspection.dataFlow; import com.intellij.codeInsight.NullableNotNullManager; import com.intellij.codeInspection.dataFlow.MethodContract.ValueConstraint; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.RecursionManager; import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; -import com.intellij.psi.util.*; +import com.intellij.psi.util.CachedValueProvider; +import com.intellij.psi.util.CachedValuesManager; +import com.intellij.psi.util.PsiModificationTracker; +import com.intellij.psi.util.PsiUtil; import com.intellij.util.Function; import com.intellij.util.NullableFunction; import com.intellij.util.containers.ContainerUtil; @@ -39,6 +43,7 @@ import static com.intellij.codeInspection.dataFlow.MethodContract.ValueConstrain * @author peter */ public class ContractInference { + public static final int MAX_CONTRACT_COUNT = 10; @NotNull public static List inferContracts(@NotNull final PsiMethod method) { @@ -64,6 +69,7 @@ public class ContractInference { } class ContractInferenceInterpreter { + private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.dataFlow.ContractInferenceInterpreter"); private final PsiMethod myMethod; private final ValueConstraint[] myEmptyConstraints; @@ -84,7 +90,7 @@ class ContractInferenceInterpreter { if (referenceTypeReturned) { contracts = boxReturnValues(contracts); } - return ContainerUtil.filter(contracts, new Condition() { + List compatible = ContainerUtil.filter(contracts, new Condition() { @Override public boolean value(MethodContract contract) { if (notNull && contract.returnValue == NOT_NULL_VALUE) { @@ -93,6 +99,11 @@ class ContractInferenceInterpreter { return InferenceFromSourceUtil.isReturnTypeCompatible(returnType, contract.returnValue); } }); + if (compatible.size() > ContractInference.MAX_CONTRACT_COUNT) { + LOG.debug("Too many contracts for " + PsiUtil.getMemberQualifiedName(myMethod) + ", shrinking the list"); + return compatible.subList(0, ContractInference.MAX_CONTRACT_COUNT); + } + return compatible; } @NotNull diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java index de019dad0cd5..e914c50278f9 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java @@ -264,10 +264,8 @@ public class DfaMemoryStateImpl implements DfaMemoryState { private Integer getOrCreateEqClassIndex(DfaValue dfaValue) { int i = getEqClassIndex(dfaValue); if (i != -1) return i; - if (!canBeReused(dfaValue) && !(((DfaBoxedValue)dfaValue).getWrappedValue() instanceof DfaConstValue)) { - return null; - } - if (dfaValue instanceof DfaTypeValue) { + if (!canBeInRelation(dfaValue) || + !canBeReused(dfaValue) && !(((DfaBoxedValue)dfaValue).getWrappedValue() instanceof DfaConstValue)) { return null; } EqClass aClass = new EqClass(myFactory); @@ -283,6 +281,11 @@ public class DfaMemoryStateImpl implements DfaMemoryState { return myEqClasses.size() - 1; } + private static boolean canBeInRelation(DfaValue dfaValue) { + DfaValue unwrapped = unwrap(dfaValue); + return unwrapped instanceof DfaVariableValue || unwrapped instanceof DfaConstValue; + } + @NotNull List getEquivalentValues(@NotNull DfaValue dfaValue) { int index = getEqClassIndex(dfaValue); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java index 39a6d28e62af..4d8df8da219a 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java @@ -17,6 +17,7 @@ package com.intellij.codeInspection.dataFlow; import com.intellij.codeInspection.dataFlow.instructions.*; import com.intellij.codeInspection.dataFlow.value.*; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Pair; import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; @@ -29,10 +30,7 @@ import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Set; +import java.util.*; import static com.intellij.psi.JavaTokenType.*; @@ -40,6 +38,7 @@ import static com.intellij.psi.JavaTokenType.*; * @author peter */ public class StandardInstructionVisitor extends InstructionVisitor { + private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.dataFlow.StandardInstructionVisitor"); private static final Object ANY_VALUE = new Object(); private final Set myReachable = new THashSet(); private final Set myCanBeNullInInstanceof = new THashSet(); @@ -161,11 +160,19 @@ public class StandardInstructionVisitor extends InstructionVisitor { DfaValue[] argValues = popCallArguments(instruction, runner, memState); final DfaValue qualifier = popQualifier(instruction, runner, memState); - List currentStates = ContainerUtil.newArrayList(memState); + LinkedHashSet currentStates = ContainerUtil.newLinkedHashSet(memState); Set finalStates = ContainerUtil.newLinkedHashSet(); if (argValues != null) { for (MethodContract contract : instruction.getContracts()) { currentStates = addContractResults(argValues, contract, currentStates, instruction, runner.getFactory(), finalStates); + if (currentStates.size() + finalStates.size() > DataFlowRunner.MAX_STATES_PER_BRANCH) { + if (LOG.isDebugEnabled()) { + LOG.debug("Too complex contract on " + instruction.getContext() + ", skipping contract processing"); + } + finalStates.clear(); + currentStates = ContainerUtil.newLinkedHashSet(memState); + break; + } } } for (DfaMemoryState state : currentStates) { @@ -237,14 +244,14 @@ public class StandardInstructionVisitor extends InstructionVisitor { return qualifier; } - private List addContractResults(DfaValue[] argValues, + private LinkedHashSet addContractResults(DfaValue[] argValues, MethodContract contract, - List states, + LinkedHashSet states, MethodCallInstruction instruction, DfaValueFactory factory, Set finalStates) { DfaConstValue.Factory constFactory = factory.getConstFactory(); - List falseStates = ContainerUtil.newArrayList(); + LinkedHashSet falseStates = ContainerUtil.newLinkedHashSet(); for (int i = 0; i < argValues.length; i++) { DfaValue argValue = argValues[i]; MethodContract.ValueConstraint constraint = contract.arguments[i]; @@ -263,7 +270,7 @@ public class StandardInstructionVisitor extends InstructionVisitor { condition = constFactory.createFromValue((argValue == expectedValue) != invertCondition, PsiType.BOOLEAN, null); } - List nextStates = ContainerUtil.newArrayList(); + LinkedHashSet nextStates = ContainerUtil.newLinkedHashSet(); for (DfaMemoryState state : states) { boolean unknownVsNull = expectedValue == constFactory.getNull() && argValue instanceof DfaVariableValue && diff --git a/java/java-impl/src/com/intellij/psi/impl/JavaCodeBlockModificationListener.java b/java/java-impl/src/com/intellij/psi/impl/JavaCodeBlockModificationListener.java index 314f55c2f7fe..b99d4a319e36 100644 --- a/java/java-impl/src/com/intellij/psi/impl/JavaCodeBlockModificationListener.java +++ b/java/java-impl/src/com/intellij/psi/impl/JavaCodeBlockModificationListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2015 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,7 +20,6 @@ import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.psi.*; import com.intellij.psi.impl.source.jsp.jspXml.JspDirective; import com.intellij.psi.util.PsiModificationTracker; -import com.intellij.psi.xml.XmlFile; import org.jetbrains.annotations.NotNull; public class JavaCodeBlockModificationListener implements PsiTreeChangePreprocessor { @@ -70,9 +69,11 @@ public class JavaCodeBlockModificationListener implements PsiTreeChangePreproces private void processChange(final PsiElement parent, final PsiElement child1, final PsiElement child2) { try { if (!isInsideCodeBlock(parent)) { - if (parent != null && isClassOwner(parent.getContainingFile()) || - isClassOwner(child1) || isClassOwner(child2) || isSourceDir(parent) || - (parent != null && isClassOwner(parent.getParent()))) { + if (isClassOwner(parent.getContainingFile()) || + isClassOwner(child1) || + isClassOwner(child2) || + isSourceDir(parent) || + isClassOwner(parent.getParent())) { myModificationTracker.incCounter(); } else { @@ -96,7 +97,7 @@ public class JavaCodeBlockModificationListener implements PsiTreeChangePreproces } private static boolean isClassOwner(final PsiElement element) { - return element instanceof PsiClassOwner && !(element instanceof XmlFile) || element instanceof JspDirective; + return element instanceof PsiClassOwner || element instanceof JspDirective; } private static boolean containsClassesInside(final PsiElement element) { diff --git a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/PackagePrefixFileSystemItemImpl.java b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/PackagePrefixFileSystemItemImpl.java index ce56ac3a36ad..6e1dc775757d 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/PackagePrefixFileSystemItemImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/PackagePrefixFileSystemItemImpl.java @@ -19,8 +19,8 @@ import com.intellij.lang.ASTNode; import com.intellij.lang.Language; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.NonPhysicalFileSystem; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.ex.dummy.DummyFileSystem; import com.intellij.psi.*; import com.intellij.psi.impl.PsiElementBase; import com.intellij.psi.search.PsiElementProcessor; @@ -192,7 +192,7 @@ class PackagePrefixFileSystemItemImpl extends PsiElementBase implements PsiFileS @Override public boolean isPhysical() { final VirtualFile file = getVirtualFile(); - return file != null && !(file.getFileSystem() instanceof DummyFileSystem); + return file != null && !(file.getFileSystem() instanceof NonPhysicalFileSystem); } @Override diff --git a/java/java-impl/src/com/intellij/refactoring/actions/IntroduceFunctionalParameterAction.java b/java/java-impl/src/com/intellij/refactoring/actions/IntroduceFunctionalParameterAction.java new file mode 100644 index 000000000000..734d27dbc767 --- /dev/null +++ b/java/java-impl/src/com/intellij/refactoring/actions/IntroduceFunctionalParameterAction.java @@ -0,0 +1,88 @@ +/* + * Copyright 2000-2015 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. + */ + +/* + * Created by IntelliJ IDEA. + * User: dsl + * Date: 06.05.2002 + * Time: 14:03:43 + * To change template for new class use + * Code Style | Class Templates options (Tools | IDE Options). + */ +package com.intellij.refactoring.actions; + +import com.intellij.lang.refactoring.RefactoringSupportProvider; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pass; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.refactoring.HelpID; +import com.intellij.refactoring.RefactoringActionHandler; +import com.intellij.refactoring.RefactoringBundle; +import com.intellij.refactoring.extractMethod.ExtractMethodHandler; +import com.intellij.refactoring.introduceParameter.IntroduceParameterHandler; +import com.intellij.refactoring.util.CommonRefactoringUtil; +import org.jetbrains.annotations.NotNull; + +public class IntroduceFunctionalParameterAction extends BasePlatformRefactoringAction { + private static final String REFACTORING_NAME = RefactoringBundle.message("introduce.functional.parameter.title"); + + @Override + protected boolean isAvailableInEditorOnly() { + return true; + } + + @Override + protected boolean isEnabledOnElements(@NotNull PsiElement[] elements) { + return false; + } + + @Override + protected RefactoringActionHandler getRefactoringHandler(@NotNull RefactoringSupportProvider provider) { + return new IntroduceParameterHandler() { + @Override + public void invoke(@NotNull Project project, @NotNull PsiElement[] elements, DataContext dataContext) { + if (dataContext != null) { + final PsiFile file = CommonDataKeys.PSI_FILE.getData(dataContext); + final Editor editor = CommonDataKeys.EDITOR.getData(dataContext); + if (file != null && editor != null && !introduceStrategy(project, editor, file, elements)) { + showErrorMessage(project, editor); + } + } + } + + @Override + public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file, DataContext dataContext) { + ExtractMethodHandler.selectAndPass(project, editor, file, new Pass() { + @Override + public void pass(PsiElement[] elements) { + if (!introduceStrategy(project, editor, file, elements)) { + showErrorMessage(project, editor); + } + } + }); + } + + private void showErrorMessage(@NotNull Project project, Editor editor) { + String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("is.not.supported.in.the.current.context", REFACTORING_NAME)); + CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.INTRODUCE_PARAMETER); + } + }; + } +} diff --git a/java/java-impl/src/com/intellij/refactoring/introduceField/ElementToWorkOn.java b/java/java-impl/src/com/intellij/refactoring/introduceField/ElementToWorkOn.java index b1449bc92340..26bb4367b854 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceField/ElementToWorkOn.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceField/ElementToWorkOn.java @@ -43,6 +43,7 @@ public class ElementToWorkOn { public static final Key PREFIX = Key.create("prefix"); public static final Key SUFFIX = Key.create("suffix"); public static final Key TEXT_RANGE = Key.create("range"); + public static final Key REPLACE_NON_PHYSICAL = Key.create("replace_non_physical"); public static final Key OUT_OF_CODE_BLOCK= Key.create("out_of_code_block"); private ElementToWorkOn(PsiLocalVariable localVariable, PsiExpression expr) { diff --git a/java/java-impl/src/com/intellij/refactoring/introduceParameter/IntroduceParameterHandler.java b/java/java-impl/src/com/intellij/refactoring/introduceParameter/IntroduceParameterHandler.java index ec7fe89d622a..7a6fe2485342 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceParameter/IntroduceParameterHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceParameter/IntroduceParameterHandler.java @@ -50,7 +50,6 @@ import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Pass; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.TextRange; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; @@ -528,68 +527,77 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase { public boolean introduceStrategy(final Project project, final Editor editor, PsiFile file) { final SelectionModel selectionModel = editor.getSelectionModel(); if (selectionModel.hasSelection()) { - final PsiElement[] elements = CodeInsightUtil - .findStatementsInRange(file, selectionModel.getSelectionStart(), selectionModel.getSelectionEnd()); - if (elements.length > 0) { - final AbstractInplaceIntroducer inplaceIntroducer = AbstractInplaceIntroducer.getActiveIntroducer(editor); - if (inplaceIntroducer instanceof InplaceIntroduceParameterPopup) { - return false; - } - final List enclosingMethods = getEnclosingMethods(Util.getContainingMethod(elements[0])); - if (enclosingMethods.isEmpty()) { - return false; - } + final PsiElement[] elements = CodeInsightUtil.findStatementsInRange(file, selectionModel.getSelectionStart(), selectionModel.getSelectionEnd()); + return introduceStrategy(project, editor, file, elements); + } + return false; + } - final PsiFile copy = PsiFileFactory.getInstance(project) - .createFileFromText(file.getName(), file.getFileType(), file.getText(), file.getModificationStamp(), false); + @VisibleForTesting + public boolean introduceStrategy(final Project project, final Editor editor, PsiFile file, final PsiElement[] elements) { + if (elements.length > 0) { + final AbstractInplaceIntroducer inplaceIntroducer = AbstractInplaceIntroducer.getActiveIntroducer(editor); + if (inplaceIntroducer instanceof InplaceIntroduceParameterPopup) { + return false; + } + final List enclosingMethods = getEnclosingMethods(Util.getContainingMethod(elements[0])); + if (enclosingMethods.isEmpty()) { + return false; + } - final PsiElement[] elementsCopy = CodeInsightUtil.findStatementsInRange(copy, - elements[0].getTextRange().getStartOffset(), + final PsiFile copy = PsiFileFactory.getInstance(project) + .createFileFromText(file.getName(), file.getFileType(), file.getText(), file.getModificationStamp(), false); + + final PsiExpression exprInRange = CodeInsightUtil.findExpressionInRange(copy, elements[0].getTextRange().getStartOffset(), + elements[elements.length - 1].getTextRange().getEndOffset()); + final PsiElement[] elementsCopy = exprInRange != null + ? new PsiElement[] {exprInRange} + : CodeInsightUtil.findStatementsInRange(copy, elements[0].getTextRange().getStartOffset(), elements[elements.length - 1].getTextRange().getEndOffset()); - final MyExtractMethodProcessor processor = new MyExtractMethodProcessor(project, editor, elementsCopy); - try { - if (!processor.prepare()) return false; - processor.showDialog(); + final MyExtractMethodProcessor processor = new MyExtractMethodProcessor(project, editor, elementsCopy); + try { + if (!processor.prepare()) return false; + processor.showDialog(); - //provide context for generated method to check exceptions compatibility - final PsiMethod emptyMethod = JavaPsiFacade.getElementFactory(project) - .createMethodFromText(processor.generateEmptyMethod("name").getText(), elements[0]); - final Collection types = FunctionalInterfaceSuggester.suggestFunctionalInterfaces(emptyMethod); - if (types.isEmpty()) { - return false; - } + //provide context for generated method to check exceptions compatibility + final PsiMethod emptyMethod = JavaPsiFacade.getElementFactory(project) + .createMethodFromText(processor.generateEmptyMethod("name").getText(), elements[0]); + final Collection types = FunctionalInterfaceSuggester.suggestFunctionalInterfaces(emptyMethod); + if (types.isEmpty()) { + return false; + } - if (types.size() == 1 || ApplicationManager.getApplication().isUnitTestMode()) { - final PsiType next = types.iterator().next(); - functionalInterfaceSelected(next, enclosingMethods, project, editor, processor, elements); + if (types.size() == 1 || ApplicationManager.getApplication().isUnitTestMode()) { + final PsiType next = types.iterator().next(); + functionalInterfaceSelected(next, enclosingMethods, project, editor, processor, elements); + } + else { + final Map classes = new LinkedHashMap(); + for (PsiType type : types) { + classes.put(PsiUtil.resolveClassInType(type), type); } - else { - final Map classes = new LinkedHashMap(); - for (PsiType type : types) { - classes.put(PsiUtil.resolveClassInType(type), type); - } - final PsiClass[] psiClasses = classes.keySet().toArray(new PsiClass[classes.size()]); - final String methodSignature = - PsiFormatUtil.formatMethod(emptyMethod, PsiSubstitutor.EMPTY, PsiFormatUtilBase.SHOW_PARAMETERS, PsiFormatUtilBase.SHOW_TYPE); - final PsiType returnType = emptyMethod.getReturnType(); - LOG.assertTrue(returnType != null); - final String title = "Choose Applicable Functional Interface: " + methodSignature + " -> " + returnType.getPresentableText(); - NavigationUtil.getPsiElementPopup(psiClasses, new PsiClassListCellRenderer(), title, - new PsiElementProcessor() { - @Override - public boolean execute(@NotNull PsiClass psiClass) { - functionalInterfaceSelected(classes.get(psiClass), enclosingMethods, project, editor, processor, elements); - return true; - } - }).showInBestPositionFor(editor); - return true; - } - + final PsiClass[] psiClasses = classes.keySet().toArray(new PsiClass[classes.size()]); + final String methodSignature = + PsiFormatUtil.formatMethod(emptyMethod, PsiSubstitutor.EMPTY, PsiFormatUtilBase.SHOW_PARAMETERS, PsiFormatUtilBase.SHOW_TYPE); + final PsiType returnType = emptyMethod.getReturnType(); + LOG.assertTrue(returnType != null); + final String title = "Choose Applicable Functional Interface: " + methodSignature + " -> " + returnType.getPresentableText(); + NavigationUtil.getPsiElementPopup(psiClasses, new PsiClassListCellRenderer(), title, + new PsiElementProcessor() { + @Override + public boolean execute(@NotNull PsiClass psiClass) { + functionalInterfaceSelected(classes.get(psiClass), enclosingMethods, project, editor, processor, + elements); + return true; + } + }).showInBestPositionFor(editor); return true; } - catch (IncorrectOperationException ignore) {} - catch (PrepareFailedException ignore) {} + + return true; } + catch (IncorrectOperationException ignore) {} + catch (PrepareFailedException ignore) {} } return false; } @@ -615,7 +623,8 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase { final PsiType selectedType, final MyExtractMethodProcessor processor, final PsiElement[] elements) { - final PsiElement commonParent = elements.length > 1 ? PsiTreeUtil.findCommonParent(elements) : elements[0].getParent(); + final PsiElement commonParent = elements.length > 1 ? PsiTreeUtil.findCommonParent(elements) + : PsiTreeUtil.getParentOfType(elements[0].getParent(), PsiCodeBlock.class, false); if (commonParent == null) { LOG.error("Should have common parent:" + Arrays.toString(elements)); return; @@ -623,7 +632,8 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase { final RangeMarker marker = editor.getDocument().createRangeMarker(commonParent.getTextRange()); final PsiElement[] copyElements = processor.getElements(); - final PsiElement containerCopy = copyElements.length > 1 ? PsiTreeUtil.findCommonParent(copyElements) : copyElements[0].getParent(); + final PsiElement containerCopy = copyElements.length > 1 ? PsiTreeUtil.findCommonParent(copyElements) + : PsiTreeUtil.getParentOfType(copyElements[0].getParent(), PsiCodeBlock.class, false); if (containerCopy == null) { LOG.error("Should have common parent:" + Arrays.toString(copyElements)); return; @@ -644,6 +654,11 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase { LOG.assertTrue(method != null); final String interfaceMethodName = method.getName(); processor.setMethodName(interfaceMethodName); + + if (copyElements.length == 1) { + copyElements[0].putUserData(ElementToWorkOn.REPLACE_NON_PHYSICAL, true); + } + processor.doExtract(); final PsiMethod extractedMethod = processor.getExtractedMethod(); @@ -704,6 +719,11 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase { return false; } + @Override + protected boolean isFoldingApplicable() { + return false; + } + @Override public boolean prepare(@Nullable Pass pass) throws PrepareFailedException { final boolean prepare = super.prepare(pass); diff --git a/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java b/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java index b63245fe10b6..84bc10e52484 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java @@ -1000,7 +1000,7 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase { } else { expr2 = RefactoringUtil.outermostParenthesizedExpression(expr1); } - if (expr2.isPhysical()) { + if (expr2.isPhysical() || expr1.getUserData(ElementToWorkOn.REPLACE_NON_PHYSICAL) != null) { return expr2.replace(ref); } else { diff --git a/java/java-impl/src/com/intellij/refactoring/safeDelete/SafeDeleteJavaCallerChooser.java b/java/java-impl/src/com/intellij/refactoring/safeDelete/SafeDeleteJavaCallerChooser.java index a53c78a76a08..ef96f251d7c2 100644 --- a/java/java-impl/src/com/intellij/refactoring/safeDelete/SafeDeleteJavaCallerChooser.java +++ b/java/java-impl/src/com/intellij/refactoring/safeDelete/SafeDeleteJavaCallerChooser.java @@ -23,6 +23,7 @@ import com.intellij.openapi.util.Ref; import com.intellij.psi.*; import com.intellij.psi.search.LocalSearchScope; import com.intellij.psi.search.searches.ReferencesSearch; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.refactoring.changeSignature.MethodNodeBase; import com.intellij.refactoring.changeSignature.inCallers.JavaCallerChooser; @@ -111,37 +112,47 @@ abstract class SafeDeleteJavaCallerChooser extends JavaCallerChooser { final PsiExpression[] expressions = argumentList.getExpressions(); if (expressions.length > parameterIndex) { final PsiExpression expression = PsiUtil.deparenthesizeExpression(expressions[parameterIndex]); - if (expression instanceof PsiReferenceExpression) { - final PsiElement resolve = ((PsiReferenceExpression)expression).resolve(); - if (resolve instanceof PsiParameter && !((PsiParameter)resolve).isVarArgs()) { - final PsiElement scope = ((PsiParameter)resolve).getDeclarationScope(); + if (expression != null) { + final Set paramRefs = new HashSet(); + expression.accept(new JavaRecursiveElementWalkingVisitor() { + @Override + public void visitReferenceExpression(PsiReferenceExpression expression) { + super.visitReferenceExpression(expression); + final PsiElement resolve = expression.resolve(); + if (resolve instanceof PsiParameter) { + paramRefs.add((PsiParameter)resolve); + } + } + }); + + final PsiParameter parameter = ContainerUtil.getFirstItem(paramRefs); + if (parameter != null && !parameter.isVarArgs()) { + final PsiElement scope = parameter.getDeclarationScope(); if (scope instanceof PsiMethod && ((PsiMethod)scope).findDeepestSuperMethods().length == 0) { final Ref ref = new Ref(false); - if (ReferencesSearch.search(resolve, new LocalSearchScope(scope)).forEach(new Processor() { + if (ReferencesSearch.search(parameter, new LocalSearchScope(scope)).forEach(new Processor() { @Override public boolean process(PsiReference reference) { final PsiElement element = reference.getElement(); if (element instanceof PsiReferenceExpression) { - final PsiElement parent = element.getParent(); - if (parent instanceof PsiExpressionList) { - final PsiElement gParent = parent.getParent(); - if (gParent instanceof PsiCallExpression) { - final PsiMethod resolved = ((PsiCallExpression)gParent).resolveMethod(); - if (scope.equals(resolved)) { - return true; - } - if (nodeMethod.equals(resolved)) { - ref.set(true); - return true; - } + PsiElement parent = PsiTreeUtil.getParentOfType(element, PsiCallExpression.class); + while (parent != null) { + final PsiMethod resolved = ((PsiCallExpression)parent).resolveMethod(); + if (scope.equals(resolved)) { + return true; } + if (nodeMethod.equals(resolved)) { + ref.set(true); + return true; + } + parent = PsiTreeUtil.getParentOfType(parent, PsiCallExpression.class, true); } return false; } return true; } }) && ref.get()) { - return (PsiParameter)resolve; + return (PsiParameter)parameter; } } } diff --git a/java/java-tests/testData/inspection/canBeFinal/InterfaceMethodInHierarchy/expected.xml b/java/java-tests/testData/inspection/canBeFinal/InterfaceMethodInHierarchy/expected.xml new file mode 100644 index 000000000000..d704d58ed391 --- /dev/null +++ b/java/java-tests/testData/inspection/canBeFinal/InterfaceMethodInHierarchy/expected.xml @@ -0,0 +1,4 @@ + + + + diff --git a/java/java-tests/testData/inspection/canBeFinal/InterfaceMethodInHierarchy/src/A.java b/java/java-tests/testData/inspection/canBeFinal/InterfaceMethodInHierarchy/src/A.java new file mode 100644 index 000000000000..65a47c6772b0 --- /dev/null +++ b/java/java-tests/testData/inspection/canBeFinal/InterfaceMethodInHierarchy/src/A.java @@ -0,0 +1,20 @@ +interface A

{ + void accept(C

c); +} + +final class AImpl

implements A

{ + private final B

m_b = null; + + @Override + public final void accept(C

c) { + m_b.accept(c); + } +} + +interface B

{ + void accept(C

c); +} + +interface C

{} +interface D {} + diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/ContractWithManyParameters.java b/java/java-tests/testData/inspection/dataFlow/fixture/ContractWithManyParameters.java new file mode 100644 index 000000000000..4b5bce223a33 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/ContractWithManyParameters.java @@ -0,0 +1,28 @@ +import java.util.Set; + +class Foo { + + private static void calculate(String p1, String p2, Set p3, + String p4, String p5, + String p6, Integer p7, Integer p8, + Integer p9, Boolean p10, String p11, + Integer p12, Integer p13) { + validate(p1, p2, p4, p5, p6, p3.toString(), p7, p8, p9, p10, p11, p12, p13); + System.out.println(p1); + } + + public static void validate(String p1, String p2, String p3, String p4, String p5, String + p6, Integer p7, Integer p8, Integer p9, Boolean p10, String p11, Integer p12, Integer p13) { + if (p1 == null && p2 == null && p3 == null && p4 == null && p5 == null && p6 == null && p7 == null && + p8 == null && p9 == null && p10 == null && p11 == null && p12 == null && p13 == null) + throw new RuntimeException(); + + if (p10 != null && (p8 == null && p7 == null && p9 == null)) + throw new RuntimeException(); + + if ((p12 != null || p13 != null) && (p12 == null || p13 == null)) + throw new RuntimeException(); + } + + +} diff --git a/java/java-tests/testData/refactoring/introduceFunctionalParameter/afterEnsureNotFolded.java b/java/java-tests/testData/refactoring/introduceFunctionalParameter/afterEnsureNotFolded.java new file mode 100644 index 000000000000..b993040d6bd8 --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceFunctionalParameter/afterEnsureNotFolded.java @@ -0,0 +1,27 @@ +import java.util.function.Function; + +class Test { + + { + final int[] equals = new int[0]; + performTest(new Function() { + public String[] apply(String[] fields) { + System.out.println(); + return getIndexed(fields, equals); + } + }); + } + + private static void performTest(Function anObject) { + String[] fields = new String[0]; + + final String[] indexed = anObject.apply(fields); + + System.out.println(indexed); + } + + private static String[] getIndexed(String[] fields, int[] indices) { + return new String[indices.length]; + } + +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/introduceFunctionalParameter/beforeEnsureNotFolded.java b/java/java-tests/testData/refactoring/introduceFunctionalParameter/beforeEnsureNotFolded.java new file mode 100644 index 000000000000..bf6583dd6d98 --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceFunctionalParameter/beforeEnsureNotFolded.java @@ -0,0 +1,20 @@ +class Test { + + { + performTest(new int[0]); + } + + private static void performTest(int[] equals) { + String[] fields = new String[0]; + + System.out.println(); + final String[] indexed = getIndexed(fields, equals); + + System.out.println(indexed); + } + + private static String[] getIndexed(String[] fields, int[] indices) { + return new String[indices.length]; + } + +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/safeDelete/DeepDeleteParameterOtherTypeInBinaryExpression.java b/java/java-tests/testData/refactoring/safeDelete/DeepDeleteParameterOtherTypeInBinaryExpression.java new file mode 100644 index 000000000000..d39bbaa19d7b --- /dev/null +++ b/java/java-tests/testData/refactoring/safeDelete/DeepDeleteParameterOtherTypeInBinaryExpression.java @@ -0,0 +1,8 @@ +class Test { + void foo(String s) { + bar(s.length()); + bar(s.length() + 1); + } + + void bar(int i){} +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/safeDelete/DeepDeleteParameterOtherTypeInBinaryExpression_after.java b/java/java-tests/testData/refactoring/safeDelete/DeepDeleteParameterOtherTypeInBinaryExpression_after.java new file mode 100644 index 000000000000..d3f5fd3b4214 --- /dev/null +++ b/java/java-tests/testData/refactoring/safeDelete/DeepDeleteParameterOtherTypeInBinaryExpression_after.java @@ -0,0 +1,8 @@ +class Test { + void foo() { + bar(); + bar(); + } + + void bar(){} +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/CanBeFinalTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/CanBeFinalTest.java index bb77b6b9bb18..08c7f6002c2a 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/CanBeFinalTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/CanBeFinalTest.java @@ -102,4 +102,13 @@ public class CanBeFinalTest extends InspectionTestCase { doTest(tool); } + + public void testInterfaceMethodInHierarchy() throws Exception { + CanBeFinalInspection tool = new CanBeFinalInspection(); + tool.REPORT_CLASSES = false; + tool.REPORT_FIELDS = false; + tool.REPORT_METHODS = true; + + doTest(tool); + } } diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/ContractInferenceFromSourceTest.groovy b/java/java-tests/testSrc/com/intellij/codeInspection/ContractInferenceFromSourceTest.groovy index 9b13347bba7a..107671c0d18f 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/ContractInferenceFromSourceTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInspection/ContractInferenceFromSourceTest.groovy @@ -468,6 +468,24 @@ public static boolean isBlank(String s) { assert c == ['null -> true'] } + public void "test do not generate too many contract clauses"() { + def c = inferContracts(""" +public static void validate(String p1, String p2, String p3, String p4, String p5, String + p6, Integer p7, Integer p8, Integer p9, Boolean p10, String p11, Integer p12, Integer p13) { + if (p1 == null && p2 == null && p3 == null && p4 == null && p5 == null && p6 == null && p7 == null && p8 == + null && p9 == null && p10 == null && p11 == null && p12 == null && p13 == null) + throw new RuntimeException(); + + if (p10 != null && (p8 == null && p7 == null && p9 == null)) + throw new RuntimeException(); + + if ((p12 != null || p13 != null) && (p12 == null || p13 == null)) + throw new RuntimeException(); + } + """) + assert c.size() <= ContractInference.MAX_CONTRACT_COUNT // there could be 74 of them in total + } + public void "test no inference for unused anonymous class methods where annotations won't be used anyway"() { def method = PsiTreeUtil.findChildOfType(myFixture.addClass(""" class Foo {{ diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java index efb38217a88b..fbedf9bd82d4 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java @@ -216,6 +216,7 @@ public class DataFlowInspectionTest extends LightCodeInsightFixtureTestCase { public void testContractAnnotation() { doTest(); } public void testContractInapplicableComparison() { doTest(); } public void testContractInLoopNotTooComplex() { doTest(); } + public void testContractWithManyParameters() { doTest(); } public void testContractWithNullable() { doTest(); } public void testContractWithNotNull() { doTest(); } public void testContractPreservesUnknownNullability() { doTest(); } diff --git a/java/java-tests/testSrc/com/intellij/refactoring/IntroduceFunctionalParameterTest.java b/java/java-tests/testSrc/com/intellij/refactoring/IntroduceFunctionalParameterTest.java index 31c26e2ae2cd..2f9702d095e1 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/IntroduceFunctionalParameterTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/IntroduceFunctionalParameterTest.java @@ -53,6 +53,10 @@ public class IntroduceFunctionalParameterTest extends LightRefactoringTestCase doTest(); } + public void testEnsureNotFolded() throws Exception { + doTest(); + } + @NotNull @Override protected String getTestDataPath() { diff --git a/java/java-tests/testSrc/com/intellij/refactoring/SafeDeleteTest.java b/java/java-tests/testSrc/com/intellij/refactoring/SafeDeleteTest.java index 819922c4016f..477d15097cd0 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/SafeDeleteTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/SafeDeleteTest.java @@ -80,6 +80,10 @@ public class SafeDeleteTest extends MultiFileTestCase { doSingleFileTest(); } + public void testDeepDeleteParameterOtherTypeInBinaryExpression() throws Exception { + doSingleFileTest(); + } + public void testImpossibleToDeepDeleteParameter() throws Exception { doSingleFileTest(); } diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefManagerImpl.java b/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefManagerImpl.java index b0af3b30cace..ce083ff09682 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefManagerImpl.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefManagerImpl.java @@ -64,7 +64,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; public class RefManagerImpl extends RefManager { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.reference.RefManager"); - private int myLastUsedMask = 256 * 256 * 256 * 4; + private int myLastUsedMask = 256 * 256 * 256 * 8; @NotNull private final Project myProject; diff --git a/platform/core-api/src/com/intellij/ide/presentation/VirtualFilePresentation.java b/platform/core-api/src/com/intellij/ide/presentation/VirtualFilePresentation.java index d2d5ebfc31f4..551a2697aef8 100644 --- a/platform/core-api/src/com/intellij/ide/presentation/VirtualFilePresentation.java +++ b/platform/core-api/src/com/intellij/ide/presentation/VirtualFilePresentation.java @@ -17,6 +17,7 @@ package com.intellij.ide.presentation; import com.intellij.ide.TypePresentationService; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.IconUtil; import com.intellij.util.PlatformIcons; import org.jetbrains.annotations.NotNull; @@ -26,7 +27,12 @@ import javax.swing.*; * @author yole */ public class VirtualFilePresentation { + public static Icon getIcon(@NotNull VirtualFile vFile) { + return IconUtil.getIcon(vFile, 0, null); + } + + public static Icon getIconImpl(@NotNull VirtualFile vFile) { Icon icon = TypePresentationService.getService().getIcon(vFile); if (icon != null) { return icon; diff --git a/platform/core-api/src/com/intellij/util/IconUtil.java b/platform/core-api/src/com/intellij/util/IconUtil.java index 07b5fca6b7e4..1d6964ad9bd3 100644 --- a/platform/core-api/src/com/intellij/util/IconUtil.java +++ b/platform/core-api/src/com/intellij/util/IconUtil.java @@ -132,7 +132,7 @@ public class IconUtil { if (!file.isValid() || project != null && (project.isDisposed() || !wasEverInitialized(project))) return null; final Icon providersIcon = getProvidersIcon(file, flags, project); - Icon icon = providersIcon == null ? VirtualFilePresentation.getIcon(file) : providersIcon; + Icon icon = providersIcon == null ? VirtualFilePresentation.getIconImpl(file) : providersIcon; final boolean dumb = project != null && DumbService.getInstance(project).isDumb(); for (FileIconPatcher patcher : getPatchers()) { @@ -160,7 +160,7 @@ public class IconUtil { public static Icon getIcon(@NotNull final VirtualFile file, @Iconable.IconFlags final int flags, @Nullable final Project project) { Icon lastIcon = Iconable.LastComputedIcon.get(file, flags); - final Icon base = lastIcon != null ? lastIcon : VirtualFilePresentation.getIcon(file); + final Icon base = lastIcon != null ? lastIcon : VirtualFilePresentation.getIconImpl(file); return IconDeferrer.getInstance().defer(base, new FileIconKey(file, project, flags), ICON_NULLABLE_FUNCTION); } diff --git a/platform/core-api/src/com/intellij/util/PathUtil.java b/platform/core-api/src/com/intellij/util/PathUtil.java index f1e9905f042c..7d9bbb9b2e40 100644 --- a/platform/core-api/src/com/intellij/util/PathUtil.java +++ b/platform/core-api/src/com/intellij/util/PathUtil.java @@ -128,4 +128,9 @@ public class PathUtil { } return path; } + + @NotNull + public static String makeFileName(@NotNull String name, @Nullable String extension) { + return name + (StringUtil.isEmpty(extension) ? "" : "." + extension); + } } diff --git a/platform/core-api/src/com/intellij/util/PlatformUtils.java b/platform/core-api/src/com/intellij/util/PlatformUtils.java index c9a040831edd..7459bed98172 100644 --- a/platform/core-api/src/com/intellij/util/PlatformUtils.java +++ b/platform/core-api/src/com/intellij/util/PlatformUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 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,6 +25,7 @@ import org.jetbrains.annotations.Nullable; public class PlatformUtils { public static final String PLATFORM_PREFIX_KEY = "idea.platform.prefix"; + // NOTE: If you add any new prefixes to this list, please update the IntelliJPlatformProduct class in DevKit plugin public static final String IDEA_PREFIX = "idea"; public static final String IDEA_CE_PREFIX = "Idea"; public static final String APPCODE_PREFIX = "AppCode"; diff --git a/platform/core-impl/src/com/intellij/psi/impl/PsiManagerImpl.java b/platform/core-impl/src/com/intellij/psi/impl/PsiManagerImpl.java index 2c4c00204937..2320f07cf071 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/PsiManagerImpl.java +++ b/platform/core-impl/src/com/intellij/psi/impl/PsiManagerImpl.java @@ -22,6 +22,7 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.fileTypes.InternalFileType; import com.intellij.openapi.progress.ProgressIndicatorProvider; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.FileIndexFacade; @@ -113,9 +114,6 @@ public class PsiManagerImpl extends PsiManagerEx { @Override public boolean isInProject(@NotNull PsiElement element) { - PsiFile file = element.getContainingFile(); - if (file != null && file.isPhysical() && file.getViewProvider().getVirtualFile() instanceof LightVirtualFile) return true; - if (element instanceof PsiDirectoryContainer) { PsiDirectory[] dirs = ((PsiDirectoryContainer)element).getDirectories(); for (PsiDirectory dir : dirs) { @@ -124,6 +122,7 @@ public class PsiManagerImpl extends PsiManagerEx { return true; } + PsiFile file = element.getContainingFile(); VirtualFile virtualFile = null; if (file != null) { virtualFile = file.getViewProvider().getVirtualFile(); @@ -131,6 +130,8 @@ public class PsiManagerImpl extends PsiManagerEx { else if (element instanceof PsiFileSystemItem) { virtualFile = ((PsiFileSystemItem)element).getVirtualFile(); } + if (virtualFile instanceof LightVirtualFile) return true; + if (virtualFile != null && virtualFile.getFileType() instanceof InternalFileType) return true; if (virtualFile != null) { return myExcludedFileIndex.isInContent(virtualFile); diff --git a/platform/duplicates-analysis/src/com/intellij/dupLocator/index/DuplicatesInspectionBase.java b/platform/duplicates-analysis/src/com/intellij/dupLocator/index/DuplicatesInspectionBase.java index 45b5b1a9b723..cc3630b13c8f 100644 --- a/platform/duplicates-analysis/src/com/intellij/dupLocator/index/DuplicatesInspectionBase.java +++ b/platform/duplicates-analysis/src/com/intellij/dupLocator/index/DuplicatesInspectionBase.java @@ -33,6 +33,8 @@ import java.util.SortedMap; import java.util.TreeMap; public class DuplicatesInspectionBase extends LocalInspectionTool { + private static final int MIN_FRAGMENT_SIZE = 3; + @Nullable @Override public ProblemDescriptor[] checkFile(@NotNull final PsiFile psiFile, @NotNull final InspectionManager manager, final boolean isOnTheFly) { @@ -145,11 +147,10 @@ public class DuplicatesInspectionBase extends LocalInspectionTool { final SmartList descriptors = new SmartList(); if (processor != null) { - for(Map.Entry entry:processor.reportedRanges.entrySet()) { final Integer offset = entry.getKey(); // todo 3 statements constant - if (processor.fragmentSize.get(offset) < 3) continue; + if (processor.fragmentSize.get(offset) < MIN_FRAGMENT_SIZE) continue; final VirtualFile file = processor.reportedFiles.get(offset); String message = "Found duplicated code in " + file.getPath(); @@ -158,8 +159,12 @@ public class DuplicatesInspectionBase extends LocalInspectionTool { final int offsetInOtherFile = processor.reportedOffsetInOtherFiles.get(offset); LocalQuickFix fix = createNavigateToDupeFix(file, offsetInOtherFile); + int hash = processor.fragmentHash.get(offset); + + LocalQuickFix viewAllDupesFix = hash != 0 ? createShowOtherDupesFix(virtualFile, offset, hash, psiFile.getProject()) : null; + ProblemDescriptor descriptor = manager - .createProblemDescriptor(targetElement, rangeInElement, message, ProblemHighlightType.WEAK_WARNING, isOnTheFly, fix); + .createProblemDescriptor(targetElement, rangeInElement, message, ProblemHighlightType.WEAK_WARNING, isOnTheFly, fix, viewAllDupesFix); descriptors.add(descriptor); } } @@ -170,6 +175,9 @@ public class DuplicatesInspectionBase extends LocalInspectionTool { protected LocalQuickFix createNavigateToDupeFix(@NotNull VirtualFile file, int offsetInOtherFile) { return null; } + protected LocalQuickFix createShowOtherDupesFix(VirtualFile file, int offset, int hash, Project project) { + return null; + } static abstract class DuplicatedCodeProcessor implements FileBasedIndex.ValueProcessor { final TreeMap reportedRanges = new TreeMap(); @@ -177,10 +185,12 @@ public class DuplicatesInspectionBase extends LocalInspectionTool { final TIntObjectHashMap reportedPsi = new TIntObjectHashMap(); final TIntIntHashMap reportedOffsetInOtherFiles = new TIntIntHashMap(); final TIntIntHashMap fragmentSize = new TIntIntHashMap(); + final TIntIntHashMap fragmentHash = new TIntIntHashMap(); final VirtualFile virtualFile; final Project project; final ProjectFileIndex myProjectFileIndex; T myNode; + int myHash; DuplicatedCodeProcessor(VirtualFile file, Project project) { virtualFile = file; @@ -191,6 +201,7 @@ public class DuplicatesInspectionBase extends LocalInspectionTool { void process(int hash, T node) { ProgressManager.checkCanceled(); myNode = node; + myHash = hash; FileBasedIndex.getInstance().processValues(DuplicatesIndex.NAME, hash, null, this, GlobalSearchScope.projectScope(project)); } @@ -229,7 +240,7 @@ public class DuplicatesInspectionBase extends LocalInspectionTool { reportedOffsetInOtherFiles.put(fragmentStartOffsetInteger, value); reportedPsi.put(fragmentStartOffsetInteger, target); fragmentSize.put(fragmentStartOffsetInteger, newFragmentSize); - + if (newFragmentSize >= MIN_FRAGMENT_SIZE) fragmentHash.put(fragmentStartOffsetInteger, myHash); return false; } return true; diff --git a/platform/lang-api/src/com/intellij/execution/CantRunException.java b/platform/lang-api/src/com/intellij/execution/CantRunException.java index b40ac4d63b68..f11032460d9b 100644 --- a/platform/lang-api/src/com/intellij/execution/CantRunException.java +++ b/platform/lang-api/src/com/intellij/execution/CantRunException.java @@ -24,6 +24,10 @@ public class CantRunException extends ExecutionException { super(message); } + public CantRunException(String s, Throwable cause) { + super(s, cause); + } + public static CantRunException noModuleConfigured(final String moduleName) { if (moduleName.trim().length() == 0) { return new CantRunException(ExecutionBundle.message("no.module.defined.error.message")); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonCodeAnalyzerImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonCodeAnalyzerImpl.java index b4c00b6a7735..094a1fe6ab81 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonCodeAnalyzerImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonCodeAnalyzerImpl.java @@ -42,9 +42,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.RangeMarker; -import com.intellij.openapi.editor.colors.EditorColorsListener; import com.intellij.openapi.editor.colors.EditorColorsManager; -import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.ex.RangeHighlighterEx; import com.intellij.openapi.editor.impl.DocumentMarkupModel; import com.intellij.openapi.editor.markup.MarkupModel; @@ -662,6 +660,9 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzerEx implements Pers } ApplicationManager.getApplication().assertIsDispatchThread(); hideLastIntentionHint(); + + if (editor.getCaretModel().getCaretCount() > 1) return; + IntentionHintComponent hintComponent = IntentionHintComponent.showIntentionHint(project, file, editor, intentions, false); if (hasToRecreate) { hintComponent.recreate(); diff --git a/platform/lang-impl/src/com/intellij/execution/util/ScriptFileUtil.java b/platform/lang-impl/src/com/intellij/execution/util/ScriptFileUtil.java new file mode 100644 index 000000000000..cc453433965d --- /dev/null +++ b/platform/lang-impl/src/com/intellij/execution/util/ScriptFileUtil.java @@ -0,0 +1,111 @@ +package com.intellij.execution.util; + +import com.intellij.execution.CantRunException; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.fileEditor.impl.LoadTextUtil; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.CharsetToolkit; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.PathUtil; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; + +public class ScriptFileUtil { + + private static final Logger LOG = Logger.getInstance(ScriptFileUtil.class); + + private static final String SCHEME = "mem://"; + private static final Map ourFilesMap = ContainerUtil.createConcurrentWeakValueMap(); + private static final AtomicLong ourFileCounter = new AtomicLong(); + + private ScriptFileUtil() {} + + public static boolean isMemoryScriptPath(@Nullable String url) { + return url != null && url.startsWith(SCHEME); + } + + public static String getScriptFilePath(@NotNull VirtualFile file) { + if (file.isInLocalFileSystem()) return file.getPath(); + + long id = ourFileCounter.incrementAndGet(); + String url = SCHEME + id + "/" + file.getName(); + ourFilesMap.put(url, file); + return url; + } + + @Nullable + public static VirtualFile findScriptFileByPath(@Nullable String path) { + if (StringUtil.isEmpty(path)) return null; + if (!path.startsWith(SCHEME)) { + return LocalFileSystem.getInstance().findFileByPath(path); + } + return ourFilesMap.get(path); + } + + @NotNull + public static String getLocalFilePath(@NotNull String scriptPath) throws CantRunException { + if (isMemoryScriptPath(scriptPath)) { + File tmpFile = copyToTempFile(scriptPath); + return tmpFile.getAbsolutePath(); + } + if (SystemInfo.isWindows) { + return PathUtil.driveLetterToLowerCase(scriptPath); + } + return scriptPath; + } + + @NotNull + public static File copyToTempFile(@NotNull String path) throws CantRunException { + VirtualFile virtualFile = findScriptFileByPath(path); + if (virtualFile == null) { + throw new CantRunException("File not found: " + path); + } + File ioFile; + try { + ioFile = FileUtil.createTempFile(virtualFile.getName(), "", true); + } + catch (IOException e) { + throw new CantRunException("Cannot create temporary file " + virtualFile.getName(), e); + } + try { + copyFile(virtualFile, ioFile); + return ioFile; + } + catch (IOException e) { + throw new CantRunException("Cannot write temp file " + virtualFile.getPath() + " to " + ioFile.getAbsolutePath(), e); + } + } + + private static void copyFile(@NotNull VirtualFile srcFile, @NotNull File destFile) throws IOException { + LOG.info("Copying to " + destFile.getPath()); + CharSequence content = getContent(srcFile); + CharBuffer cb = CharBuffer.wrap(content); + ByteBuffer bb = CharsetToolkit.UTF8_CHARSET.encode(cb); + byte[] result = new byte[bb.remaining()]; + bb.get(result); + FileUtil.writeToFile(destFile, result, false); + } + + @NotNull + private static CharSequence getContent(@NotNull VirtualFile file) { + Document document = FileDocumentManager.getInstance().getCachedDocument(file); + if (document != null) { + return document.getText(); + } + return LoadTextUtil.loadText(file); + } + +} diff --git a/platform/lang-impl/src/com/intellij/find/EditorSearchComponent.java b/platform/lang-impl/src/com/intellij/find/EditorSearchComponent.java index f30da0b39b4b..88b6124165f2 100644 --- a/platform/lang-impl/src/com/intellij/find/EditorSearchComponent.java +++ b/platform/lang-impl/src/com/intellij/find/EditorSearchComponent.java @@ -451,10 +451,15 @@ public class EditorSearchComponent extends EditorHeaderComponent implements Data nothingToSearchFor(); } if (mySearchField instanceof JTextArea) { - UIUtil.adjustRows((JTextArea)mySearchField, 2, 6); + adjustRows((JTextArea)mySearchField, 2, 6); } } + private static void adjustRows(JTextArea area, int minRows, int maxRows) { + area.setRows(Math.max(minRows, Math.min(maxRows, area.getText().split("\n").length))); + } + + public boolean isRegexp() { return myFindModel.isRegularExpressions(); } @@ -641,7 +646,7 @@ public class EditorSearchComponent extends EditorHeaderComponent implements Data setMatchesLimit(LivePreviewController.MATCHES_LIMIT); myFindModel.setStringToReplace(myReplaceField.getText()); if (myReplaceField instanceof JTextArea) { - UIUtil.adjustRows((JTextArea)myReplaceField, 2, 6); + adjustRows((JTextArea)myReplaceField, 2, 6); } } diff --git a/platform/lang-impl/src/com/intellij/ide/actions/SearchEverywhereAction.java b/platform/lang-impl/src/com/intellij/ide/actions/SearchEverywhereAction.java index 60dcc2ea2d06..4bcc12c7164f 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/SearchEverywhereAction.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/SearchEverywhereAction.java @@ -1565,7 +1565,7 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA private synchronized void buildSymbols(final String pattern) { - final SearchResult symbols = getSymbols(pattern, MAX_SYMBOLS, mySymbolsChooseByName); + final SearchResult symbols = getSymbols(pattern, MAX_SYMBOLS, showAll.get(), mySymbolsChooseByName); check(); if (symbols.size() > 0) { @@ -1670,7 +1670,7 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA } } - private SearchResult getSymbols(String pattern, final int max, ChooseByNamePopup chooseByNamePopup) { + private SearchResult getSymbols(String pattern, final int max, final boolean includeLibs, ChooseByNamePopup chooseByNamePopup) { final SearchResult symbols = new SearchResult(); if (!Registry.is("search.everywhere.symbols")) { return symbols; @@ -1678,7 +1678,7 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA final GlobalSearchScope scope = GlobalSearchScope.projectScope(project); if (chooseByNamePopup == null) return symbols; final ChooseByNameItemProvider provider = chooseByNamePopup.getProvider(); - provider.filterElements(chooseByNamePopup, pattern, false, + provider.filterElements(chooseByNamePopup, pattern, includeLibs, myProgressIndicator, new Processor() { @Override public boolean process(Object o) { @@ -1687,7 +1687,7 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA final PsiFile file = element.getContainingFile(); if (!myListModel.contains(o) && //some elements are non-physical like DB columns - (file == null || (file.getVirtualFile() != null && scope.accept(file.getVirtualFile())))) { + (file == null || (file.getVirtualFile() != null && (includeLibs || scope.accept(file.getVirtualFile()))))) { symbols.add(o); } } @@ -2087,7 +2087,7 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA = id == WidgetID.CLASSES ? getClasses(pattern, showAll.get(), DEFAULT_MORE_STEP_COUNT, myClassChooseByName) : id == WidgetID.FILES ? getFiles(pattern, DEFAULT_MORE_STEP_COUNT, myFileChooseByName) : id == WidgetID.RUN_CONFIGURATIONS ? getConfigurations(pattern, DEFAULT_MORE_STEP_COUNT) - : id == WidgetID.SYMBOLS ? getSymbols(pattern, DEFAULT_MORE_STEP_COUNT, mySymbolsChooseByName) + : id == WidgetID.SYMBOLS ? getSymbols(pattern, DEFAULT_MORE_STEP_COUNT, showAll.get(), mySymbolsChooseByName) : id == WidgetID.ACTIONS ? getActionsOrSettings(pattern, DEFAULT_MORE_STEP_COUNT, true) : id == WidgetID.SETTINGS ? getActionsOrSettings(pattern, DEFAULT_MORE_STEP_COUNT, false) : new SearchResult(); diff --git a/platform/lang-impl/src/com/intellij/ide/scratch/NewScratchFileAction.java b/platform/lang-impl/src/com/intellij/ide/scratch/NewScratchFileAction.java index d6cbd4e4cb17..a3bd3ba44473 100644 --- a/platform/lang-impl/src/com/intellij/ide/scratch/NewScratchFileAction.java +++ b/platform/lang-impl/src/com/intellij/ide/scratch/NewScratchFileAction.java @@ -23,9 +23,7 @@ import com.intellij.lang.StdLanguages; import com.intellij.lang.injection.InjectedLanguageManager; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Caret; -import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileTypes.LanguageFileType; @@ -39,7 +37,9 @@ import com.intellij.openapi.ui.popup.util.BaseListPopupStep; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.*; +import com.intellij.psi.LanguageSubstitutors; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; import com.intellij.testFramework.LightVirtualFile; import com.intellij.util.Consumer; import com.intellij.util.containers.ContainerUtil; @@ -73,7 +73,7 @@ public class NewScratchFileAction extends DumbAwareAction { @NotNull public static List getLastUsedLanguagesIds(Project project) { - String[] values = PropertiesComponent.getInstance(project).getValues(ScratchpadManager.class.getName()); + String[] values = PropertiesComponent.getInstance(project).getValues(ScratchFileService.class.getName()); return values == null ? ContainerUtil.emptyList() : ContainerUtil.list(values); } @@ -111,29 +111,18 @@ public class NewScratchFileAction extends DumbAwareAction { public Language getLanguageFromCaret(@NotNull Project project, @Nullable Editor editor, @Nullable PsiFile psiFile) { - if (editor == null) return null; - if (psiFile == null) return null; + if (editor == null || psiFile == null) return null; Caret caret = editor.getCaretModel().getPrimaryCaret(); int offset = caret.getOffset(); PsiElement element = InjectedLanguageManager.getInstance(project).findInjectedElementAt(psiFile, offset); - element = element == null ? psiFile.findElementAt(offset) : element; - Language language = element != null ? element.getLanguage() : psiFile.getLanguage(); - return substitute(project, language); + PsiFile file = element != null ? element.getContainingFile() : psiFile; + return file.getLanguage(); } public static void openNewFile(@NotNull Project project, @NotNull Language language, @NotNull final String text) { FeatureUsageTracker.getInstance().triggerFeatureUsed("scratch"); - VirtualFile file = ScratchpadManager.getInstance(project).createScratchFile(substitute(project, language)); - PsiFile psiFile = PsiManager.getInstance(project).findFile(file); - final Document document = psiFile == null ? null : PsiDocumentManager.getInstance(project).getDocument(psiFile); - if (document != null && StringUtil.isNotEmpty(text)) { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - document.setText(text); - } - }); - } + VirtualFile file = ScratchFileService.getInstance().createScratchFile(project, language, text); + if (file == null) return; FileEditorManager.getInstance(project).openFile(file, true); } diff --git a/platform/lang-impl/src/com/intellij/ide/scratch/ScratchFileService.java b/platform/lang-impl/src/com/intellij/ide/scratch/ScratchFileService.java new file mode 100644 index 000000000000..f6f0e2ae410f --- /dev/null +++ b/platform/lang-impl/src/com/intellij/ide/scratch/ScratchFileService.java @@ -0,0 +1,91 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.ide.scratch; + +import com.intellij.lang.Language; +import com.intellij.lang.PerFileMappings; +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Map; + +public abstract class ScratchFileService { + + public static final RootType SCRATCHES = RootType.newRootType("scratches", "Scratches"); + + public static ScratchFileService getInstance(@NotNull Project project) { + return ServiceManager.getService(project, ScratchFileService.class); + } + + public static ScratchFileService getInstance() { + return ServiceManager.getService(ScratchFileService.class); + } + + @NotNull + public abstract String getRootPath(@NotNull RootType rootType); + + public abstract boolean isFileInRoot(@NotNull VirtualFile file, @NotNull RootType rootType); + + @Nullable + public abstract VirtualFile createScratchFile(@NotNull Project project, @NotNull Language language, @NotNull String initialContent); + + @NotNull + public abstract PerFileMappings getScratchesMapping(); + + public static class RootType { + private static final Map ourInstances = ContainerUtil.newLinkedHashMap(); + + private final String myId; + private final String myDisplayName; + + private RootType(@NotNull String id, @Nullable String displayName) { + myId = id; + myDisplayName = displayName; + } + + @NotNull + public String getId() { + return myId; + } + + @Nullable + public String getDisplayName() { + return myDisplayName; + } + + public boolean isHidden() { + return myDisplayName == null; + } + + public static synchronized RootType newRootType(String id, String displayName) { + RootType rootType = new RootType(id, displayName); + RootType prev = ourInstances.put(id, rootType); + if (prev != null) { + throw new AssertionError(String.format("rootType '%s' already registered", id)); + } + return rootType; + } + + public static synchronized List getAllRootTypes() { + return ContainerUtil.newArrayList(ourInstances.values()); + } + } +} diff --git a/platform/lang-impl/src/com/intellij/ide/scratch/ScratchFileServiceImpl.java b/platform/lang-impl/src/com/intellij/ide/scratch/ScratchFileServiceImpl.java new file mode 100644 index 000000000000..dc9102cd1337 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/ide/scratch/ScratchFileServiceImpl.java @@ -0,0 +1,338 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.ide.scratch; + +import com.intellij.icons.AllIcons; +import com.intellij.ide.FileIconProvider; +import com.intellij.ide.navigationToolbar.AbstractNavBarModelExtension; +import com.intellij.lang.Language; +import com.intellij.lang.LanguageUtil; +import com.intellij.lang.PerFileMappings; +import com.intellij.lang.PerFileMappingsBase; +import com.intellij.openapi.application.PathManager; +import com.intellij.openapi.application.Result; +import com.intellij.openapi.application.RunResult; +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.components.PersistentStateComponent; +import com.intellij.openapi.components.State; +import com.intellij.openapi.components.Storage; +import com.intellij.openapi.components.StoragePathMacros; +import com.intellij.openapi.fileEditor.impl.NonProjectFileWritingAccessExtension; +import com.intellij.openapi.fileTypes.*; +import com.intellij.openapi.fileTypes.ex.FileTypeIdentifiableByVirtualFile; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.Iconable; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.wm.IdeFrame; +import com.intellij.openapi.wm.StatusBar; +import com.intellij.openapi.wm.WindowManager; +import com.intellij.openapi.wm.WindowManagerListener; +import com.intellij.psi.LanguageSubstitutor; +import com.intellij.psi.LanguageSubstitutors; +import com.intellij.ui.LayeredIcon; +import com.intellij.ui.UIBundle; +import com.intellij.util.ObjectUtils; +import com.intellij.util.PathUtil; +import com.intellij.util.containers.ContainerUtil; +import org.jdom.Element; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.util.Collection; +import java.util.List; + + +public abstract class ScratchFileServiceImpl extends ScratchFileService { + + public static final LanguageFileType SCRATCH_FILE_TYPE = new MyFileType(); + + @State( + name = "ScratchFileService", + storages = { + @Storage(file = StoragePathMacros.APP_CONFIG + "/scratches.xml") + }) + public static class App extends ScratchFileServiceImpl implements PersistentStateComponent { + + private final MyLanguages myScratchMapping = new MyLanguages(); + + @NotNull + @Override + public String getRootPath(@NotNull RootType rootType) { + return FileUtil.toSystemIndependentName(PathManager.getConfigPath()) + "/" + rootType.getId(); + } + + public App(WindowManager windowManager) { + WindowManagerListener listener = new WindowManagerListener() { + @Override + public void frameCreated(IdeFrame frame) { + Project project = frame.getProject(); + StatusBar statusBar = frame.getStatusBar(); + if (project == null || statusBar == null || statusBar.getWidget(ScratchWidget.WIDGET_ID) != null) return; + ScratchWidget widget = new ScratchWidget(project); + statusBar.addWidget(widget, "before Encoding", project); + statusBar.updateWidget(ScratchWidget.WIDGET_ID); + } + + @Override + public void beforeFrameReleased(IdeFrame frame) { + } + }; + for (IdeFrame frame : windowManager.getAllProjectFrames()) { + listener.frameCreated(frame); + } + windowManager.addListener(listener); + } + + @NotNull + @Override + public PerFileMappings getScratchesMapping() { + return myScratchMapping; + } + + @Nullable + @Override + public Element getState() { + return myScratchMapping.getState(); + } + + @Override + public void loadState(Element state) { + myScratchMapping.loadState(state); + } + } + + private static class MyLanguages extends PerFileMappingsBase { + @Override + protected List getAvailableValues() { + return LanguageUtil.getFileLanguages(); + } + + @Nullable + @Override + protected String serialize(Language language) { + return language.getID(); + } + + @Nullable + @Override + protected Language handleUnknownMapping(VirtualFile file, String value) { + return PlainTextLanguage.INSTANCE; + } + + @Nullable + @Override + public Language getMapping(@Nullable VirtualFile file) { + Language language = super.getMapping(file); + if (language == null && file != null && file.getFileType() == SCRATCH_FILE_TYPE) { + String extension = file.getExtension(); + FileType fileType = extension == null ? null : FileTypeManager.getInstance().getFileTypeByExtension(extension); + language = fileType instanceof LanguageFileType ? ((LanguageFileType)fileType).getLanguage() : null; + } + return language; + } + } + + + public static class Prj extends ScratchFileServiceImpl { + + private final Project myProject; + + public Prj(@NotNull Project project) { + myProject = project; + } + + @NotNull + protected Project getProject() { + return myProject; + } + + @NotNull + @Override + public String getRootPath(@NotNull RootType rootType) { + if (rootType == SCRATCHES) return ScratchFileService.getInstance().getRootPath(rootType); + return FileUtil.toSystemIndependentName(StringUtil.notNullize(PathUtil.getParentPath(myProject.getProjectFilePath()))) + "/" + rootType.getId(); + } + + @Nullable + @Override + public VirtualFile createScratchFile(@NotNull Project project, @NotNull Language language, @NotNull String initialContent) { + return ScratchFileService.getInstance().createScratchFile(project, language, initialContent); + } + + @NotNull + @Override + public PerFileMappings getScratchesMapping() { + return ScratchFileService.getInstance().getScratchesMapping(); + } + } + + public static class TypeFactory extends FileTypeFactory { + + @Override + public void createFileTypes(@NotNull FileTypeConsumer consumer) { + consumer.consume(SCRATCH_FILE_TYPE); + } + } + + + public static class Substitutor extends LanguageSubstitutor { + @Nullable + @Override + public Language getLanguage(@NotNull VirtualFile file, @NotNull Project project) { + if (file.getFileType() != SCRATCH_FILE_TYPE) return null; + PerFileMappings mapping = ScratchFileService.getInstance().getScratchesMapping(); + Language language = mapping.getMapping(file); + return language != null && language != SCRATCH_FILE_TYPE.getLanguage() ? + LanguageSubstitutors.INSTANCE.substituteLanguage(language, file, project) : language; + } + } + + public static class Highlighter implements SyntaxHighlighterProvider { + @Override + @Nullable + public SyntaxHighlighter create(@NotNull FileType fileType, @Nullable Project project, @Nullable VirtualFile file) { + if (fileType == SCRATCH_FILE_TYPE && project != null && file != null) { + PerFileMappings mapping = ScratchFileService.getInstance().getScratchesMapping(); + Language language = mapping.getMapping(file); + return language != null ? SyntaxHighlighterFactory.getSyntaxHighlighter(language, project, file) : null; + } + return null; + } + } + + public static class IconProvider implements FileIconProvider { + + @Nullable + @Override + public Icon getIcon(@NotNull VirtualFile file, @Iconable.IconFlags int flags, @Nullable Project project) { + if (project == null || file.getFileType() != SCRATCH_FILE_TYPE) return null; + PerFileMappings mapping = ScratchFileService.getInstance().getScratchesMapping(); + Language language = ObjectUtils.notNull(mapping.getMapping(file), SCRATCH_FILE_TYPE.getLanguage()); + LanguageFileType fileType = language.getAssociatedFileType(); + return fileType == null ? null : LayeredIcon.create(fileType.getIcon(), AllIcons.Actions.Scratch); + } + } + + public static class AccessExtension implements NonProjectFileWritingAccessExtension { + + @Override + public boolean isWritable(@NotNull VirtualFile file) { + return file.getFileType() == SCRATCH_FILE_TYPE; + } + } + + public static class NavBarExtension extends AbstractNavBarModelExtension { + + @Nullable + @Override + public String getPresentableText(Object object) { + return null; + } + + @NotNull + @Override + public Collection additionalRoots(Project project) { + String path = ScratchFileService.getInstance().getRootPath(SCRATCHES); + VirtualFile root = LocalFileSystem.getInstance().findFileByPath(path); + return ContainerUtil.createMaybeSingletonList(root); + } + } + + @Nullable + @Override + public VirtualFile createScratchFile(@NotNull Project project, @NotNull final Language language, @NotNull final String initialContent) { + RunResult result = + new WriteCommandAction(project, UIBundle.message("file.chooser.create.new.file.command.name")) { + @Override + protected void run(@NotNull Result result) throws Throwable { + VirtualFile dir = VfsUtil.createDirectories(getRootPath(SCRATCHES)); + VirtualFile file = VfsUtil.createChildSequent(LocalFileSystem.getInstance(), dir, "scratch", ""); + getScratchesMapping().setMapping(file, language); + VfsUtil.saveText(file, initialContent); + result.setResult(file); + } + }.execute(); + if (result.hasException()) { + Messages.showMessageDialog(UIBundle.message("create.new.file.could.not.create.file.error.message", "scratch"), + UIBundle.message("error.dialog.title"), Messages.getErrorIcon()); + return null; + } + return result.getResultObject(); + } + + @Override + public boolean isFileInRoot(@NotNull VirtualFile file, @NotNull RootType rootType) { + return rootType == SCRATCHES ? file.getFileType() == SCRATCH_FILE_TYPE : isFileInRootImpl(file, rootType); + } + + private static boolean isFileInRootImpl(@NotNull VirtualFile file, RootType scratches) { + String rootPath = ScratchFileService.getInstance().getRootPath(scratches); + return file.getPath().startsWith(rootPath); + } + + private static class MyFileType extends LanguageFileType implements FileTypeIdentifiableByVirtualFile, InternalFileType { + + MyFileType() { + super(PlainTextLanguage.INSTANCE); + } + + @Override + public boolean isMyFileType(@NotNull VirtualFile file) { + return isFileInRootImpl(file, SCRATCHES); + } + + @NotNull + @Override + public String getName() { + return "Scratch"; + } + + @NotNull + @Override + public String getDescription() { + return "Scratch"; + } + + @NotNull + @Override + public String getDefaultExtension() { + return ""; + } + + @Nullable + @Override + public Icon getIcon() { + return PlainTextFileType.INSTANCE.getIcon(); + } + + @Override + public boolean isReadOnly() { + return true; + } + + @Nullable + @Override + public String getCharset(@NotNull VirtualFile file, @NotNull byte[] content) { + return null; + } + } +} \ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/ide/scratch/ScratchProjectViewPane.java b/platform/lang-impl/src/com/intellij/ide/scratch/ScratchProjectViewPane.java new file mode 100644 index 000000000000..e1098c107e7f --- /dev/null +++ b/platform/lang-impl/src/com/intellij/ide/scratch/ScratchProjectViewPane.java @@ -0,0 +1,218 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.ide.scratch; + +import com.intellij.ide.SelectInTarget; +import com.intellij.ide.impl.ProjectViewSelectInTarget; +import com.intellij.ide.projectView.PresentationData; +import com.intellij.ide.projectView.TreeStructureProvider; +import com.intellij.ide.projectView.ViewSettings; +import com.intellij.ide.projectView.impl.ProjectAbstractTreeStructureBase; +import com.intellij.ide.projectView.impl.ProjectTreeStructure; +import com.intellij.ide.projectView.impl.ProjectViewPane; +import com.intellij.ide.projectView.impl.nodes.BasePsiNode; +import com.intellij.ide.util.treeView.AbstractTreeNode; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.pom.NavigatableWithText; +import com.intellij.psi.PsiDirectory; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFileSystemItem; +import com.intellij.psi.PsiManager; +import com.intellij.psi.search.PsiElementProcessor; +import com.intellij.util.PlatformIcons; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * @author gregsh + */ +public class ScratchProjectViewPane extends ProjectViewPane { + + public static final String ID = "Scratches"; + + public ScratchProjectViewPane(Project project) { + super(project); + } + + @Override + public String getTitle() { + return "Scratches"; + } + + @Override + public Icon getIcon() { + return super.getIcon(); + } + + @NotNull + @Override + public String getId() { + return ID; + } + + @Override + protected ProjectAbstractTreeStructureBase createStructure() { + return new MyTreeStructure(myProject); + } + + @Override + public int getWeight() { + return 11; + } + + @Override + public SelectInTarget createSelectInTarget() { + return new ProjectViewSelectInTarget(myProject) { + @Override + public String toString() { + return getTitle(); + } + + @Override + public String getMinorViewId() { + return getId(); + } + + @Override + public float getWeight() { + return ScratchProjectViewPane.this.getWeight(); + } + }; + } + + @Nullable + @Override + protected PsiElement getPSIElement(@Nullable Object element) { + return element instanceof ScratchFileService.RootType ? getDirectory(myProject, (ScratchFileService.RootType)element) : super.getPSIElement(element); + } + + @Nullable + private static PsiDirectory getDirectory(@NotNull Project project, @NotNull ScratchFileService.RootType rootType) { + String path = ScratchFileService.getInstance().getRootPath(rootType); + VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByPath(path); + return virtualFile == null ? null : PsiManager.getInstance(project).findDirectory(virtualFile); + } + + private static class MyTreeStructure extends ProjectTreeStructure { + + MyTreeStructure(final Project project) { + super(project, ID); + } + + @Override + protected AbstractTreeNode createRoot(Project project, ViewSettings settings) { + return new MyProjectNode(project); + } + + @Nullable + @Override + public List getProviders() { + return null; + } + + } + + private static class MyProjectNode extends AbstractTreeNode { + MyProjectNode(Project project) { + super(project, project); + } + + @NotNull + @Override + public Collection getChildren() { + List list = ContainerUtil.newArrayList(); + for (ScratchFileService.RootType rootType : ScratchFileService.RootType.getAllRootTypes()) { + if (rootType.isHidden()) continue; + list.add(new MyRootNode(getProject(), rootType)); + } + return list; + } + + @Override + protected void update(PresentationData presentation) { + } + } + + private static class MyRootNode extends AbstractTreeNode { + + MyRootNode(Project project, ScratchFileService.RootType type) { + super(project, type); + } + + @NotNull + @Override + public Collection getChildren() { + ScratchFileService.RootType rootType = getValue(); + PsiDirectory directory = getDirectory(getProject(), rootType); + if (directory == null) return Collections.emptyList(); + return new MyPsiNode(getProject(), directory).getChildren(); + } + + @Override + protected void update(PresentationData presentation) { + presentation.setIcon(PlatformIcons.DIRECTORY_CLOSED_ICON); + presentation.setPresentableText(getValue().getDisplayName()); + } + } + + private static class MyPsiNode extends BasePsiNode implements NavigatableWithText { + + MyPsiNode(Project project, PsiFileSystemItem value) { + super(project, value, ViewSettings.DEFAULT); + } + + @Override + public boolean isAlwaysLeaf() { + return !getValue().isDirectory(); + } + + @Nullable + @Override + protected Collection getChildrenImpl() { + if (isAlwaysLeaf()) return Collections.emptyList(); + final List list = ContainerUtil.newArrayList(); + getValue().processChildren(new PsiElementProcessor() { + @Override + public boolean execute(@NotNull PsiFileSystemItem element) { + list.add(new MyPsiNode(getProject(), element)); + return true; + } + }); + return list; + } + + @Override + protected void updateImpl(PresentationData data) { + PsiFileSystemItem value = getValue(); + data.setIcon(value.getIcon(0)); + data.setPresentableText(value.getName()); + } + + @Nullable + @Override + public String getNavigateActionText(boolean focusEditor) { + return null; + } + } +} diff --git a/platform/lang-impl/src/com/intellij/ide/scratch/ScratchWidget.java b/platform/lang-impl/src/com/intellij/ide/scratch/ScratchWidget.java index 649516d97aad..e5f7577fe43f 100644 --- a/platform/lang-impl/src/com/intellij/ide/scratch/ScratchWidget.java +++ b/platform/lang-impl/src/com/intellij/ide/scratch/ScratchWidget.java @@ -17,6 +17,7 @@ package com.intellij.ide.scratch; import com.intellij.icons.AllIcons; import com.intellij.lang.Language; +import com.intellij.lang.PerFileMappings; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.FileEditorManagerEvent; @@ -24,11 +25,14 @@ import com.intellij.openapi.fileTypes.LanguageFileType; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.ListPopup; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileManager; +import com.intellij.openapi.vfs.newvfs.BulkFileListener; +import com.intellij.openapi.vfs.newvfs.events.VFileEvent; import com.intellij.openapi.wm.CustomStatusBarWidget; import com.intellij.openapi.wm.StatusBarWidget; import com.intellij.openapi.wm.impl.status.EditorBasedWidget; import com.intellij.openapi.wm.impl.status.TextPanel; -import com.intellij.testFramework.LightVirtualFile; +import com.intellij.psi.LanguageSubstitutors; import com.intellij.ui.ClickListener; import com.intellij.ui.awt.RelativePoint; import com.intellij.util.Consumer; @@ -39,6 +43,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; import java.awt.event.MouseEvent; +import java.util.List; class ScratchWidget extends EditorBasedWidget implements CustomStatusBarWidget.Multiframe, CustomStatusBarWidget { static final String WIDGET_ID = "Scratch"; @@ -50,16 +55,16 @@ class ScratchWidget extends EditorBasedWidget implements CustomStatusBarWidget.M new ClickListener() { @Override public boolean onClick(@NotNull MouseEvent e, int clickCount) { - final Project project = getProject(); + Project project = getProject(); Editor editor = getEditor(); - final LightVirtualFile selectedFile = getScratchFile(); - if (project == null || editor == null || selectedFile == null) return false; + final VirtualFile file = getSelectedFile(); + if (project == null || editor == null || file == null) return false; + final PerFileMappings fileService = ScratchFileService.getInstance().getScratchesMapping(); - ListPopup popup = NewScratchFileAction.buildLanguagePopup(project, selectedFile.getLanguage(), new Consumer() { + ListPopup popup = NewScratchFileAction.buildLanguagePopup(project, fileService.getMapping(file), new Consumer() { @Override public void consume(Language language) { - selectedFile.setLanguage(NewScratchFileAction.substitute(project, language)); - FileContentUtilCore.reparseFiles(selectedFile); + fileService.setMapping(file, language); update(); } }); @@ -70,6 +75,17 @@ class ScratchWidget extends EditorBasedWidget implements CustomStatusBarWidget.M return true; } }.installOn(myPanel); + myConnection.subscribe(VirtualFileManager.VFS_CHANGES, new BulkFileListener.Adapter() { + @Override + public void after(@NotNull List events) { + for (VFileEvent event : events) { + if (event.getRequestor() == FileContentUtilCore.FORCE_RELOAD_REQUESTOR) { + update(); + break; + } + } + } + }); } @NotNull @@ -85,9 +101,16 @@ class ScratchWidget extends EditorBasedWidget implements CustomStatusBarWidget.M } private void update() { - LightVirtualFile file = getScratchFile(); - if (file != null) { - Language lang = file.getLanguage(); + Project project = getProject(); + if (project == null) return; + VirtualFile file = getSelectedFile(); + if (file == null) return; + ScratchFileService fileService = ScratchFileService.getInstance(); + if (fileService.isFileInRoot(file, ScratchFileService.SCRATCHES)) { + Language lang = fileService.getScratchesMapping().getMapping(file); + if (lang == null) { + lang = LanguageSubstitutors.INSTANCE.substituteLanguage(((LanguageFileType)file.getFileType()).getLanguage(), file, project); + } myPanel.setText(lang.getDisplayName()); myPanel.setBorder(WidgetBorder.INSTANCE); myPanel.setIcon(getDefaultIcon(lang)); @@ -102,12 +125,6 @@ class ScratchWidget extends EditorBasedWidget implements CustomStatusBarWidget.M } } - @Nullable - private LightVirtualFile getScratchFile() { - VirtualFile file = getSelectedFile(); - return file instanceof LightVirtualFile && file.getFileSystem() instanceof ScratchpadFileSystem ? (LightVirtualFile)file : null; - } - @Override public StatusBarWidget copy() { return new ScratchWidget(myProject); diff --git a/platform/lang-impl/src/com/intellij/ide/scratch/ScratchpadFileSystem.java b/platform/lang-impl/src/com/intellij/ide/scratch/ScratchpadFileSystem.java deleted file mode 100644 index e2c906bbb45b..000000000000 --- a/platform/lang-impl/src/com/intellij/ide/scratch/ScratchpadFileSystem.java +++ /dev/null @@ -1,115 +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.intellij.ide.scratch; - -import com.intellij.icons.AllIcons; -import com.intellij.ide.presentation.Presentation; -import com.intellij.ide.presentation.PresentationProvider; -import com.intellij.lang.Language; -import com.intellij.openapi.util.Condition; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileManager; -import com.intellij.openapi.vfs.VirtualFileSystem; -import com.intellij.openapi.vfs.ex.dummy.DummyFileSystem; -import com.intellij.testFramework.LightVirtualFile; -import com.intellij.ui.LayeredIcon; -import com.intellij.util.containers.ContainerUtil; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import java.util.List; -import java.util.Map; - -public class ScratchpadFileSystem extends DummyFileSystem { - private static final String PROTOCOL = "scratchpad"; - private final Map myCachedFiles = ContainerUtil.newHashMap(); - - public static ScratchpadFileSystem getScratchFileSystem() { - return (ScratchpadFileSystem)VirtualFileManager.getInstance().getFileSystem(PROTOCOL); - } - - public void removeByPrefix(@NotNull final String prefix) { - List toRemove = ContainerUtil.findAll(myCachedFiles.keySet(), new Condition() { - @Override - public boolean value(String s) { - return s.startsWith(prefix); - } - }); - for (String s : toRemove) { - myCachedFiles.remove(s); - } - } - - @Override - public VirtualFile findFileByPath(@NotNull String path) { - VirtualFile file = myCachedFiles.get(path); - if (file != null && file.isValid()) return file; - return null; - } - - @NotNull - public VirtualFile addFile(@NotNull String name, @NotNull Language language, @NotNull String prefix) { - VirtualFile file = new MyLightVirtualFile(name, language, prefix); - myCachedFiles.put(file.getPath(), file); - return file; - } - - @NotNull - @Override - public String getProtocol() { - return PROTOCOL; - } - - @NotNull - @Override - public String extractPresentableUrl(@NotNull String path) { - return calcSuffix(findFileByPath(path)); - } - - private static String calcSuffix(@Nullable VirtualFile file) { - return file instanceof LightVirtualFile ? ((LightVirtualFile)file).getLanguage().getDisplayName() : "Unknown language"; - } - - @Presentation(provider = ScratchPresentation.class) - public static class MyLightVirtualFile extends LightVirtualFile { - private final String myPrefix; - - public MyLightVirtualFile(@NotNull String fileName, @NotNull Language language, @NotNull String projectPrefix) { - super(fileName, language, ""); - myPrefix = projectPrefix; - } - - @NotNull - @Override - public VirtualFileSystem getFileSystem() { - return getScratchFileSystem(); - } - - @NotNull - @Override - public String getPath() { - return myPrefix + super.getPath(); - } - } - - public static class ScratchPresentation extends PresentationProvider { - @Override - public Icon getIcon(@NotNull LightVirtualFile file) { - return LayeredIcon.create(file.getFileType().getIcon(), AllIcons.Actions.New); - } - } -} diff --git a/platform/lang-impl/src/com/intellij/ide/scratch/ScratchpadIconProvider.java b/platform/lang-impl/src/com/intellij/ide/scratch/ScratchpadIconProvider.java deleted file mode 100644 index a86fc6e0c8fe..000000000000 --- a/platform/lang-impl/src/com/intellij/ide/scratch/ScratchpadIconProvider.java +++ /dev/null @@ -1,38 +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.intellij.ide.scratch; - -import com.intellij.icons.AllIcons; -import com.intellij.ide.FileIconProvider; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Iconable; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.ui.LayeredIcon; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; - -public class ScratchpadIconProvider implements FileIconProvider { - @Nullable - @Override - public Icon getIcon(@NotNull VirtualFile file, @Iconable.IconFlags int flags, @Nullable Project project) { - if (file instanceof ScratchpadFileSystem.MyLightVirtualFile) { - return LayeredIcon.create(file.getFileType().getIcon(), AllIcons.Actions.Scratch); - } - return null; - } -} diff --git a/platform/lang-impl/src/com/intellij/ide/scratch/ScratchpadManager.java b/platform/lang-impl/src/com/intellij/ide/scratch/ScratchpadManager.java deleted file mode 100644 index e9125e4396c2..000000000000 --- a/platform/lang-impl/src/com/intellij/ide/scratch/ScratchpadManager.java +++ /dev/null @@ -1,31 +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.intellij.ide.scratch; - -import com.intellij.lang.Language; -import com.intellij.openapi.components.ServiceManager; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; -import org.jetbrains.annotations.NotNull; - -public abstract class ScratchpadManager { - public static ScratchpadManager getInstance(@NotNull Project project) { - return ServiceManager.getService(project, ScratchpadManager.class); - } - - @NotNull - public abstract VirtualFile createScratchFile(@NotNull Language language); -} diff --git a/platform/lang-impl/src/com/intellij/ide/scratch/ScratchpadManagerImpl.java b/platform/lang-impl/src/com/intellij/ide/scratch/ScratchpadManagerImpl.java deleted file mode 100644 index c7dc4462475a..000000000000 --- a/platform/lang-impl/src/com/intellij/ide/scratch/ScratchpadManagerImpl.java +++ /dev/null @@ -1,94 +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.intellij.ide.scratch; - -import com.intellij.ide.util.PropertiesComponent; -import com.intellij.lang.Language; -import com.intellij.openapi.Disposable; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.fileEditor.FileEditorManagerListener; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Computable; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.wm.StatusBar; -import com.intellij.openapi.wm.WindowManager; -import com.intellij.util.ArrayUtil; -import com.intellij.util.containers.ContainerUtil; -import org.jetbrains.annotations.NotNull; - -import java.util.List; - -public class ScratchpadManagerImpl extends ScratchpadManager implements Disposable { - private final Project myProject; - private Integer myIndex = 0; - - public ScratchpadManagerImpl(@NotNull Project project) { - myProject = project; - StatusBar statusBar = WindowManager.getInstance().getStatusBar(myProject); - if (statusBar == null) return; - if (statusBar.getWidget(ScratchWidget.WIDGET_ID) != null) return; - ScratchWidget widget = new ScratchWidget(myProject); - statusBar.addWidget(widget, "before Encoding", myProject); - statusBar.updateWidget(ScratchWidget.WIDGET_ID); - project.getMessageBus().connect(project).subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, widget); - } - - @NotNull - @Override - public VirtualFile createScratchFile(@NotNull final Language language) { - updateHistory(myProject, language); - - return ApplicationManager.getApplication().runWriteAction(new Computable() { - @Override - public VirtualFile compute() { - String name = generateFileName(); - return ScratchpadFileSystem.getScratchFileSystem().addFile(name, language, calculatePrefix(ScratchpadManagerImpl.this.myProject)); - } - }); - } - - private static void updateHistory(Project project, Language language) { - String[] values = PropertiesComponent.getInstance(project).getValues(ScratchpadManager.class.getName()); - List lastUsed = ContainerUtil.newArrayListWithCapacity(5); - lastUsed.add(language.getID()); - if (values != null) { - for (String value : values) { - if (!lastUsed.contains(value)) { - lastUsed.add(value); - } - if (lastUsed.size() == 5) break; - } - } - PropertiesComponent.getInstance(project).setValues(ScratchpadManager.class.getName(), ArrayUtil.toStringArray(lastUsed)); - } - - @NotNull - private static String calculatePrefix(@NotNull Project project) { - return project.getLocationHash(); - } - - @NotNull - private String generateFileName() { - int updated = myIndex++; - String index = updated == 0 ? "" : "." + updated; - return "scratch" + index; - } - - @Override - public void dispose() { - ScratchpadFileSystem.getScratchFileSystem().removeByPrefix(calculatePrefix(myProject)); - } -} \ No newline at end of file diff --git a/platform/platform-api/src/com/intellij/openapi/editor/actionSystem/EditorAction.java b/platform/platform-api/src/com/intellij/openapi/editor/actionSystem/EditorAction.java index d93a508f97bd..e5b6abde7bbb 100644 --- a/platform/platform-api/src/com/intellij/openapi/editor/actionSystem/EditorAction.java +++ b/platform/platform-api/src/com/intellij/openapi/editor/actionSystem/EditorAction.java @@ -21,6 +21,7 @@ import com.intellij.openapi.command.UndoConfirmationPolicy; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.Project; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -148,7 +149,10 @@ public abstract class EditorAction extends AnAction implements DumbAware { @Override public Object getData(String dataId) { if (PROJECT.is(dataId)) { - return editor.getProject(); + final Project project = editor.getProject(); + if (project != null) { + return project; + } } return original.getData(dataId); } diff --git a/platform/platform-api/src/com/intellij/openapi/ui/Messages.java b/platform/platform-api/src/com/intellij/openapi/ui/Messages.java index 73667c2b21a9..66c028664897 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/Messages.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/Messages.java @@ -1673,7 +1673,11 @@ public class Messages { return myExitFunc.fun(exitCode, myCheckBox); } - return exitCode == OK_EXIT_CODE ? myCheckBox.isSelected() ? OK_EXIT_CODE : CANCEL_EXIT_CODE : CANCEL_EXIT_CODE; + boolean checkBoxSelected = (myCheckBox != null && myCheckBox.isSelected()); + + boolean okExitCode = (exitCode == OK_EXIT_CODE); + + return checkBoxSelected && okExitCode ? OK_EXIT_CODE : CANCEL_EXIT_CODE; } @Override diff --git a/platform/platform-api/src/com/intellij/openapi/vfs/VfsUtil.java b/platform/platform-api/src/com/intellij/openapi/vfs/VfsUtil.java index 14c9d319b2d5..371b2def9ac9 100644 --- a/platform/platform-api/src/com/intellij/openapi/vfs/VfsUtil.java +++ b/platform/platform-api/src/com/intellij/openapi/vfs/VfsUtil.java @@ -26,10 +26,7 @@ import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.newvfs.NewVirtualFile; -import com.intellij.util.ArrayUtil; -import com.intellij.util.Function; -import com.intellij.util.Processor; -import com.intellij.util.SystemProperties; +import com.intellij.util.*; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.Convertor; import com.intellij.util.lang.UrlClassLoader; @@ -435,10 +432,11 @@ public class VfsUtil extends VfsUtilCore { } public static VirtualFile createChildSequent(Object requestor, @NotNull VirtualFile dir, @NotNull String prefix, @NotNull String extension) throws IOException { - String fileName = prefix + "." + extension; + String dotExt = PathUtil.makeFileName("", extension); + String fileName = prefix + dotExt; int i = 1; while (dir.findChild(fileName) != null) { - fileName = prefix + i + "." + extension; + fileName = prefix + i + dotExt; i++; } return dir.createChildData(requestor, fileName); diff --git a/platform/platform-api/src/com/intellij/ui/components/JBViewport.java b/platform/platform-api/src/com/intellij/ui/components/JBViewport.java index 15a4966a931f..c990d17bee2a 100644 --- a/platform/platform-api/src/com/intellij/ui/components/JBViewport.java +++ b/platform/platform-api/src/com/intellij/ui/components/JBViewport.java @@ -66,6 +66,8 @@ public class JBViewport extends JViewport implements ZoomableViewport { }; private StatusText myEmptyText; + private boolean myPaintingNow; + private ZoomingDelegate myZoomer; private Dimension myTempViewSize; @@ -112,6 +114,7 @@ public class JBViewport extends JViewport implements ZoomableViewport { @Override public void paint(Graphics g) { + myPaintingNow = true; if (myZoomer != null && myZoomer.isActive()) { myZoomer.paint(g); } @@ -122,6 +125,7 @@ public class JBViewport extends JViewport implements ZoomableViewport { myEmptyText.paint(this, g); } } + myPaintingNow = false; } @Nullable @@ -147,4 +151,8 @@ public class JBViewport extends JViewport implements ZoomableViewport { public void magnify(double magnification) { myZoomer.magnify(magnification); } + + public boolean isPaintingNow() { + return myPaintingNow; + } } diff --git a/platform/platform-api/src/com/intellij/util/ui/StatusText.java b/platform/platform-api/src/com/intellij/util/ui/StatusText.java index 479b968d4317..99e3046a8bed 100644 --- a/platform/platform-api/src/com/intellij/util/ui/StatusText.java +++ b/platform/platform-api/src/com/intellij/util/ui/StatusText.java @@ -20,6 +20,8 @@ import com.intellij.ui.ClickListener; import com.intellij.ui.SimpleColoredComponent; import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.UIBundle; +import com.intellij.ui.components.JBViewport; +import com.intellij.util.ObjectUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -186,13 +188,37 @@ public abstract class StatusText { } public void paint(Component owner, Graphics g) { - boolean wrongComponent = owner != myOwner && owner != null && owner.getParent() != myOwner; - if (!isStatusVisible() || wrongComponent) return; + if (!isStatusVisible()) return; - Rectangle b = getTextComponentBound(); - myComponent.setBounds(0, 0, b.width, b.height); + if (owner == myOwner) { + doPaintStatusText(g, getTextComponentBound()); + } + else { + paintOnComponentUnderViewport(owner, g); + } + } - Graphics2D g2 = (Graphics2D)g.create(b.x, b.y, b.width, b.height); + private void paintOnComponentUnderViewport(Component component, Graphics g) { + JBViewport viewport = ObjectUtils.tryCast(myOwner, JBViewport.class); + if (viewport == null || viewport.getView() != component || viewport.isPaintingNow()) return; + + // We're painting a component which has a viewport as it's ancestor. + // As the viewport paints status text, we'll erase it, so we need to schedule a repaint for the viewport with status text's bounds. + // But it causes flicker, so we paint status text over the component first and then schedule the viewport repaint. + + Rectangle textBoundsInViewport = getTextComponentBound(); + + int xInOwner = textBoundsInViewport.x - component.getX(); + int yInOwner = textBoundsInViewport.y - component.getY(); + Rectangle textBoundsInOwner = new Rectangle(xInOwner, yInOwner, textBoundsInViewport.width, textBoundsInViewport.height); + doPaintStatusText(g, textBoundsInOwner); + + viewport.repaint(textBoundsInViewport); + } + + private void doPaintStatusText(Graphics g, Rectangle textComponentBounds) { + myComponent.setBounds(0, 0, textComponentBounds.width, textComponentBounds.height); + Graphics2D g2 = (Graphics2D)g.create(textComponentBounds.x, textComponentBounds.y, textComponentBounds.width, textComponentBounds.height); myComponent.paint(g2); g2.dispose(); } 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 55f49157d4c0..65351debe42d 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 @@ -1161,7 +1161,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi @Override @NotNull public VisualPosition xyToVisualPosition(@NotNull Point p) { - int line = yPositionToVisibleLine(p.y); + int line = yPositionToVisibleLine(Math.max(p.y, 0)); int px = p.x; if (line == 0 && myPrefixText != null) { px -= myPrefixWidthInPixels; diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileDocumentManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileDocumentManagerImpl.java index af229d1abb18..fd3ecf9c8fe2 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileDocumentManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileDocumentManagerImpl.java @@ -55,7 +55,6 @@ import com.intellij.openapi.util.Key; import com.intellij.openapi.util.io.FileUtilRt; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.*; -import com.intellij.openapi.vfs.ex.dummy.DummyFileSystem; import com.intellij.openapi.vfs.newvfs.NewVirtualFileSystem; import com.intellij.pom.core.impl.PomModelImpl; import com.intellij.psi.ExternalChangeAction; @@ -190,7 +189,7 @@ public class FileDocumentManagerImpl extends FileDocumentManager implements Virt document.putUserData(FILE_KEY, file); } - if (!(file instanceof LightVirtualFile || file.getFileSystem() instanceof DummyFileSystem)) { + if (!(file instanceof LightVirtualFile || file.getFileSystem() instanceof NonPhysicalFileSystem)) { document.addDocumentListener( new DocumentAdapter() { @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/IdeSettingsDialog.java b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/IdeSettingsDialog.java index 4c047cdf2a66..5c720c413e00 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/IdeSettingsDialog.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/IdeSettingsDialog.java @@ -274,7 +274,7 @@ public class IdeSettingsDialog extends DialogWrapper implements DataProvider { if (OptionsEditor.KEY.is(dataId)) { return myEditor; } - return null; + return myEditor == null ? null : myEditor.getData(dataId); } private class ApplyAction extends AbstractAction { diff --git a/platform/platform-impl/src/com/intellij/openapi/ui/impl/GlassPaneDialogWrapperPeer.java b/platform/platform-impl/src/com/intellij/openapi/ui/impl/GlassPaneDialogWrapperPeer.java index 3a72b600a269..7717de4a616d 100644 --- a/platform/platform-impl/src/com/intellij/openapi/ui/impl/GlassPaneDialogWrapperPeer.java +++ b/platform/platform-impl/src/com/intellij/openapi/ui/impl/GlassPaneDialogWrapperPeer.java @@ -131,8 +131,19 @@ public class GlassPaneDialogWrapperPeer extends DialogWrapperPeer implements Foc private void createDialog(final Window owner) throws GlasspanePeerUnavailableException { Window active = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow(); if (!(active instanceof JDialog) && owner instanceof IdeFrame) { - final JFrame frame = (JFrame) owner; - final JComponent glassPane = (JComponent) frame.getGlassPane(); + + Component glassPane; + + // Not all successor of IdeFrame are frames + if (owner instanceof JFrame) { + glassPane = ((JFrame)owner).getGlassPane(); + } + else if (owner instanceof JDialog) { + glassPane = ((JDialog)owner).getGlassPane(); + } + else { + throw new IllegalStateException("Cannot find glass pane for " + owner.getClass().getName()); + } assert glassPane instanceof IdeGlassPaneEx : "GlassPane should be instance of IdeGlassPane!"; myDialog = new MyDialog((IdeGlassPaneEx) glassPane, myWrapper, myProject); diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java index 3201ad09e12f..6684b24af327 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java @@ -210,9 +210,15 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable { myActivityMonitor.addActivity(FOCUS, ModalityState.any()); if (!forced) { - if (!myFocusRequests.contains(command)) { - myFocusRequests.add(command); - } + + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + if (!myFocusRequests.contains(command)) { + myFocusRequests.add(command); + } + } + }); SwingUtilities.invokeLater(new Runnable() { @Override @@ -385,15 +391,23 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable { return false; } - private void setCommand(@NotNull FocusCommand command) { + private void setCommand(@NotNull final FocusCommand command) { myRequestFocusCmd = command; - if (!myFocusRequests.contains(command)) { - myFocusRequests.add(command); - } + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + if (!myFocusRequests.contains(command)) { + myFocusRequests.add(command); + } + } + }); } - private void resetCommand(@NotNull FocusCommand cmd, boolean reject) { + private void resetCommand(@NotNull final FocusCommand cmd, boolean reject) { + + assertDispatchThread(); + if (cmd == myRequestFocusCmd) { myRequestFocusCmd = null; } @@ -403,7 +417,12 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable { processor.finish(myKeyProcessorContext); } - myFocusRequests.remove(cmd); + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + myFocusRequests.remove(cmd); + } + }); if (reject) { ActionCallback cb = cmd.getCallback(); @@ -413,8 +432,13 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable { } } - private void resetUnforcedCommand(@NotNull FocusCommand cmd) { - myFocusRequests.remove(cmd); + private void resetUnforcedCommand(@NotNull final FocusCommand cmd) { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + myFocusRequests.remove(cmd); + } + }); } private static boolean canExecuteOnInactiveApplication(@NotNull FocusCommand cmd) { @@ -666,20 +690,26 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable { } private void invalidateFocusRequestsQueue() { - if (myFocusRequests.isEmpty()) return; + assertDispatchThread(); + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + if (myFocusRequests.isEmpty()) return; - FocusCommand[] requests = myFocusRequests.toArray(new FocusCommand[myFocusRequests.size()]); - boolean wasChanged = false; - for (FocusCommand each : requests) { - if (each.isExpired()) { - resetCommand(each, true); - wasChanged = true; + FocusCommand[] requests = myFocusRequests.toArray(new FocusCommand[myFocusRequests.size()]); + boolean wasChanged = false; + for (FocusCommand each : requests) { + if (each.isExpired()) { + resetCommand(each, true); + wasChanged = true; + } + } + + if (wasChanged && myFocusRequests.isEmpty()) { + restartIdleAlarm(); + } } - } - - if (wasChanged && myFocusRequests.isEmpty()) { - restartIdleAlarm(); - } + }); } private boolean isIdleQueueEmpty() { @@ -695,6 +725,8 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable { if (!isTypeaheadEnabled()) return false; if (isFlushingIdleRequests()) return false; + assertDispatchThread(); + if (!isFocusTransferReady() || !isPendingKeyEventsRedispatched() || !myTypeAheadRequestors.isEmpty()) { for (FocusCommand each : myFocusRequests) { final KeyEventProcessor processor = each.getProcessor(); diff --git a/platform/platform-impl/src/com/intellij/ui/messages/JBMacMessages.java b/platform/platform-impl/src/com/intellij/ui/messages/JBMacMessages.java index b1ca220609aa..25e70af0d629 100644 --- a/platform/platform-impl/src/com/intellij/ui/messages/JBMacMessages.java +++ b/platform/platform-impl/src/com/intellij/ui/messages/JBMacMessages.java @@ -49,7 +49,7 @@ public class JBMacMessages extends MacMessagesEmulation { window = getForemostWindow(null); } SheetMessage sheetMessage = new SheetMessage(window, title, message, UIUtil.getQuestionIcon(), - new String [] {defaultButton, alternateButton, otherButton}, null, defaultButton, alternateButton); + new String [] {defaultButton, alternateButton, otherButton}, doNotAskOption, defaultButton, alternateButton); String resultString = sheetMessage.getResult(); int result = resultString.equals(defaultButton) ? Messages.YES : resultString.equals(alternateButton) ? Messages.NO : Messages.CANCEL; if (doNotAskOption != null) { diff --git a/platform/platform-impl/src/com/intellij/ui/win/RecentTasks.java b/platform/platform-impl/src/com/intellij/ui/win/RecentTasks.java index eecdc173f78d..c9380568672a 100644 --- a/platform/platform-impl/src/com/intellij/ui/win/RecentTasks.java +++ b/platform/platform-impl/src/com/intellij/ui/win/RecentTasks.java @@ -31,6 +31,9 @@ public class RecentTasks { private final static WeakReference openerThread = new WeakReference(Thread.currentThread()); + private final static String openerThreadName = + Thread.currentThread().getName(); + static { UrlClassLoader.loadPlatformLibrary("jumpListBridge"); } @@ -71,6 +74,6 @@ public class RecentTasks { private static void checkThread() { Thread t = openerThread.get(); if (t == null || !t.equals(Thread.currentThread())) - throw new RuntimeException("This class has to be used from the same thread"); + throw new RuntimeException("Current thread is " + Thread.currentThread().getName() + "This class has to be used from " + openerThreadName + " thread"); } } diff --git a/platform/platform-resources-en/src/messages/ActionsBundle.properties b/platform/platform-resources-en/src/messages/ActionsBundle.properties index cac75db369df..ed2e3bb493ac 100644 --- a/platform/platform-resources-en/src/messages/ActionsBundle.properties +++ b/platform/platform-resources-en/src/messages/ActionsBundle.properties @@ -612,6 +612,8 @@ action.IntroduceConstant.text=_Constant... action.IntroduceConstant.description=Replace selected expression with a constant (static final field) action.IntroduceParameter.text=_Parameter... action.IntroduceParameter.description=Turn the selected expression into method parameter +action.IntroduceFunctionalParameter.text=Functiona_l Parameter... +action.IntroduceFunctionalParameter.description=Replace selected statements with a call to new functional method parameter action.ExtractInterface.text=_Interface... action.ExtractInterface.description=Extract interface from the selected class action.ExtractModule.text=_Module... diff --git a/platform/platform-resources-en/src/messages/RefactoringBundle.properties b/platform/platform-resources-en/src/messages/RefactoringBundle.properties index e04615b8158d..b090b049ac86 100644 --- a/platform/platform-resources-en/src/messages/RefactoringBundle.properties +++ b/platform/platform-resources-en/src/messages/RefactoringBundle.properties @@ -787,3 +787,4 @@ rename.project.handler.title=Rename &project enter.new.project.name=Enter new project name: rename.project=Rename Project renames.project=Renames project +introduce.functional.parameter.title=Extract Functional Parameter diff --git a/platform/platform-resources/src/META-INF/LangExtensions.xml b/platform/platform-resources/src/META-INF/LangExtensions.xml index 85bebce06660..45c3a8720545 100644 --- a/platform/platform-resources/src/META-INF/LangExtensions.xml +++ b/platform/platform-resources/src/META-INF/LangExtensions.xml @@ -328,8 +328,17 @@ - + + + + + + + + + @@ -868,9 +877,6 @@ - - - diff --git a/platform/platform-resources/src/idea/Keymap_Default.xml b/platform/platform-resources/src/idea/Keymap_Default.xml index 8cd7594ebaaa..baf7d20c0c52 100644 --- a/platform/platform-resources/src/idea/Keymap_Default.xml +++ b/platform/platform-resources/src/idea/Keymap_Default.xml @@ -716,6 +716,9 @@ + + + diff --git a/platform/projectModel-api/src/com/intellij/lang/LanguagePerFileMappings.java b/platform/projectModel-api/src/com/intellij/lang/LanguagePerFileMappings.java index 1047e294e1c5..c28fdf877eea 100644 --- a/platform/projectModel-api/src/com/intellij/lang/LanguagePerFileMappings.java +++ b/platform/projectModel-api/src/com/intellij/lang/LanguagePerFileMappings.java @@ -15,214 +15,23 @@ */ package com.intellij.lang; -import com.intellij.injected.editor.VirtualFileWindow; -import com.intellij.openapi.components.PersistentStateComponent; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; -import com.intellij.openapi.roots.impl.FilePropertyPusher; -import com.intellij.openapi.roots.impl.PushedFilePropertiesUpdater; -import com.intellij.openapi.util.Comparing; -import com.intellij.openapi.util.Key; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileManager; -import com.intellij.psi.PsiDocumentManager; -import com.intellij.testFramework.LightVirtualFile; -import com.intellij.util.containers.ContainerUtil; -import gnu.trove.THashMap; -import org.jdom.Element; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.TestOnly; - -import java.util.*; /** * @author peter */ -public abstract class LanguagePerFileMappings implements PersistentStateComponent, PerFileMappings { +public abstract class LanguagePerFileMappings extends PerFileMappingsBase implements PerFileMappings { - private static final Logger LOG = Logger.getInstance("com.intellij.lang.LanguagePerFileMappings"); - - private final Map myMappings = new HashMap(); private final Project myProject; - public LanguagePerFileMappings(final Project project) { + public LanguagePerFileMappings(@NotNull Project project) { myProject = project; } - @Nullable - protected FilePropertyPusher getFilePropertyPusher() { - return null; - } - - @Override - public Map getMappings() { - synchronized (myMappings) { - cleanup(); - return Collections.unmodifiableMap(myMappings); - } - } - - private void cleanup() { - for (final VirtualFile file : new ArrayList(myMappings.keySet())) { - if (file != null //PROJECT, top-level - && !file.isValid()) { - myMappings.remove(file); - } - } - } - - @Override - @Nullable - public T getMapping(@Nullable VirtualFile file) { - FilePropertyPusher pusher = getFilePropertyPusher(); - T t = getMappingInner(file, myMappings, pusher == null? null : pusher.getFileDataKey()); - return t == null? getDefaultMapping(file) : t; - } - - @Nullable - protected static T getMappingInner(@Nullable VirtualFile file, @Nullable Map mappings, @Nullable Key pusherKey) { - if (file instanceof VirtualFileWindow) { - final VirtualFileWindow window = (VirtualFileWindow)file; - file = window.getDelegate(); - } - VirtualFile originalFile = file instanceof LightVirtualFile ? ((LightVirtualFile)file).getOriginalFile() : null; - if (Comparing.equal(originalFile, file)) originalFile = null; - - if (file != null) { - final T pushedValue = pusherKey == null? null : file.getUserData(pusherKey); - if (pushedValue != null) return pushedValue; - } - if (originalFile != null) { - final T pushedValue = pusherKey == null? null : originalFile.getUserData(pusherKey); - if (pushedValue != null) return pushedValue; - } - if (mappings == null) return null; - synchronized (mappings) { - for (VirtualFile cur = file; ; cur = cur.getParent()) { - T t = mappings.get(cur); - if (t != null) return t; - if (originalFile != null) { - t = mappings.get(originalFile); - if (t != null) return t; - originalFile = originalFile.getParent(); - } - if (cur == null) break; - } - } - return null; - } - - @Override - public T chosenToStored(VirtualFile file, T value) { - return value; - } - - @Override - public boolean isSelectable(T value) { - return true; - } - - @Override - @Nullable - public T getDefaultMapping(@Nullable final VirtualFile file) { - return null; - } - - @Nullable - public T getImmediateMapping(@Nullable final VirtualFile file) { - synchronized (myMappings) { - return myMappings.get(file); - } - } - - @Override - public void setMappings(final Map mappings) { - final Collection oldFiles; - synchronized (myMappings) { - oldFiles = new ArrayList(myMappings.keySet()); - myMappings.clear(); - myMappings.putAll(mappings); - cleanup(); - } - handleMappingChange(mappings.keySet(), oldFiles, !getProject().isDefault()); - } - - public void setMapping(@Nullable final VirtualFile file, @Nullable T dialect) { - synchronized (myMappings) { - if (dialect == null) { - myMappings.remove(file); - } - else { - myMappings.put(file, dialect); - } - } - final List files = ContainerUtil.createMaybeSingletonList(file); - handleMappingChange(files, files, false); - } - - private void handleMappingChange(final Collection files, Collection oldFiles, final boolean includeOpenFiles) { - final FilePropertyPusher pusher = getFilePropertyPusher(); - if (pusher != null) { - for (VirtualFile oldFile : oldFiles) { - if (oldFile == null) continue; // project - oldFile.putUserData(pusher.getFileDataKey(), null); - } - PushedFilePropertiesUpdater updater = PushedFilePropertiesUpdater.getInstance(myProject); - if (updater == null) { - if (!myProject.isDefault()) { - LOG.error("updater = null. project=" + myProject.getName()+", this="+getClass().getSimpleName()); - } - } - else { - updater.pushAll(pusher); - } - } - if (shouldReparseFiles()) { - PsiDocumentManager.getInstance(myProject).reparseFiles(files, includeOpenFiles); - } - } - - @Override - public Collection getAvailableValues(VirtualFile file) { - return getAvailableValues(); - } - - protected abstract List getAvailableValues(); - - @Nullable - protected abstract String serialize(T t); - - @Override - public Element getState() { - synchronized (myMappings) { - cleanup(); - final Element element = new Element("x"); - final List files = new ArrayList(myMappings.keySet()); - Collections.sort(files, new Comparator() { - @Override - public int compare(final VirtualFile o1, final VirtualFile o2) { - if (o1 == null || o2 == null) return o1 == null ? o2 == null ? 0 : 1 : -1; - return o1.getPath().compareTo(o2.getPath()); - } - }); - for (VirtualFile file : files) { - final T dialect = myMappings.get(file); - String value = serialize(dialect); - if (value != null) { - final Element child = new Element("file"); - element.addContent(child); - child.setAttribute("url", file == null ? "PROJECT" : file.getUrl()); - child.setAttribute(getValueAttribute(), value); - } - } - return element; - } - } - - @Nullable - protected T handleUnknownMapping(VirtualFile file, String value) { - return null; + @NotNull + protected Project getProject() { + return myProject; } @NotNull @@ -230,52 +39,4 @@ public abstract class LanguagePerFileMappings implements PersistentStateCompo return "dialect"; } - @Override - public void loadState(final Element state) { - synchronized (myMappings) { - final THashMap dialectMap = new THashMap(); - for (T dialect : getAvailableValues()) { - String key = serialize(dialect); - if (key != null) { - dialectMap.put(key, dialect); - } - } - final List files = state.getChildren("file"); - for (Element fileElement : files) { - final String url = fileElement.getAttributeValue("url"); - final String dialectID = fileElement.getAttributeValue(getValueAttribute()); - final VirtualFile file = url.equals("PROJECT") ? null : VirtualFileManager.getInstance().findFileByUrl(url); - T dialect = dialectMap.get(dialectID); - if (dialect == null) { - dialect = handleUnknownMapping(file, dialectID); - if (dialect == null) continue; - } - if (file != null || url.equals("PROJECT")) { - myMappings.put(file, dialect); - } - } - } - } - - @TestOnly - public void cleanupForNextTest() { - synchronized (myMappings) { - myMappings.clear(); - } - } - - protected Project getProject() { - return myProject; - } - - protected boolean shouldReparseFiles() { - return true; - } - - public boolean hasMappings() { - synchronized (myMappings) { - return !myMappings.isEmpty(); - } - } - } diff --git a/platform/projectModel-api/src/com/intellij/lang/PerFileMappings.java b/platform/projectModel-api/src/com/intellij/lang/PerFileMappings.java index d818bb27fd5b..85fec22971bc 100644 --- a/platform/projectModel-api/src/com/intellij/lang/PerFileMappings.java +++ b/platform/projectModel-api/src/com/intellij/lang/PerFileMappings.java @@ -17,6 +17,7 @@ package com.intellij.lang; import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; @@ -26,20 +27,23 @@ import java.util.Map; * @author Dmitry Avdeev */ public interface PerFileMappings { - + + @NotNull Map getMappings(); - void setMappings(Map mappings); + void setMappings(@NotNull Map mappings); - Collection getAvailableValues(final VirtualFile file); + void setMapping(@Nullable VirtualFile file, T value); + + Collection getAvailableValues(VirtualFile file); @Nullable - T getMapping(final VirtualFile file); + T getMapping(VirtualFile file); @Nullable T getDefaultMapping(@Nullable VirtualFile file); - T chosenToStored(final VirtualFile file, final T value); + T chosenToStored(VirtualFile file, T value); - boolean isSelectable(final T value); + boolean isSelectable(T value); } diff --git a/platform/projectModel-api/src/com/intellij/lang/PerFileMappingsBase.java b/platform/projectModel-api/src/com/intellij/lang/PerFileMappingsBase.java new file mode 100644 index 000000000000..11e50c9160fc --- /dev/null +++ b/platform/projectModel-api/src/com/intellij/lang/PerFileMappingsBase.java @@ -0,0 +1,270 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.lang; + +import com.intellij.injected.editor.VirtualFileWindow; +import com.intellij.openapi.components.PersistentStateComponent; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ProjectManager; +import com.intellij.openapi.roots.impl.FilePropertyPusher; +import com.intellij.openapi.roots.impl.PushedFilePropertiesUpdater; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileManager; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.testFramework.LightVirtualFile; +import com.intellij.util.containers.ContainerUtil; +import gnu.trove.THashMap; +import org.jdom.Element; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.TestOnly; + +import java.util.*; + +/** + * @author gregsh + */ +public abstract class PerFileMappingsBase implements PersistentStateComponent, PerFileMappings { + private final Map myMappings = ContainerUtil.newHashMap(); + + @Nullable + protected FilePropertyPusher getFilePropertyPusher() { + return null; + } + + @Nullable + protected Project getProject() { return null; } + + @NotNull + @Override + public Map getMappings() { + synchronized (myMappings) { + cleanup(); + return Collections.unmodifiableMap(myMappings); + } + } + + private void cleanup() { + for (final VirtualFile file : new ArrayList(myMappings.keySet())) { + if (file != null //PROJECT, top-level + && !file.isValid()) { + myMappings.remove(file); + } + } + } + + @Override + @Nullable + public T getMapping(@Nullable VirtualFile file) { + FilePropertyPusher pusher = getFilePropertyPusher(); + T t = getMappingInner(file, myMappings, pusher == null? null : pusher.getFileDataKey()); + return t == null? getDefaultMapping(file) : t; + } + + @Nullable + protected static T getMappingInner(@Nullable VirtualFile file, @Nullable Map mappings, @Nullable Key pusherKey) { + if (file instanceof VirtualFileWindow) { + final VirtualFileWindow window = (VirtualFileWindow)file; + file = window.getDelegate(); + } + VirtualFile originalFile = file instanceof LightVirtualFile ? ((LightVirtualFile)file).getOriginalFile() : null; + if (Comparing.equal(originalFile, file)) originalFile = null; + + if (file != null) { + final T pushedValue = pusherKey == null? null : file.getUserData(pusherKey); + if (pushedValue != null) return pushedValue; + } + if (originalFile != null) { + final T pushedValue = pusherKey == null? null : originalFile.getUserData(pusherKey); + if (pushedValue != null) return pushedValue; + } + if (mappings == null) return null; + synchronized (mappings) { + for (VirtualFile cur = file; ; cur = cur.getParent()) { + T t = mappings.get(cur); + if (t != null) return t; + if (originalFile != null) { + t = mappings.get(originalFile); + if (t != null) return t; + originalFile = originalFile.getParent(); + } + if (cur == null) break; + } + } + return null; + } + + @Override + public T chosenToStored(VirtualFile file, T value) { + return value; + } + + @Override + public boolean isSelectable(T value) { + return true; + } + + @Override + @Nullable + public T getDefaultMapping(@Nullable VirtualFile file) { + return null; + } + + @Nullable + public T getImmediateMapping(@Nullable VirtualFile file) { + synchronized (myMappings) { + return myMappings.get(file); + } + } + + @Override + public void setMappings(@NotNull final Map mappings) { + Collection oldFiles; + synchronized (myMappings) { + oldFiles = ContainerUtil.newArrayList(myMappings.keySet()); + myMappings.clear(); + myMappings.putAll(mappings); + cleanup(); + } + Project project = getProject(); + handleMappingChange(mappings.keySet(), oldFiles, project != null && !project.isDefault()); + } + + public void setMapping(@Nullable final VirtualFile file, @Nullable T dialect) { + synchronized (myMappings) { + if (dialect == null) { + myMappings.remove(file); + } + else { + myMappings.put(file, dialect); + } + } + List files = ContainerUtil.createMaybeSingletonList(file); + handleMappingChange(files, files, false); + } + + private void handleMappingChange(Collection files, Collection oldFiles, boolean includeOpenFiles) { + Project project = getProject(); + FilePropertyPusher pusher = getFilePropertyPusher(); + if (project != null && pusher != null) { + for (VirtualFile oldFile : oldFiles) { + if (oldFile == null) continue; // project + oldFile.putUserData(pusher.getFileDataKey(), null); + } + PushedFilePropertiesUpdater updater = PushedFilePropertiesUpdater.getInstance(project); + updater.pushAll(pusher); + } + if (shouldReparseFiles()) { + Project[] projects = project == null ? ProjectManager.getInstance().getOpenProjects() : new Project[] { project }; + for (Project p : projects) { + PsiDocumentManager.getInstance(p).reparseFiles(files, includeOpenFiles); + } + } + } + + @Override + public Collection getAvailableValues(VirtualFile file) { + return getAvailableValues(); + } + + protected abstract List getAvailableValues(); + + @Nullable + protected abstract String serialize(T t); + + @Override + public Element getState() { + synchronized (myMappings) { + cleanup(); + final Element element = new Element("x"); + final List files = new ArrayList(myMappings.keySet()); + Collections.sort(files, new Comparator() { + @Override + public int compare(final VirtualFile o1, final VirtualFile o2) { + if (o1 == null || o2 == null) return o1 == null ? o2 == null ? 0 : 1 : -1; + return o1.getPath().compareTo(o2.getPath()); + } + }); + for (VirtualFile file : files) { + final T dialect = myMappings.get(file); + String value = serialize(dialect); + if (value != null) { + final Element child = new Element("file"); + element.addContent(child); + child.setAttribute("url", file == null ? "PROJECT" : file.getUrl()); + child.setAttribute(getValueAttribute(), value); + } + } + return element; + } + } + + @Nullable + protected T handleUnknownMapping(VirtualFile file, String value) { + return null; + } + + @NotNull + protected String getValueAttribute() { + return "value"; + } + + @Override + public void loadState(final Element state) { + synchronized (myMappings) { + final THashMap dialectMap = new THashMap(); + for (T dialect : getAvailableValues()) { + String key = serialize(dialect); + if (key != null) { + dialectMap.put(key, dialect); + } + } + final List files = state.getChildren("file"); + for (Element fileElement : files) { + final String url = fileElement.getAttributeValue("url"); + final String dialectID = fileElement.getAttributeValue(getValueAttribute()); + final VirtualFile file = url.equals("PROJECT") ? null : VirtualFileManager.getInstance().findFileByUrl(url); + T dialect = dialectMap.get(dialectID); + if (dialect == null) { + dialect = handleUnknownMapping(file, dialectID); + if (dialect == null) continue; + } + if (file != null || url.equals("PROJECT")) { + myMappings.put(file, dialect); + } + } + } + } + + @TestOnly + public void cleanupForNextTest() { + synchronized (myMappings) { + myMappings.clear(); + } + } + + protected boolean shouldReparseFiles() { + return true; + } + + public boolean hasMappings() { + synchronized (myMappings) { + return !myMappings.isEmpty(); + } + } +} diff --git a/platform/testFramework/src/com/intellij/testFramework/PlatformTestUtil.java b/platform/testFramework/src/com/intellij/testFramework/PlatformTestUtil.java index 0d2486670e76..007feedab41e 100644 --- a/platform/testFramework/src/com/intellij/testFramework/PlatformTestUtil.java +++ b/platform/testFramework/src/com/intellij/testFramework/PlatformTestUtil.java @@ -45,11 +45,7 @@ import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileFilter; import com.intellij.openapi.vfs.ex.temp.TempFileSystem; -import com.intellij.util.Alarm; -import com.intellij.util.ArrayUtil; -import com.intellij.util.ReflectionUtil; -import com.intellij.util.ThrowableRunnable; -import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.*; import com.intellij.util.containers.HashMap; import com.intellij.util.io.ZipUtil; import com.intellij.util.ui.UIUtil; @@ -68,8 +64,6 @@ import javax.swing.tree.TreePath; import java.awt.*; import java.awt.event.InvocationEvent; import java.io.*; -import java.lang.ref.ReferenceQueue; -import java.lang.ref.SoftReference; import java.nio.charset.Charset; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; @@ -837,20 +831,7 @@ public class PlatformTestUtil { } public static void tryGcSoftlyReachableObjects() { - ReferenceQueue q = new ReferenceQueue(); - SoftReference ref = new SoftReference(new Object(), q); - List list = ContainerUtil.newArrayListWithCapacity(100 + useReference(ref)); - for (int i = 0; i < 100; i++) { - if (q.poll() != null) { - break; - } - list.add(new SoftReference(new byte[(int)Runtime.getRuntime().freeMemory() / 2])); - } - } - - private static int useReference(SoftReference ref) { - Object o = ref.get(); - return o == null ? 0 : Math.abs(o.hashCode()) % 10; + GCUtil.tryGcSoftlyReachableObjects(); } public static void withEncoding(@NotNull String encoding, @NotNull final Runnable r) { diff --git a/platform/testFramework/src/com/intellij/testFramework/fixtures/CodeInsightTestFixture.java b/platform/testFramework/src/com/intellij/testFramework/fixtures/CodeInsightTestFixture.java index 2cd323c0fa8f..61efedf49fc2 100644 --- a/platform/testFramework/src/com/intellij/testFramework/fixtures/CodeInsightTestFixture.java +++ b/platform/testFramework/src/com/intellij/testFramework/fixtures/CodeInsightTestFixture.java @@ -274,7 +274,7 @@ public interface CodeInsightTestFixture extends IdeaProjectTestFixture { * @return duration */ long checkHighlighting(boolean checkWarnings, boolean checkInfos, boolean checkWeakWarnings); - + long checkHighlighting(boolean checkWarnings, boolean checkInfos, boolean checkWeakWarnings, boolean ignoreExtraHighlighting); long checkHighlighting(); @@ -356,7 +356,7 @@ public interface CodeInsightTestFixture extends IdeaProjectTestFixture { * * @param hint the text that the intention text should begin with. * @return the matching intention - * @throws java.lang.AssertionError if no intentions are found or if multiple intentions match the hint text. + * @throws java.lang.AssertionError if no intentions are found or if multiple intentions match the hint text. */ IntentionAction findSingleIntention(@NotNull String hint); @@ -561,9 +561,11 @@ public interface CodeInsightTestFixture extends IdeaProjectTestFixture { * Actually, it works just like {@link #completeBasic()} but supports * several {@link #CARET_MARKER} * + * @return list of all completion elements just like in {@link #completeBasic()} * @see #completeBasic() */ - void completeBasicAllCarets(); + @NotNull + List completeBasicAllCarets(); void saveText(VirtualFile file, String text); } diff --git a/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java b/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java index 7bcd62e9bcee..7c0d11761826 100644 --- a/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java +++ b/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java @@ -1068,7 +1068,8 @@ public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsig @Override - public void completeBasicAllCarets() { + @NotNull + public final List completeBasicAllCarets() { final CaretModel caretModel = myEditor.getCaretModel(); final List carets = caretModel.getAllCarets(); @@ -1082,10 +1083,15 @@ public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsig // We do it in reverse order because completions would affect offsets // i.e.: when you complete "spa" to "spam", next caret offset increased by 1 Collections.reverse(originalOffsets); + final List result = new ArrayList(); for (final int originalOffset : originalOffsets) { caretModel.moveToOffset(originalOffset); - completeBasic(); + final LookupElement[] lookupElements = completeBasic(); + if (lookupElements != null) { + result.addAll(Arrays.asList(lookupElements)); + } } + return result; } @Override diff --git a/platform/util/src/com/intellij/util/GCUtil.java b/platform/util/src/com/intellij/util/GCUtil.java new file mode 100644 index 000000000000..2659d0f92d52 --- /dev/null +++ b/platform/util/src/com/intellij/util/GCUtil.java @@ -0,0 +1,51 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.util; + +import com.intellij.util.containers.ContainerUtil; + +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; +import java.lang.ref.WeakReference; +import java.util.List; + +public class GCUtil { + public static void tryForceGC() { + tryGcSoftlyReachableObjects(); + WeakReference weakReference = new WeakReference(new Object()); + do { + System.gc(); + } + while (weakReference.get() != null); + } + + public static void tryGcSoftlyReachableObjects() { + ReferenceQueue q = new ReferenceQueue(); + SoftReference ref = new SoftReference(new Object(), q); + List list = ContainerUtil.newArrayListWithCapacity(100 + useReference(ref)); + for (int i = 0; i < 100; i++) { + if (q.poll() != null) { + break; + } + list.add(new SoftReference(new byte[(int)Runtime.getRuntime().freeMemory() / 2])); + } + } + + private static int useReference(SoftReference ref) { + Object o = ref.get(); + return o == null ? 0 : Math.abs(o.hashCode()) % 10; + } +} diff --git a/platform/util/src/com/intellij/util/ui/UIUtil.java b/platform/util/src/com/intellij/util/ui/UIUtil.java index 6af544af7755..02bed0eedf02 100644 --- a/platform/util/src/com/intellij/util/ui/UIUtil.java +++ b/platform/util/src/com/intellij/util/ui/UIUtil.java @@ -3215,10 +3215,6 @@ public class UIUtil { textComponent.getActionMap().put("redoKeystroke", REDO_ACTION); } - public static void adjustRows(JTextArea area, int minRows, int maxRows) { - area.setRows(Math.max(minRows, Math.min(maxRows, area.getText().split("\n").length))); - } - public static void playSoundFromResource(final String resourceName) { final Class callerClass = ReflectionUtil.getGrandCallerClass(); if (callerClass == null) return; diff --git a/platform/util/testSrc/com/intellij/util/containers/ConcurrentMapsTest.java b/platform/util/testSrc/com/intellij/util/containers/ConcurrentMapsTest.java index f97d78434835..7ea422ed99b2 100644 --- a/platform/util/testSrc/com/intellij/util/containers/ConcurrentMapsTest.java +++ b/platform/util/testSrc/com/intellij/util/containers/ConcurrentMapsTest.java @@ -16,11 +16,10 @@ package com.intellij.util.containers; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.GCUtil; import gnu.trove.TObjectHashingStrategy; import org.junit.Test; -import java.lang.ref.SoftReference; -import java.util.List; import java.util.Map; import java.util.Set; @@ -96,12 +95,7 @@ public class ConcurrentMapsTest { } public static void tryGcSoftlyReachableObjects() { - SoftReference reference = new SoftReference(new Object()); - List list = ContainerUtil.newArrayList(); - while (reference.get() != null) { - int chunk = (int)Math.min(Runtime.getRuntime().freeMemory() / 2, Integer.MAX_VALUE); - list.add(new SoftReference(new byte[chunk])); - } + GCUtil.tryGcSoftlyReachableObjects(); } @Test(timeout = TIMEOUT) diff --git a/platform/util/testSrc/com/intellij/util/containers/WeakListTest.java b/platform/util/testSrc/com/intellij/util/containers/WeakListTest.java index f10676905241..3c0b1bf33ab9 100644 --- a/platform/util/testSrc/com/intellij/util/containers/WeakListTest.java +++ b/platform/util/testSrc/com/intellij/util/containers/WeakListTest.java @@ -15,9 +15,9 @@ */ package com.intellij.util.containers; +import com.intellij.util.GCUtil; import org.junit.Test; -import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -225,11 +225,6 @@ public class WeakListTest { } private static void gc() { - ConcurrentMapsTest.tryGcSoftlyReachableObjects(); - WeakReference weakReference = new WeakReference(new Object()); - do { - System.gc(); - } - while (weakReference.get() != null); + GCUtil.tryForceGC(); } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/VcsLogGraphTable.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/VcsLogGraphTable.java index 7ce1bf82c416..ceaf8707d6a3 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/VcsLogGraphTable.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/VcsLogGraphTable.java @@ -39,6 +39,8 @@ import com.intellij.vcs.log.data.VcsLogDataHolder; import com.intellij.vcs.log.data.VisiblePack; import com.intellij.vcs.log.graph.ColorGenerator; import com.intellij.vcs.log.graph.PrintElement; +import com.intellij.vcs.log.graph.RowInfo; +import com.intellij.vcs.log.graph.RowType; import com.intellij.vcs.log.graph.actions.GraphAnswer; import com.intellij.vcs.log.graph.actions.GraphMouseAction; import com.intellij.vcs.log.printer.idea.GraphCellPainter; @@ -321,16 +323,17 @@ public class VcsLogGraphTable extends JBTable implements TypeSafeDataProvider, C } public void applyHighlighters(@NotNull Component rendererComponent, int row, boolean selected) { + RowInfo rowInfo = myDataPack.getVisibleGraph().getRowInfo(row); boolean fgUpdated = false; for (VcsLogHighlighter highlighter : myHighlighters) { - Color color = highlighter.getForeground(myDataPack.getVisibleGraph().getRowInfo(row).getCommit(), selected); + Color color = highlighter.getForeground(rowInfo.getCommit(), selected); if (color != null) { rendererComponent.setForeground(color); fgUpdated = true; } } if (!fgUpdated) { // reset highlighting if no-one wants to change it - rendererComponent.setForeground(UIUtil.getTableForeground(selected)); + rendererComponent.setForeground(rowInfo.getRowType() == RowType.UNMATCHED ? JBColor.GRAY : UIUtil.getTableForeground(selected)); } } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/memory/InnerClassMayBeStaticInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/memory/InnerClassMayBeStaticInspection.java index 6db808eb729d..751ce0a416f2 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/memory/InnerClassMayBeStaticInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/memory/InnerClassMayBeStaticInspection.java @@ -111,8 +111,10 @@ public class InnerClassMayBeStaticInspection extends BaseInspection { if (argumentList == null) { continue; } - final PsiExpression expression = - factory.createExpressionFromText("new " + classReference.getQualifiedName() + argumentList.getText(), innerClass); + final PsiReferenceParameterList parameterList = classReference.getParameterList(); + final String genericParameters = parameterList != null ? parameterList.getText() : ""; + final PsiExpression expression = factory + .createExpressionFromText("new " + classReference.getQualifiedName() + genericParameters + argumentList.getText(), innerClass); codeStyleManager.shortenClassReferences(newExpression.replace(expression)); } final PsiModifierList modifiers = innerClass.getModifierList(); diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/memory/inner_class_static/PreserveGenericSignature.after.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/memory/inner_class_static/PreserveGenericSignature.after.java new file mode 100644 index 000000000000..47e556e9804b --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/memory/inner_class_static/PreserveGenericSignature.after.java @@ -0,0 +1,16 @@ +class IdeaTest { + + public void test(){ + print(new InnerClass().foo(Integer.valueOf(1))); + } + + public void print(Integer foo){ + System.out.println(foo); + } + + static class InnerClass{ + public T foo(T bar){ + return bar; + } + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/memory/inner_class_static/PreserveGenericSignature.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/memory/inner_class_static/PreserveGenericSignature.java new file mode 100644 index 000000000000..30c074c91e8e --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/memory/inner_class_static/PreserveGenericSignature.java @@ -0,0 +1,16 @@ +class IdeaTest { + + public void test(){ + print(new InnerClass().foo(Integer.valueOf(1))); + } + + public void print(Integer foo){ + System.out.println(foo); + } + + class InnerClass{ + public T foo(T bar){ + return bar; + } + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/memory/InnerClassMayBeStaticFixTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/memory/InnerClassMayBeStaticFixTest.java index 49a855ed605f..465427174cc4 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/memory/InnerClassMayBeStaticFixTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/memory/InnerClassMayBeStaticFixTest.java @@ -33,8 +33,6 @@ public class InnerClassMayBeStaticFixTest extends IGQuickFixesTestCase { } public void testSimple() { doTest(); } - - public void testAnonymousInside() { - doTest(); - } + public void testAnonymousInside() { doTest(); } + public void testPreserveGenericSignature() { doTest(); } } diff --git a/plugins/devkit/src/projectRoots/IdeaJdk.java b/plugins/devkit/src/projectRoots/IdeaJdk.java index fce82406886f..4bdd0f275382 100644 --- a/plugins/devkit/src/projectRoots/IdeaJdk.java +++ b/plugins/devkit/src/projectRoots/IdeaJdk.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -149,27 +149,8 @@ public class IdeaJdk extends JavaDependentSdkType implements JavaSdkType { } public String suggestSdkName(String currentSdkName, String sdkHome) { - @NonNls final String productName; - if (new File(sdkHome, "lib/rubymine.jar").exists()) { - productName = "RubyMine "; - } - else if (new File(sdkHome, "lib/pycharm.jar").exists()) { - productName = "PyCharm "; - } - else if (new File(sdkHome, "lib/webide.jar").exists()) { - productName = "WebStorm/PhpStorm "; - } - else if (new File(sdkHome, "license/AppCode_license.txt").exists()) { - productName = "AppCode "; - } - else if (new File(sdkHome, "license/CLion_Preview_License.txt").exists()) { - productName = "CLion "; - } - else { - productName = "IDEA "; - } String buildNumber = getBuildNumber(sdkHome); - return productName + (buildNumber != null ? buildNumber : ""); + return IntelliJPlatformProduct.fromBuildNumber(buildNumber).getName() + " " + (buildNumber != null ? buildNumber : ""); } @Nullable diff --git a/plugins/devkit/src/projectRoots/IntelliJPlatformProduct.java b/plugins/devkit/src/projectRoots/IntelliJPlatformProduct.java new file mode 100644 index 000000000000..05c1e9970e66 --- /dev/null +++ b/plugins/devkit/src/projectRoots/IntelliJPlatformProduct.java @@ -0,0 +1,63 @@ +/* + * Copyright 2000-2015 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 org.jetbrains.idea.devkit.projectRoots; + +import com.intellij.util.PlatformUtils; + +/** + * @author yole + */ +public enum IntelliJPlatformProduct { + IDEA("IU", "IntelliJ IDEA", null), + IDEA_IC("IC", "IntelliJ IDEA Community Edition", PlatformUtils.IDEA_CE_PREFIX), + RUBYMINE("RM", "RubyMine", PlatformUtils.RUBY_PREFIX), + PYCHARM("PY", "PyCharm", PlatformUtils.PYCHARM_PREFIX), + PYCHARM_PC("PC", "PyCharm Community Edition", PlatformUtils.PYCHARM_CE_PREFIX), + PYCHARM_EDU("PE", "PyCharm Educational Edition", PlatformUtils.PYCHARM_EDU_PREFIX), + PHPSTORM("PS", "PhpStorm", PlatformUtils.PHP_PREFIX), + WEBSTORM("WS", "WebStorm", PlatformUtils.WEB_PREFIX), + APPCODE("OC", "AppCode", PlatformUtils.APPCODE_PREFIX), + CLION("CL", "CLion", PlatformUtils.CLION_PREFIX), + DBE("DB", "0xDBE", PlatformUtils.DBE_PREFIX), + ANDROID_STUDIO("AI", "Android Studio", "AndroidStudio"); + + private String myProductCode; + private String myName; + private String myPlatformPrefix; + + public String getName() { + return myName; + } + + public String getPlatformPrefix() { + return myPlatformPrefix; + } + + IntelliJPlatformProduct(String productCode, String name, String platformPrefix) { + myProductCode = productCode; + myName = name; + myPlatformPrefix = platformPrefix; + } + + public static IntelliJPlatformProduct fromBuildNumber(String buildNumber) { + for (IntelliJPlatformProduct product : values()) { + if (buildNumber.startsWith(product.myProductCode)) { + return product; + } + } + return IDEA; + } +} diff --git a/plugins/devkit/src/run/IdeaLicenseHelper.java b/plugins/devkit/src/run/IdeaLicenseHelper.java index ca8e3115547f..fcef98e1950f 100644 --- a/plugins/devkit/src/run/IdeaLicenseHelper.java +++ b/plugins/devkit/src/run/IdeaLicenseHelper.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -15,12 +15,10 @@ */ package org.jetbrains.idea.devkit.run; +import com.google.common.io.PatternFilenameFilter; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.util.io.FileUtil; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; @@ -30,59 +28,22 @@ import java.io.IOException; * Date: Dec 3, 2004 */ public class IdeaLicenseHelper { - @NonNls private static final String LICENSE_PATH_PREFERRED = "idea80.key"; - @NonNls private static final String LICENSE_PATH_70 = "idea70.key"; - @NonNls private static final String LICENSE_PATH_60 = "idea60.key"; - @NonNls private static final String LICENSE_PATH_50 = "idea50.key"; - @NonNls private static final String LICENSE_PATH_40 = "idea40.key"; - @NonNls private static final String LICENSE_PATH_SYSTEM = "idea.license"; - - @NonNls private static final String CONFIG_DIR_NAME = "config"; - private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.devkit.run.IdeaLicenseHelper"); - @Nullable - public static File isIDEALicenseInSandbox(@NonNls final String configPath, @NonNls final String systemPath, @NonNls final String binPath){ - final File config = new File(configPath, LICENSE_PATH_PREFERRED); - if (config.exists()){ - return config; - } - final File idea70 = new File(configPath, LICENSE_PATH_70); - if (idea70.exists()){ - return idea70; - } - final File idea60 = new File(configPath, LICENSE_PATH_60); - if (idea60.exists()){ - return idea60; - } - final File idea5 = new File(configPath, LICENSE_PATH_50); - if (idea5.exists()){ - return idea5; - } - final File idea4 = new File(configPath, LICENSE_PATH_40); - if (idea4.exists()){ - return idea4; - } - final File system = new File(systemPath, LICENSE_PATH_SYSTEM); - if (system.exists()){ - return system; - } - final File bin = new File(binPath, LICENSE_PATH_SYSTEM); - if (bin.exists()){ - return bin; - } - return null; - } - - public static void copyIDEALicense(final String sandboxHome, Sdk jdk){ - if (isIDEALicenseInSandbox(sandboxHome + File.separator + CONFIG_DIR_NAME, sandboxHome + File.separator + "system", jdk.getHomePath() + File.separator + "bin") == null){ - final File ideaLicense = isIDEALicenseInSandbox(PathManager.getConfigPath(), PathManager.getSystemPath(), PathManager.getBinPath()); - if (ideaLicense != null){ - try { - FileUtil.copy(ideaLicense, new File(new File(sandboxHome, CONFIG_DIR_NAME), LICENSE_PATH_PREFERRED)); - } - catch (IOException e) { - LOG.error(e); + public static void copyIDEALicense(final String sandboxHome) { + File sandboxSystemPath = new File(sandboxHome, "system"); + File systemPath = new File(PathManager.getSystemPath()); + File[] runningIdeaLicenses = systemPath.listFiles(new PatternFilenameFilter("idea\\d+\\.key")); + if (runningIdeaLicenses != null) { + for (File license : runningIdeaLicenses) { + File devIdeaLicense = new File(sandboxSystemPath, license.getName()); + if (!devIdeaLicense.exists()) { + try { + FileUtil.copy(license, devIdeaLicense); + } + catch (IOException e) { + LOG.error(e); + } } } } diff --git a/plugins/devkit/src/run/PluginRunConfiguration.java b/plugins/devkit/src/run/PluginRunConfiguration.java index 2c7361f8f571..cf20cd906cb3 100644 --- a/plugins/devkit/src/run/PluginRunConfiguration.java +++ b/plugins/devkit/src/run/PluginRunConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -38,6 +38,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.devkit.DevKitBundle; import org.jetbrains.idea.devkit.projectRoots.IdeaJdk; +import org.jetbrains.idea.devkit.projectRoots.IntelliJPlatformProduct; import org.jetbrains.idea.devkit.projectRoots.Sandbox; import java.io.File; @@ -97,7 +98,7 @@ public class PluginRunConfiguration extends RunConfigurationBase implements Modu final String canonicalSandbox = sandboxHome; //copy license from running instance of idea - IdeaLicenseHelper.copyIDEALicense(sandboxHome, ideaJdk); + IdeaLicenseHelper.copyIDEALicense(sandboxHome); final JavaCommandLineState state = new JavaCommandLineState(env) { @Override @@ -141,37 +142,9 @@ public class PluginRunConfiguration extends RunConfigurationBase implements Modu if (!vm.hasProperty(PlatformUtils.PLATFORM_PREFIX_KEY)) { String buildNumber = IdeaJdk.getBuildNumber(usedIdeaJdk.getHomePath()); + if (buildNumber != null) { - String prefix = null; - - if (buildNumber.startsWith("IC")) { - prefix = PlatformUtils.IDEA_CE_PREFIX; - } - else if (buildNumber.startsWith("PY")) { - prefix = PlatformUtils.PYCHARM_PREFIX; - } - else if (buildNumber.startsWith("PC")) { - prefix = PlatformUtils.PYCHARM_CE_PREFIX; - } - else if (buildNumber.startsWith("RM")) { - prefix = PlatformUtils.RUBY_PREFIX; - } - else if (buildNumber.startsWith("PS")) { - prefix = PlatformUtils.PHP_PREFIX; - } - else if (buildNumber.startsWith("WS")) { - prefix = PlatformUtils.WEB_PREFIX; - } - else if (buildNumber.startsWith("OC")) { - prefix = PlatformUtils.APPCODE_PREFIX; - } - else if (buildNumber.startsWith("CL")) { - prefix = PlatformUtils.CLION_PREFIX; - } - else if (buildNumber.startsWith("DB")) { - prefix = PlatformUtils.DBE_PREFIX; - } - + String prefix = IntelliJPlatformProduct.fromBuildNumber(buildNumber).getPlatformPrefix(); if (prefix != null) { vm.defineProperty(PlatformUtils.PLATFORM_PREFIX_KEY, prefix); } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/synthetic/GroovyScriptClass.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/synthetic/GroovyScriptClass.java index d893d3ff1f74..38c3b37f3c5c 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/synthetic/GroovyScriptClass.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/synthetic/GroovyScriptClass.java @@ -31,6 +31,7 @@ import com.intellij.psi.util.CachedValuesManager; import com.intellij.psi.util.PsiUtil; import com.intellij.ui.RowIcon; import com.intellij.util.IncorrectOperationException; +import com.intellij.util.PathUtil; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -492,7 +493,7 @@ public class GroovyScriptClass extends LightElement implements PsiClass, Synthet @Override public PsiElement setName(@NotNull String name) throws IncorrectOperationException { - myFile.setName(name + "." + myFile.getViewProvider().getVirtualFile().getExtension()); + myFile.setName(PathUtil.makeFileName(name, myFile.getViewProvider().getVirtualFile().getExtension())); return this; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/runner/DefaultGroovyScriptRunner.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/runner/DefaultGroovyScriptRunner.java index 28ed24c25a35..3810fd81cea0 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/runner/DefaultGroovyScriptRunner.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/runner/DefaultGroovyScriptRunner.java @@ -22,12 +22,14 @@ import com.intellij.execution.Executor; import com.intellij.execution.configurations.JavaParameters; import com.intellij.execution.configurations.RunProfile; import com.intellij.execution.runners.ExecutionUtil; +import com.intellij.execution.util.ScriptFileUtil; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ui.configuration.ClasspathEditor; import com.intellij.openapi.roots.ui.configuration.ModulesConfigurator; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.encoding.EncodingManager; @@ -77,7 +79,8 @@ public class DefaultGroovyScriptRunner extends GroovyScriptRunner { params.getProgramParametersList().add("--debug"); } - params.getProgramParametersList().add(FileUtil.toSystemDependentName(configuration.getScriptPath())); + String path = ScriptFileUtil.getLocalFilePath(StringUtil.notNullize(configuration.getScriptPath())); + params.getProgramParametersList().add(FileUtil.toSystemDependentName(path)); params.getProgramParametersList().addParametersString(configuration.getScriptParameters()); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/runner/GroovyScriptRunConfiguration.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/runner/GroovyScriptRunConfiguration.java index 3d9ed638717e..d48b3dab792c 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/runner/GroovyScriptRunConfiguration.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/runner/GroovyScriptRunConfiguration.java @@ -22,6 +22,7 @@ import com.intellij.execution.process.ProcessAdapter; import com.intellij.execution.process.ProcessEvent; import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.util.ProgramParametersUtil; +import com.intellij.execution.util.ScriptFileUtil; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.PathMacroManager; import com.intellij.openapi.diagnostic.Logger; @@ -37,8 +38,6 @@ import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.JDOMExternalizer; import com.intellij.openapi.util.WriteExternalException; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.psi.PsiClass; @@ -47,8 +46,10 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; import com.intellij.refactoring.listeners.RefactoringElementAdapter; import com.intellij.refactoring.listeners.RefactoringElementListener; +import com.intellij.util.ObjectUtils; import com.intellij.util.PathUtil; import com.intellij.util.SystemProperties; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.hash.LinkedHashMap; import org.jdom.Element; import org.jetbrains.annotations.NotNull; @@ -175,7 +176,7 @@ public class GroovyScriptRunConfiguration extends ModuleBasedConfiguration {// 30 + List var1 = Arrays.asList(new Integer[]{Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3), Integer.valueOf(4), Integer.valueOf(5), Integer.valueOf(6), Integer.valueOf(7)});// 29 + int var2 = (int)Math.random();// 30 + var1.forEach((var2x) -> {// 32 int var3 = 2 * var2x.intValue(); System.out.println(var3 + var2 + this.field); }); } public void testLambda1() { - int var1 = (int)Math.random();// 37 + int var1 = (int)Math.random();// 39 Runnable var2 = () -> { System.out.println("hello1" + var1); - };// 38 + };// 40 Runnable var3 = () -> { System.out.println("hello2" + var1); - };// 39 + };// 41 } public void testLambda2() { - reduce((var0, var1) -> {// 43 + reduce((var0, var1) -> {// 45 return Math.max(var0, var1); }); } public void testLambda3() { - reduce(Math::max);// 47 + reduce(Math::max);// 49 } public void testLambda4() { - reduce(TestClassLambda::localMax);// 51 + reduce(TestClassLambda::localMax);// 53 } public void testLambda5() { - String var1 = "abcd";// 55 - function(var1::toString);// 56 + String var1 = "abcd";// 57 + function(var1::toString);// 58 } public void testLambda6() { - ArrayList var1 = new ArrayList();// 60 - int var2 = var1.size() * 2;// 61 - int var3 = var1.size() * 5;// 62 - var1.removeIf((var2x) -> {// 63 + ArrayList var1 = new ArrayList();// 62 + int var2 = var1.size() * 2;// 63 + int var3 = var1.size() * 5;// 64 + var1.removeIf((var2x) -> {// 65 return var2 >= var2x.length() && var2x.length() <= var3; }); } + public static void testLambda7(Annotation[] var0) { + Arrays.stream(var0).map(Annotation::annotationType);// 69 + } + public static OptionalInt reduce(IntBinaryOperator var0) { - return null;// 67 + return null;// 73 } public static String function(Supplier var0) { - return (String)var0.get();// 71 + return (String)var0.get();// 77 } public static int localMax(int var0, int var1) { - return 0;// 75 + return 0;// 81 } public void nestedLambdas() { - byte var1 = 5;// 79 + byte var1 = 5;// 85 Runnable var2 = () -> { Runnable var1x = () -> { System.out.println("hello2" + var1); }; System.out.println("hello1" + var1); - };// 80 + };// 86 } } class 'pkg/TestClassLambda' { method 'testLambda ()V' { - 7 15 - 8 15 - e 15 - f 15 - 15 15 - 16 15 - 1c 15 - 1d 15 - 23 15 - 24 15 - 2a 15 - 2c 15 - 33 15 - 35 15 - 39 15 - 3c 15 - 3d 16 - 40 16 - 41 16 - 4a 17 + 7 17 + 8 17 + e 17 + f 17 + 15 17 + 16 17 + 1c 17 + 1d 17 + 23 17 + 24 17 + 2a 17 + 2c 17 + 33 17 + 35 17 + 39 17 + 3c 17 + 3d 18 + 40 18 + 41 18 + 4a 19 } method 'testLambda1 ()V' { - 0 24 - 3 24 - 4 24 - b 27 - 12 30 + 0 26 + 3 26 + 4 26 + b 29 + 12 32 } method 'testLambda2 ()V' { - 5 34 + 5 36 } method 'testLambda3 ()V' { - 5 40 + 5 42 } method 'testLambda4 ()V' { - 5 44 + 5 46 } method 'testLambda5 ()V' { - 0 48 - 2 48 - e 49 + 0 50 + 2 50 + e 51 } method 'testLambda6 ()V' { - 7 53 - 9 54 - e 54 - f 54 - 10 54 - 12 55 - 17 55 - 18 55 - 19 55 - 22 56 + 7 55 + 9 56 + e 56 + f 56 + 10 56 + 12 57 + 17 57 + 18 57 + 19 57 + 22 58 + } + + method 'testLambda7 ([Ljava/lang/annotation/Annotation;)V' { + 1 64 + 9 64 } method 'reduce (Ljava/util/function/IntBinaryOperator;)Ljava/util/OptionalInt;' { - 0 62 - 1 62 + 0 68 + 1 68 } method 'function (Ljava/util/function/Supplier;)Ljava/lang/String;' { - 1 66 - 6 66 - 9 66 + 1 72 + 6 72 + 9 72 } method 'localMax (II)I' { - 0 70 - 1 70 + 0 76 + 1 76 } method 'nestedLambdas ()V' { - 0 74 - 1 74 - 8 80 + 0 80 + 1 80 + 8 86 } } Lines mapping: -27 <-> 16 -28 <-> 17 -30 <-> 18 -37 <-> 25 -38 <-> 28 -39 <-> 31 -43 <-> 35 -47 <-> 41 -51 <-> 45 -55 <-> 49 -56 <-> 50 -60 <-> 54 -61 <-> 55 +29 <-> 18 +30 <-> 19 +32 <-> 20 +39 <-> 27 +40 <-> 30 +41 <-> 33 +45 <-> 37 +49 <-> 43 +53 <-> 47 +57 <-> 51 +58 <-> 52 62 <-> 56 63 <-> 57 -67 <-> 63 -71 <-> 67 -75 <-> 71 -79 <-> 75 -80 <-> 81 +64 <-> 58 +65 <-> 59 +69 <-> 65 +73 <-> 69 +77 <-> 73 +81 <-> 77 +85 <-> 81 +86 <-> 87 diff --git a/plugins/java-decompiler/engine/testData/src/pkg/TestClassLambda.java b/plugins/java-decompiler/engine/testData/src/pkg/TestClassLambda.java index 1c29cd5292ad..860a5cdafb24 100644 --- a/plugins/java-decompiler/engine/testData/src/pkg/TestClassLambda.java +++ b/plugins/java-decompiler/engine/testData/src/pkg/TestClassLambda.java @@ -15,7 +15,9 @@ */ package pkg; +import java.lang.annotation.Annotation; import java.util.*; +import java.util.Arrays; import java.util.function.IntBinaryOperator; import java.util.function.Supplier; @@ -63,6 +65,10 @@ public class TestClassLambda { list.removeIf(s -> (bottom >= s.length() && s.length() <= top)); } + public static void testLambda7(Annotation[] annotations) { + Arrays.stream(annotations).map(Annotation::annotationType); + } + public static OptionalInt reduce(IntBinaryOperator op) { return null; } diff --git a/plugins/java-decompiler/plugin/test/org/jetbrains/java/decompiler/IdeaDecompilerTest.java b/plugins/java-decompiler/plugin/test/org/jetbrains/java/decompiler/IdeaDecompilerTest.java index f2ed7234414e..ba87248bf961 100644 --- a/plugins/java-decompiler/plugin/test/org/jetbrains/java/decompiler/IdeaDecompilerTest.java +++ b/plugins/java-decompiler/plugin/test/org/jetbrains/java/decompiler/IdeaDecompilerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,6 @@ import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; -import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.registry.RegistryValue; @@ -38,12 +37,10 @@ import com.intellij.testFramework.PlatformTestUtil; import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; import com.intellij.util.Alarm; import com.intellij.util.ThrowableRunnable; -import com.intellij.util.containers.ContainerUtil; import com.intellij.util.io.URLUtil; import org.jetbrains.annotations.NotNull; import java.awt.*; -import java.util.Set; public class IdeaDecompilerTest extends LightCodeInsightFixtureTestCase { @Override @@ -79,7 +76,7 @@ public class IdeaDecompilerTest extends LightCodeInsightFixtureTestCase { if (file.isDirectory()) { System.out.println(file.getPath()); } - else if (file.getFileType() == StdFileTypes.CLASS && !file.getName().contains("$") && !skip(file)) { + else if (file.getFileType() == StdFileTypes.CLASS && !file.getName().contains("$")) { PsiFile clsFile = getPsiManager().findFile(file); assertNotNull(file.getPath(), clsFile); PsiElement mirror = ((ClsFileImpl)clsFile).getMirror(); @@ -97,20 +94,6 @@ public class IdeaDecompilerTest extends LightCodeInsightFixtureTestCase { } return true; } - - private boolean skip(VirtualFile file) { - if (!SystemInfo.isJavaVersionAtLeast("1.8")) return false; - String path = file.getPath(); - int p = path.indexOf("!/"); - return p > 0 && knowProblems.contains(path.substring(p + 2)); - } - - // todo[r.sh] drop when IDEA-129734 get fixed - private final Set knowProblems = ContainerUtil.newHashSet( - "java/lang/reflect/AnnotatedElement.class", "java/util/stream/Nodes.class", "java/util/stream/FindOps.class", - "java/util/stream/Collectors.class", "java/util/stream/DistinctOps.class", "java/util/stream/IntPipeline.class", - "java/util/stream/LongPipeline.class", "java/util/stream/DoublePipeline.class" - ); }); } diff --git a/plugins/properties/src/com/intellij/lang/properties/editor/NewPropertyAction.java b/plugins/properties/src/com/intellij/lang/properties/editor/NewPropertyAction.java index 76be016f4f49..af29c65a45e3 100644 --- a/plugins/properties/src/com/intellij/lang/properties/editor/NewPropertyAction.java +++ b/plugins/properties/src/com/intellij/lang/properties/editor/NewPropertyAction.java @@ -20,12 +20,10 @@ import com.intellij.lang.properties.PropertiesBundle; import com.intellij.lang.properties.ResourceBundle; import com.intellij.lang.properties.psi.PropertiesFile; import com.intellij.lang.properties.structureView.PropertiesPrefixGroup; -import com.intellij.openapi.actionSystem.ActionPlaces; -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.PlatformDataKeys; +import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; +import com.intellij.openapi.editor.Editor; import com.intellij.openapi.fileEditor.FileEditor; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.InputValidator; @@ -55,11 +53,15 @@ class NewPropertyAction extends AnAction { if (project == null) { return; } - final FileEditor editor = PlatformDataKeys.FILE_EDITOR.getData(e.getDataContext()); - if (editor == null || !(editor instanceof ResourceBundleEditor)) { - return; + final ResourceBundleEditor resourceBundleEditor; + final DataContext context = e.getDataContext(); + FileEditor fileEditor = PlatformDataKeys.FILE_EDITOR.getData(context); + if (fileEditor instanceof ResourceBundleEditor) { + resourceBundleEditor = (ResourceBundleEditor)fileEditor; + } else { + final Editor editor = CommonDataKeys.EDITOR.getData(context); + resourceBundleEditor = editor != null ? editor.getUserData(ResourceBundleEditor.RESOURCE_BUNDLE_EDITOR_KEY) : null; } - final ResourceBundleEditor resourceBundleEditor = (ResourceBundleEditor)editor; final String prefix; final String separator; diff --git a/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditor.java b/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditor.java index 40e5d679af90..921aedb8cf48 100644 --- a/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditor.java +++ b/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditor.java @@ -47,6 +47,7 @@ import com.intellij.openapi.fileEditor.*; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.Key; import com.intellij.openapi.util.UserDataHolderBase; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.*; @@ -85,6 +86,7 @@ public class ResourceBundleEditor extends UserDataHolderBase implements FileEdit Logger.getInstance("#com.intellij.lang.properties.editor.ResourceBundleEditor"); @NonNls private static final String VALUES = "values"; @NonNls private static final String NO_PROPERTY_SELECTED = "noPropertySelected"; + public static final Key RESOURCE_BUNDLE_EDITOR_KEY = Key.create("resourceBundleEditor"); private final StructureViewComponent myStructureViewComponent; private final Map myEditors; @@ -798,11 +800,12 @@ public class ResourceBundleEditor extends UserDataHolderBase implements FileEdit } } - private static Editor createEditor() { + private Editor createEditor() { EditorFactory editorFactory = EditorFactory.getInstance(); Document document = editorFactory.createDocument(""); EditorEx editor = (EditorEx)editorFactory.createEditor(document); reinitSettings(editor); + editor.putUserData(RESOURCE_BUNDLE_EDITOR_KEY, this); return editor; } diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/StudyDocumentListener.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/StudyDocumentListener.java index 9714472a5649..a7a2e0debeaa 100644 --- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/StudyDocumentListener.java +++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/StudyDocumentListener.java @@ -28,6 +28,9 @@ public class StudyDocumentListener extends DocumentAdapter { // with fragments containing "\n" @Override public void beforeDocumentChange(DocumentEvent e) { + if (!myTaskFile.isTrackChanges()) { + return; + } Document document = e.getDocument(); myTaskWindows.clear(); for (TaskWindow taskWindow : myTaskFile.getTaskWindows()) { @@ -39,6 +42,9 @@ public class StudyDocumentListener extends DocumentAdapter { @Override public void documentChanged(DocumentEvent e) { + if (!myTaskFile.isTrackChanges()) { + return; + } if (e instanceof DocumentEventImpl) { DocumentEventImpl event = (DocumentEventImpl)e; Document document = e.getDocument(); diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/StudyState.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/StudyState.java index 4c382e0cc622..1e890b0ebd30 100644 --- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/StudyState.java +++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/StudyState.java @@ -6,6 +6,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.jetbrains.edu.learning.course.Task; import com.jetbrains.edu.learning.course.TaskFile; import com.jetbrains.edu.learning.editor.StudyEditor; +import org.jetbrains.annotations.Nullable; public class StudyState { private final StudyEditor myStudyEditor; @@ -15,7 +16,7 @@ public class StudyState { private final Task myTask; private final VirtualFile myTaskDir; - public StudyState(final StudyEditor studyEditor) { + public StudyState(@Nullable final StudyEditor studyEditor) { myStudyEditor = studyEditor; myEditor = studyEditor != null ? studyEditor.getEditor() : null; myTaskFile = studyEditor != null ? studyEditor.getTaskFile() : null; diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/StudyUtils.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/StudyUtils.java index 9262ac6c7041..6f5f211f5ae1 100644 --- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/StudyUtils.java +++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/StudyUtils.java @@ -272,4 +272,10 @@ public class StudyUtils { extensions[0].setCommandLineParameters(cmd, project, filePath, pythonPath, currentTask); } } + + public static void enableAction(@NotNull final AnActionEvent event, boolean isEnable) { + final Presentation presentation = event.getPresentation(); + presentation.setVisible(isEnable); + presentation.setEnabled(isEnable); + } } diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyRefreshTaskFileAction.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyRefreshTaskFileAction.java index 3458d252a15b..c2ef5ab211a5 100644 --- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyRefreshTaskFileAction.java +++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyRefreshTaskFileAction.java @@ -4,6 +4,7 @@ import com.intellij.ide.projectView.ProjectView; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.application.ApplicationManager; 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.fileEditor.FileDocumentManager; @@ -18,8 +19,7 @@ import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.wm.IdeFocusManager; -import com.jetbrains.edu.learning.StudyDocumentListener; -import com.jetbrains.edu.learning.StudyTaskManager; +import com.jetbrains.edu.learning.StudyState; import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.course.*; import com.jetbrains.edu.learning.editor.StudyEditor; @@ -30,8 +30,9 @@ import java.io.File; public class StudyRefreshTaskFileAction extends DumbAwareAction { 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()); - public static void refresh(final Project project) { + public static void refresh(@NotNull final Project project) { ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { @@ -39,51 +40,54 @@ public class StudyRefreshTaskFileAction extends DumbAwareAction { @SuppressWarnings("IOResourceOpenedButNotSafelyClosed") @Override public void run() { - final Editor editor = StudyEditor.getSelectedEditor(project); - assert editor != null; - final Document document = editor.getDocument(); - refreshFile(editor, document, project); + StudyEditor studyEditor = StudyEditor.getSelectedStudyEditor(project); + StudyState studyState = new StudyState(studyEditor); + if (studyEditor == null || !studyState.isValid()) { + LOG.info("RefreshTaskFileAction was invoked outside of Study Editor"); + return; + } + refreshFile(studyState, project); } }); } }); } - public static void refreshFile(@NotNull final Editor editor, @NotNull final Document document, @NotNull final Project project) { - StudyTaskManager taskManager = StudyTaskManager.getInstance(project); - Course course = taskManager.getCourse(); - assert course != null; - FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance(); - VirtualFile openedFile = fileDocumentManager.getFile(document); - assert openedFile != null; - final TaskFile selectedTaskFile = taskManager.getTaskFile(openedFile); - assert selectedTaskFile != null; - String openedFileName = openedFile.getName(); - Task currentTask = selectedTaskFile.getTask(); - resetTaskFile(document, project, course, selectedTaskFile, openedFileName, currentTask); - selectedTaskFile.drawAllWindows(editor); - selectedTaskFile.createGuardedBlocks(document, editor); + private static void refreshFile(@NotNull final StudyState studyState, @NotNull final Project project) { + final Editor editor = studyState.getEditor(); + final TaskFile taskFile = studyState.getTaskFile(); + if (!resetTaskFile(editor.getDocument(), project, taskFile, studyState.getVirtualFile().getName())) { + return; + } + taskFile.drawAllWindows(editor); + taskFile.createGuardedBlocks(editor); ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { IdeFocusManager.getInstance(project).requestFocus(editor.getContentComponent(), true); } }); - selectedTaskFile.navigateToFirstTaskWindow(editor); - showBalloon(project); + taskFile.navigateToFirstTaskWindow(editor); + showBalloon(project, "You can start again now", MessageType.INFO); } - public static void resetTaskFile(Document document, Project project, Course course, TaskFile taskFile, String name, Task task) { - resetDocument(document, course, name, task); - updateLessonInfo(task); + private static boolean resetTaskFile(@NotNull final Document document, + @NotNull final Project project, + TaskFile taskFile, + String name) { + if (!resetDocument(project, document, taskFile, name)) { + return false; + } + updateLessonInfo(taskFile.getTask()); StudyUtils.updateStudyToolWindow(project); resetTaskWindows(taskFile); ProjectView.getInstance(project).refresh(); + return true; } - private static void showBalloon(Project project) { + private static void showBalloon(@NotNull final Project project, String text, @NotNull final MessageType messageType) { BalloonBuilder balloonBuilder = - JBPopupFactory.getInstance().createHtmlTextBalloonBuilder("You can start again now", MessageType.INFO, null); + JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(text, messageType, null); final Balloon balloon = balloonBuilder.createBalloon(); StudyEditor selectedStudyEditor = StudyEditor.getSelectedStudyEditor(project); assert selectedStudyEditor != null; @@ -104,33 +108,38 @@ public class StudyRefreshTaskFileAction extends DumbAwareAction { lessonInfo.update(StudyStatus.Unchecked, +1); } - private static void resetDocument(final Document document, Course course, String fileName, Task task) { + private static boolean resetDocument(@NotNull final Project project, + @NotNull final Document document, + @NotNull final TaskFile taskFile, + String fileName) { StudyEditor.deleteGuardedBlocks(document); - StudyDocumentListener listener = StudyEditor.getListener(document); - if (listener != null) { - document.removeDocumentListener(listener); - } + taskFile.setTrackChanges(false); clearDocument(document); + Task task = taskFile.getTask(); String lessonDir = Lesson.LESSON_DIR + String.valueOf(task.getLesson().getIndex() + 1); String taskDir = Task.TASK_DIR + String.valueOf(task.getIndex() + 1); + Course course = task.getLesson().getCourse(); File resourceFile = new File(course.getResourcePath()); File resourceRoot = resourceFile.getParentFile(); + if (!resourceFile.exists() || resourceRoot == null) { + showBalloon(project, "Course was deleted", MessageType.ERROR); + return false; + } String patternPath = FileUtil.join(resourceRoot.getPath(), lessonDir, taskDir, fileName); VirtualFile patternFile = VfsUtil.findFileByIoFile(new File(patternPath), true); if (patternFile == null) { - return; + return false; } - Document patternDocument = FileDocumentManager.getInstance().getDocument(patternFile); + final Document patternDocument = FileDocumentManager.getInstance().getDocument(patternFile); if (patternDocument == null) { - return; + return false; } document.setText(patternDocument.getCharsSequence()); - if (listener != null) { - document.addDocumentListener(listener); - } + taskFile.setTrackChanges(true); + return true; } - private static void clearDocument(final Document document) { + private static void clearDocument(@NotNull final Document document) { final int lineCount = document.getLineCount(); if (lineCount != 0) { CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() { @@ -142,7 +151,23 @@ public class StudyRefreshTaskFileAction extends DumbAwareAction { } } - public void actionPerformed(@NotNull AnActionEvent e) { - refresh(e.getProject()); + public void actionPerformed(@NotNull AnActionEvent event) { + final Project project = event.getProject(); + if (project != null) { + refresh(project); + } + } + + @Override + public void update(AnActionEvent event) { + final Project project = event.getProject(); + if (project != null) { + StudyEditor studyEditor = StudyEditor.getSelectedStudyEditor(project); + StudyState studyState = new StudyState(studyEditor); + if (studyState.isValid()) { + StudyUtils.enableAction(event, true); + } + } + StudyUtils.enableAction(event, false); } } diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/course/Lesson.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/course/Lesson.java index 34be407bf5ef..fef02eff0c60 100644 --- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/course/Lesson.java +++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/course/Lesson.java @@ -108,4 +108,8 @@ public class Lesson implements Stateful { } return myCourse.getLessons().get(myIndex - 1); } + + public Course getCourse() { + return myCourse; + } } diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/course/TaskFile.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/course/TaskFile.java index 1f5f7ebbbb52..5e835e076ac5 100644 --- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/course/TaskFile.java +++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/course/TaskFile.java @@ -36,6 +36,7 @@ public class TaskFile implements Stateful { private TaskWindow mySelectedTaskWindow = null; public int myIndex = -1; private boolean myUserCreated = false; + private boolean myTrackChanges = true; /** * @return if all the windows in task file are marked as resolved @@ -107,7 +108,7 @@ public class TaskFile implements Stateful { final Document document = editor.getDocument(); EditorActionManager.getInstance() .setReadonlyFragmentModificationHandler(document, new TaskWindowDeleteHandler(editor)); - createGuardedBlocks(document, editor); + createGuardedBlocks(editor); editor.getColorsScheme().setColor(EditorColors.READONLY_FRAGMENT_BACKGROUND_COLOR, null); } @@ -227,11 +228,15 @@ public class TaskFile implements Stateful { /** * Marks symbols adjacent to task windows as read-only fragments */ - public void createGuardedBlocks(@NotNull final Document document, @NotNull final Editor editor) { + public void createGuardedBlocks(@NotNull final Editor editor) { + final Document document = editor.getDocument(); if (document instanceof DocumentImpl) { DocumentImpl documentImpl = (DocumentImpl)document; List blocks = documentImpl.getGuardedBlocks(); for (TaskWindow taskWindow : taskWindows) { + if (!taskWindow.isValid(document)) { + return; + } int start = taskWindow.getRealStartOffset(document); int end = start + taskWindow.getLength(); if (start != 0) { @@ -249,4 +254,12 @@ public class TaskFile implements Stateful { .addRangeHighlighter(start, end, HighlighterLayer.LAST + 1, null, HighlighterTargetArea.EXACT_RANGE); blocks.add(rh); } + + public boolean isTrackChanges() { + return myTrackChanges; + } + + public void setTrackChanges(boolean trackChanges) { + myTrackChanges = trackChanges; + } } diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/editor/StudyEditor.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/editor/StudyEditor.java index a5c7edb9291a..19e0b47183c3 100644 --- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/editor/StudyEditor.java +++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/editor/StudyEditor.java @@ -59,7 +59,7 @@ import java.util.Map; /** * Implementation of StudyEditor which has panel with special buttons and task text - * also @see {@link com.jetbrains.python.edu.editor.StudyFileEditorProvider} + * also @see {@link com.jetbrains.edu.learning.editor.StudyFileEditorProvider} */ public class StudyEditor implements TextEditor { private static final String TASK_TEXT_HEADER = "Task Text"; diff --git a/python/testSrc/com/jetbrains/env/PyEnvTestCase.java b/python/testSrc/com/jetbrains/env/PyEnvTestCase.java index 853a5504de63..4907b1e27ddf 100644 --- a/python/testSrc/com/jetbrains/env/PyEnvTestCase.java +++ b/python/testSrc/com/jetbrains/env/PyEnvTestCase.java @@ -1,13 +1,17 @@ package com.jetbrains.env; import com.google.common.collect.Lists; +import com.intellij.execution.ExecutionException; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.util.text.StringUtil; import com.intellij.testFramework.UsefulTestCase; import com.intellij.util.SystemProperties; import com.intellij.util.ui.UIUtil; import com.jetbrains.python.fixtures.PyTestCase; +import com.jetbrains.python.packaging.PyPackage; +import com.jetbrains.python.packaging.PyPackageManager; import org.hamcrest.Matchers; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -57,6 +61,11 @@ public abstract class PyEnvTestCase extends UsefulTestCase { PyTestCase.initPlatformPrefix(); } + @Nullable + public static PyPackage getInstalledDjango(@NotNull final Sdk sdk) throws ExecutionException { + return PyPackageManager.getInstance(sdk).findPackage("django", false); + } + @Override public void setUp() throws Exception { super.setUp(); diff --git a/python/testSrc/com/jetbrains/python/PyBinaryModuleCompletionTest.java b/python/testSrc/com/jetbrains/python/PyBinaryModuleCompletionTest.java index 009212fee4bd..eb0b3ef56acf 100644 --- a/python/testSrc/com/jetbrains/python/PyBinaryModuleCompletionTest.java +++ b/python/testSrc/com/jetbrains/python/PyBinaryModuleCompletionTest.java @@ -16,6 +16,7 @@ package com.jetbrains.python; import com.intellij.testFramework.LightProjectDescriptor; +import com.jetbrains.python.fixtures.PyLightProjectDescriptor; import com.jetbrains.python.fixtures.PyTestCase; /** diff --git a/python/testSrc/com/jetbrains/python/fixtures/PyLightProjectDescriptor.java b/python/testSrc/com/jetbrains/python/fixtures/PyLightProjectDescriptor.java new file mode 100644 index 000000000000..8569b2faf51e --- /dev/null +++ b/python/testSrc/com/jetbrains/python/fixtures/PyLightProjectDescriptor.java @@ -0,0 +1,65 @@ +/* + * Copyright 2000-2015 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.fixtures; + +import com.intellij.openapi.application.PathManager; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleType; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.roots.ContentEntry; +import com.intellij.openapi.roots.ModifiableRootModel; +import com.intellij.openapi.roots.OrderRootType; +import com.intellij.openapi.roots.libraries.Library; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.testFramework.LightProjectDescriptor; +import com.jetbrains.python.PythonMockSdk; +import com.jetbrains.python.PythonModuleTypeBase; + +/** + * Project descriptor (extracted from {@link com.jetbrains.python.fixtures.PyTestCase}) and should be used with it. + * @author Ilya.Kazakevich +*/ +public class PyLightProjectDescriptor implements LightProjectDescriptor { + private final String myPythonVersion; + + public PyLightProjectDescriptor(String pythonVersion) { + myPythonVersion = pythonVersion; + } + + @Override + public ModuleType getModuleType() { + return PythonModuleTypeBase.getInstance(); + } + + @Override + public Sdk getSdk() { + return PythonMockSdk.findOrCreate(myPythonVersion); + } + + @Override + public void configureModule(Module module, ModifiableRootModel model, ContentEntry contentEntry) { + } + + protected void createLibrary(ModifiableRootModel model, final String name, final String path) { + final Library.ModifiableModel modifiableModel = model.getModuleLibraryTable().createLibrary(name).getModifiableModel(); + final VirtualFile home = + LocalFileSystem.getInstance().refreshAndFindFileByPath(PathManager.getHomePath() + path); + + modifiableModel.addRoot(home, OrderRootType.CLASSES); + modifiableModel.commit(); + } +} diff --git a/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java b/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java index af93b8178dca..ddd200e071d0 100644 --- a/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java +++ b/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java @@ -28,19 +28,12 @@ import com.intellij.find.findUsages.FindUsagesOptions; import com.intellij.ide.DataManager; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.PathManager; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.module.Module; -import com.intellij.openapi.module.ModuleType; -import com.intellij.openapi.projectRoots.Sdk; -import com.intellij.openapi.roots.ContentEntry; -import com.intellij.openapi.roots.ModifiableRootModel; -import com.intellij.openapi.roots.OrderRootType; import com.intellij.openapi.roots.impl.FilePropertyPusher; -import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.util.Ref; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; @@ -62,8 +55,6 @@ import com.intellij.usages.Usage; import com.intellij.usages.rules.PsiElementUsage; import com.intellij.util.CommonProcessors.CollectProcessor; import com.jetbrains.python.PythonHelpersLocator; -import com.jetbrains.python.PythonMockSdk; -import com.jetbrains.python.PythonModuleTypeBase; import com.jetbrains.python.PythonTestUtil; import com.jetbrains.python.psi.LanguageLevel; import com.jetbrains.python.psi.PyClass; @@ -318,37 +309,6 @@ public abstract class PyTestCase extends UsefulTestCase { configurator.configureProject(myFixture.getProject(), newPath, moduleRef); } - protected static class PyLightProjectDescriptor implements LightProjectDescriptor { - private final String myPythonVersion; - - public PyLightProjectDescriptor(String pythonVersion) { - myPythonVersion = pythonVersion; - } - - @Override - public ModuleType getModuleType() { - return PythonModuleTypeBase.getInstance(); - } - - @Override - public Sdk getSdk() { - return PythonMockSdk.findOrCreate(myPythonVersion); - } - - @Override - public void configureModule(Module module, ModifiableRootModel model, ContentEntry contentEntry) { - } - - protected void createLibrary(ModifiableRootModel model, final String name, final String path) { - final Library.ModifiableModel modifiableModel = model.getModuleLibraryTable().createLibrary(name).getModifiableModel(); - final VirtualFile home = - LocalFileSystem.getInstance().refreshAndFindFileByPath(PathManager.getHomePath() + path); - - modifiableModel.addRoot(home, OrderRootType.CLASSES); - modifiableModel.commit(); - } - } - public static void initPlatformPrefix() { PlatformTestCase.autodetectPlatformPrefix(); } diff --git a/resources/src/idea/JavaActions.xml b/resources/src/idea/JavaActions.xml index 6cc9ba83758f..7836da0b2add 100644 --- a/resources/src/idea/JavaActions.xml +++ b/resources/src/idea/JavaActions.xml @@ -68,10 +68,14 @@ - + + + + + diff --git a/spellchecker/src/com/intellij/spellchecker/jetbrains.dic b/spellchecker/src/com/intellij/spellchecker/jetbrains.dic index 98ec7e480eda..2427f8b381cd 100644 --- a/spellchecker/src/com/intellij/spellchecker/jetbrains.dic +++ b/spellchecker/src/com/intellij/spellchecker/jetbrains.dic @@ -184,7 +184,9 @@ giud globals google gruntfile +gruntfiles gulpfile +gulpfiles gzip gzipped hamcrest diff --git a/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/CreateNSDeclarationIntentionFix.java b/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/CreateNSDeclarationIntentionFix.java index 0a8a25b8fca3..0783da57f406 100644 --- a/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/CreateNSDeclarationIntentionFix.java +++ b/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/CreateNSDeclarationIntentionFix.java @@ -45,16 +45,14 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.impl.cache.impl.id.IdTableBuilding; import com.intellij.psi.meta.PsiMetaData; -import com.intellij.psi.xml.XmlDocument; -import com.intellij.psi.xml.XmlFile; -import com.intellij.psi.xml.XmlTag; -import com.intellij.psi.xml.XmlToken; +import com.intellij.psi.xml.*; import com.intellij.ui.components.JBList; import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; -import com.intellij.xml.XmlNamespaceHelper; import com.intellij.xml.XmlElementDescriptor; import com.intellij.xml.XmlExtension; +import com.intellij.xml.XmlNamespaceHelper; +import com.intellij.xml.XmlSchemaProvider; import com.intellij.xml.impl.schema.AnyXmlElementDescriptor; import com.intellij.xml.impl.schema.XmlNSDescriptorImpl; import com.intellij.xml.util.XmlUtil; @@ -63,7 +61,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; -import java.util.Arrays; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; @@ -144,15 +142,32 @@ public class CreateNSDeclarationIntentionFix implements HintAction, LocalQuickFi return element != null && element.isValid(); } + /** Looks up the unbound namespaces and sorts them */ + @NotNull + private List getNamespaces(PsiElement element, XmlFile xmlFile) { + if (element instanceof XmlAttribute) { + element = element.getParent(); + } + Set set = getXmlExtension().guessUnboundNamespaces(element, xmlFile); + + final String match = getUnboundNamespaceForPrefix(myNamespacePrefix, xmlFile, set); + if (match != null) { + return Collections.singletonList(match); + } + + List namespaces = new ArrayList(set); + Collections.sort(namespaces); + return namespaces; + } + @Override public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException { if (!FileModificationService.getInstance().prepareFileForWrite(file)) return; final PsiElement element = myElement.retrieve(); if (element == null) return; - final Set set = getXmlExtension().guessUnboundNamespaces(element, getFile()); - final String[] namespaces = ArrayUtil.toStringArray(set); - Arrays.sort(namespaces); + XmlFile xmlFile = getFile(); + final String[] namespaces = ArrayUtil.toStringArray(getNamespaces(element, xmlFile)); runActionOverSeveralAttributeValuesAfterLettingUserSelectTheNeededOne( namespaces, @@ -195,6 +210,22 @@ public class CreateNSDeclarationIntentionFix implements HintAction, LocalQuickFi editor); } + /** Given a prefix in a file and a set of candidate namespaces, returns the namespace that matches the prefix (if any) + * as determined by the {@link XmlSchemaProvider#getDefaultPrefix(String, XmlFile)} + * implementations */ + @Nullable + public static String getUnboundNamespaceForPrefix(String prefix, XmlFile xmlFile, Set namespaces) { + final List providers = XmlSchemaProvider.getAvailableProviders(xmlFile); + for (XmlSchemaProvider provider : providers) { + for (String namespace : namespaces) { + if (prefix.equals(provider.getDefaultPrefix(namespace, xmlFile))) { + return namespace; + } + } + } + return null; + } + private String getTitle() { return XmlErrorMessages.message("select.namespace.title", StringUtil.capitalize(getXmlExtension().getNamespaceAlias(getFile()))); } @@ -216,7 +247,7 @@ public class CreateNSDeclarationIntentionFix implements HintAction, LocalQuickFi } final PsiElement element = myElement.retrieve(); if (element == null) return false; - final Set namespaces = getXmlExtension().guessUnboundNamespaces(element, getFile()); + final List namespaces = getNamespaces(element, getFile()); if (!namespaces.isEmpty()) { final String message = ShowAutoImportPass.getMessage(namespaces.size() > 1, namespaces.iterator().next()); final String title = getTitle(); diff --git a/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/ImportNSAction.java b/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/ImportNSAction.java index d3afcd58640c..24917fdeaee5 100644 --- a/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/ImportNSAction.java +++ b/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/ImportNSAction.java @@ -25,27 +25,26 @@ import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.xml.XmlFile; import com.intellij.ui.components.JBList; +import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; import com.intellij.xml.XmlNamespaceHelper; import org.jetbrains.annotations.NotNull; import javax.swing.*; -import java.util.Arrays; import java.util.Collections; -import java.util.Set; +import java.util.List; /** * @author Dmitry Avdeev */ public class ImportNSAction implements QuestionAction { - private final Set myNamespaces; + private final List myNamespaces; private final XmlFile myFile; private final PsiElement myElement; private final Editor myEditor; private final String myTitle; - public ImportNSAction(final Set namespaces, XmlFile file, @NotNull PsiElement element, Editor editor, final String title) { - + public ImportNSAction(final List namespaces, XmlFile file, @NotNull PsiElement element, Editor editor, final String title) { myNamespaces = namespaces; myFile = file; myElement = element; @@ -55,9 +54,8 @@ public class ImportNSAction implements QuestionAction { @Override public boolean execute() { - final Object[] objects = myNamespaces.toArray(); - Arrays.sort(objects); - final JList list = new JBList(objects); + final String[] strings = ArrayUtil.toStringArray(myNamespaces); + final JList list = new JBList(strings); list.setCellRenderer(XmlNSRenderer.INSTANCE); list.setSelectedIndex(0); final int offset = myElement.getTextOffset(); diff --git a/xml/impl/src/com/intellij/xml/XmlNamespaceHelper.java b/xml/impl/src/com/intellij/xml/XmlNamespaceHelper.java index 797ae43a8ad3..058e989f3bb6 100644 --- a/xml/impl/src/com/intellij/xml/XmlNamespaceHelper.java +++ b/xml/impl/src/com/intellij/xml/XmlNamespaceHelper.java @@ -19,8 +19,10 @@ import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.extensions.Extensions; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; +import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlFile; import com.intellij.psi.xml.XmlTag; import com.intellij.util.IncorrectOperationException; @@ -55,6 +57,13 @@ public abstract class XmlNamespaceHelper { @Nullable public String getNamespacePrefix(PsiElement element) { + if (element instanceof XmlAttribute) { + XmlAttribute attribute = (XmlAttribute)element; + String prefix = attribute.getNamespacePrefix(); + if (!StringUtil.isEmpty(prefix)) { + return prefix; + } + } final PsiElement tag = element instanceof XmlTag ? element : element.getParent(); if (tag instanceof XmlTag) { return ((XmlTag)tag).getNamespacePrefix(); diff --git a/xml/xml-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/XmlUnboundNsPrefixInspection.java b/xml/xml-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/XmlUnboundNsPrefixInspection.java index a8bd40221ce9..fc05a4849506 100644 --- a/xml/xml-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/XmlUnboundNsPrefixInspection.java +++ b/xml/xml-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/XmlUnboundNsPrefixInspection.java @@ -152,6 +152,11 @@ public class XmlUnboundNsPrefixInspection extends XmlSuppressableInspectionTool LocalQuickFix fix = isOnTheFly ? XmlQuickFixFactory.getInstance().createNSDeclarationIntentionFix(context, namespacePrefix, token) : null; reportTagProblem(element, localizedMessage, range, highlightType, fix, holder); } + else if (element instanceof XmlAttribute) { + LocalQuickFix fix = isOnTheFly ? XmlQuickFixFactory.getInstance().createNSDeclarationIntentionFix(element, namespacePrefix, token) : null; + XmlAttribute attribute = (XmlAttribute)element; + holder.registerProblem(attribute.getNameElement(), localizedMessage, highlightType, range, fix); + } else { holder.registerProblem(element, localizedMessage, highlightType, range); }