diff --git a/RegExpSupport/src/org/intellij/lang/regexp/psi/impl/RegExpBackrefImpl.java b/RegExpSupport/src/org/intellij/lang/regexp/psi/impl/RegExpBackrefImpl.java index 5aa03c2cac45..63bad0e924d7 100644 --- a/RegExpSupport/src/org/intellij/lang/regexp/psi/impl/RegExpBackrefImpl.java +++ b/RegExpSupport/src/org/intellij/lang/regexp/psi/impl/RegExpBackrefImpl.java @@ -104,6 +104,7 @@ public class RegExpBackrefImpl extends RegExpElementImpl implements RegExpBackre return RegExpBackrefImpl.this.resolve(); } + @NotNull public Object[] getVariants() { return ArrayUtil.EMPTY_OBJECT_ARRAY; } diff --git a/RegExpSupport/src/org/intellij/lang/regexp/psi/impl/RegExpPropertyImpl.java b/RegExpSupport/src/org/intellij/lang/regexp/psi/impl/RegExpPropertyImpl.java index b4b9cfe87dd1..8f58881c7f96 100644 --- a/RegExpSupport/src/org/intellij/lang/regexp/psi/impl/RegExpPropertyImpl.java +++ b/RegExpSupport/src/org/intellij/lang/regexp/psi/impl/RegExpPropertyImpl.java @@ -118,6 +118,7 @@ public class RegExpPropertyImpl extends RegExpElementImpl implements RegExpPrope return false; } + @NotNull public Object[] getVariants() { final ASTNode categoryNode = getCategoryNode(); if (categoryNode != null && categoryNode.getText().startsWith("In") && !categoryNode.getText().startsWith("Intelli")) { diff --git a/build/scripts/layouts.gant b/build/scripts/layouts.gant index 2ef163ed660d..952d7834d069 100644 --- a/build/scripts/layouts.gant +++ b/build/scripts/layouts.gant @@ -159,9 +159,7 @@ def layoutFull(String home, String targetDirectory) { layoutPlugin("IntentionPowerPack", "IntentionPowerPackPlugin") layoutPlugin("ToString", "generate-tostring", "toString") - layoutPlugin("SpellChecker", "spellchecker", "spellchecker-core") { - fileset(dir: "$home/plugins/spellchecker/lib") - } + layoutPlugin("SpellChecker", "spellchecker", "spellchecker-core") layoutPlugin("maven") { diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompileContextExProxy.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompileContextExProxy.java index e312ca89abe2..5e45940a6e00 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/CompileContextExProxy.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompileContextExProxy.java @@ -141,11 +141,11 @@ public class CompileContextExProxy implements CompileContextEx { return myDelegate.isRebuild(); } - public T getUserData(final Key key) { + public T getUserData(@NotNull final Key key) { return myDelegate.getUserData(key); } - public void putUserData(final Key key, final T value) { + public void putUserData(@NotNull final Key key, final T value) { myDelegate.putUserData(key, value); } diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java index 00b601bf69bf..aef725908db7 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java @@ -1703,6 +1703,9 @@ public class CompileDriver { if (context.getMessageCount(CompilerMessageCategory.ERROR) > 0) { return false; } + if (LOG.isDebugEnabled() && items.length > 0) { + LOG.debug("Start processing files by " + adapter.getCompiler().getDescription()); + } final CompileScope scope = context.getCompileScope(); final List toProcess = new ArrayList(); final Set allUrls = new HashSet(); @@ -1806,7 +1809,6 @@ public class CompileDriver { final VirtualFile file = aProcessed.getFile(); vFiles.add(file); if (LOG.isDebugEnabled()) { - LOG.debug("File processed by " + adapter.getCompiler().getDescription()); LOG.debug("\tFile processed " + file.getPresentableUrl() + "; ts=" + file.getTimeStamp()); } diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompositeScope.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompositeScope.java index 3f15c8af5ab3..2b8195fbe556 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/CompositeScope.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompositeScope.java @@ -89,7 +89,7 @@ public class CompositeScope extends UserDataHolderBase implements CompileScope{ return modules.toArray(new Module[modules.size()]); } - public T getUserData(Key key) { + public T getUserData(@NotNull Key key) { for (CompileScope compileScope : myScopes) { T userData = compileScope.getUserData(key); if (userData != null) { diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/TrackDependenciesScope.java b/java/compiler/impl/src/com/intellij/compiler/impl/TrackDependenciesScope.java index 00a37258adf8..a26e5a50ed40 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/TrackDependenciesScope.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/TrackDependenciesScope.java @@ -66,7 +66,7 @@ public class TrackDependenciesScope extends UserDataHolderBase implements Compil } } - public T getUserData(final Key key) { + public T getUserData(@NotNull final Key key) { T userData = myDelegate.getUserData(key); if (userData != null) { return userData; diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactCompileScope.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactCompileScope.java index 4702d30f3f31..38805aa9083c 100644 --- a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactCompileScope.java +++ b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactCompileScope.java @@ -31,10 +31,7 @@ import com.intellij.util.Processor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; +import java.util.*; /** * @author nik @@ -87,9 +84,10 @@ public class ArtifactCompileScope { Set artifacts = new HashSet(); final ArtifactManager artifactManager = ArtifactManager.getInstance(project); final Set modules = new HashSet(Arrays.asList(compileScope.getAffectedModules())); + final List allModules = Arrays.asList(ModuleManager.getInstance(project).getModules()); for (Artifact artifact : artifactManager.getArtifacts()) { if (artifact.isBuildOnMake()) { - if (modules.containsAll(Arrays.asList(ModuleManager.getInstance(project).getModules())) + if (modules.containsAll(allModules) || containsModuleOutput(artifact, modules, artifactManager)) { artifacts.add(artifact); } diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/IncrementalArtifactsCompiler.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/IncrementalArtifactsCompiler.java index 38d04612f2e8..770fc0989716 100644 --- a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/IncrementalArtifactsCompiler.java +++ b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/IncrementalArtifactsCompiler.java @@ -84,6 +84,9 @@ public class IncrementalArtifactsCompiler implements PackagingCompiler { final CompileContext context = builderContext.getCompileContext(); final Set artifactsToBuild = ArtifactCompileScope.getArtifactsToBuild(project, context.getCompileScope()); + if (LOG.isDebugEnabled()) { + LOG.debug("artifacts to build: " + artifactsToBuild); + } List additionalArtifacts = new ArrayList(); for (BuildParticipantProvider provider : BuildParticipantProvider.EXTENSION_POINT_NAME.getExtensions()) { for (Module module : ModuleManager.getInstance(project).getModules()) { @@ -93,6 +96,9 @@ public class IncrementalArtifactsCompiler implements PackagingCompiler { } } } + if (LOG.isDebugEnabled() && !additionalArtifacts.isEmpty()) { + LOG.debug("additional artifacts to build: " + additionalArtifacts); + } artifactsToBuild.addAll(additionalArtifacts); final List allArtifacts = new ArrayList(Arrays.asList(ArtifactManager.getInstance(project).getArtifacts())); @@ -120,7 +126,7 @@ public class IncrementalArtifactsCompiler implements PackagingCompiler { ArtifactPackagingProcessingItem[] allProcessingItems = collectItems(builderContext, context.getProject()); if (LOG.isDebugEnabled()) { - int num = Math.min(100, allProcessingItems.length); + int num = Math.min(5000, allProcessingItems.length); LOG.debug("All files (" + num + " of " + allProcessingItems.length + "):"); for (int i = 0; i < num; i++) { LOG.debug(allProcessingItems[i].getFile().getPath()); @@ -203,7 +209,7 @@ public class IncrementalArtifactsCompiler implements PackagingCompiler { final boolean testMode = ApplicationManager.getApplication().isUnitTestMode(); if (LOG.isDebugEnabled()) { - int num = Math.min(100, items.length); + int num = Math.min(200, items.length); LOG.debug("Files to process (" + num + " of " + items.length + "):"); for (int i = 0; i < num; i++) { LOG.debug(items[i].getFile().getPath()); diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/ModuleOutputPackagingElement.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/ModuleOutputPackagingElement.java index 3f2083f9e12d..29d5c9d709b9 100644 --- a/java/compiler/impl/src/com/intellij/packaging/impl/elements/ModuleOutputPackagingElement.java +++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/ModuleOutputPackagingElement.java @@ -80,9 +80,12 @@ public class ModuleOutputPackagingElement extends PackagingElement T getUserData(Key key) { + public T getUserData(@NotNull Key key) { return null; } - public void putUserData(Key key, T value) { + public void putUserData(@NotNull Key key, T value) { } public boolean isMake() { @@ -113,4 +114,4 @@ public class DummyCompileContext implements CompileContext { public boolean isRebuild() { return false; } -} \ No newline at end of file +} diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ValueDescriptorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ValueDescriptorImpl.java index 64f8259bb958..21b0febf54e9 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ValueDescriptorImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ValueDescriptorImpl.java @@ -110,8 +110,12 @@ public abstract class ValueDescriptorImpl extends NodeDescriptorImpl implements myStoredEvaluationContext.getDebugProcess().getManagerThread().invoke(new SuspendContextCommandImpl(myStoredEvaluationContext.getSuspendContext()) { public void contextAction() throws Exception { // re-setting the context will cause value recalculation - setContext(myStoredEvaluationContext); - semaphore.up(); + try { + setContext(myStoredEvaluationContext); + } + finally { + semaphore.up(); + } } }); semaphore.waitFor(); diff --git a/java/idea-ui/src/com/intellij/facet/impl/ui/FacetEditorContextBase.java b/java/idea-ui/src/com/intellij/facet/impl/ui/FacetEditorContextBase.java index c54f96d755c0..ac4e67d51576 100644 --- a/java/idea-ui/src/com/intellij/facet/impl/ui/FacetEditorContextBase.java +++ b/java/idea-ui/src/com/intellij/facet/impl/ui/FacetEditorContextBase.java @@ -98,7 +98,7 @@ public abstract class FacetEditorContextBase extends UserDataHolderBase implemen public abstract ArtifactsStructureConfigurableContext getArtifactsStructureContext(); @Nullable - public T getUserData(final Key key) { + public T getUserData(@NotNull final Key key) { T t = super.getUserData(key); if (t == null && myParentContext != null) { t = myParentContext.getUserData(key); diff --git a/java/idea-ui/src/com/intellij/facet/impl/ui/FacetErrorPanel.java b/java/idea-ui/src/com/intellij/facet/impl/ui/FacetErrorPanel.java index 55dc5bedd559..00b7f5e687a6 100644 --- a/java/idea-ui/src/com/intellij/facet/impl/ui/FacetErrorPanel.java +++ b/java/idea-ui/src/com/intellij/facet/impl/ui/FacetErrorPanel.java @@ -56,6 +56,7 @@ public class FacetErrorPanel { public void actionPerformed(final ActionEvent e) { if (myCurrentQuickFix != null) { myCurrentQuickFix.run(myQuickFixButton); + myValidatorsManager.validate(); } } }); diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectStructureConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectStructureConfigurable.java index d4d47895b2c7..1003a570f891 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectStructureConfigurable.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectStructureConfigurable.java @@ -383,7 +383,7 @@ public class ProjectStructureConfigurable extends BaseConfigurable implements Se public ActionCallback select(@NotNull LibraryOrderEntry libraryOrderEntry, final boolean requestFocus) { final Library lib = libraryOrderEntry.getLibrary(); - if (lib != null && lib.getTable() == null) { + if (lib == null || lib.getTable() == null) { Place place = new Place().putPath(CATEGORY, myModulesConfig); place.putPath(BaseStructureConfigurable.TREE_OBJECT, libraryOrderEntry.getOwnerModule()); return navigateTo(place, requestFocus); diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/BaseLibrariesConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/BaseLibrariesConfigurable.java index 7d4f51e5a047..2d3079a3e5c0 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/BaseLibrariesConfigurable.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/BaseLibrariesConfigurable.java @@ -274,21 +274,21 @@ public abstract class BaseLibrariesConfigurable extends BaseStructureConfigurabl for (VirtualFile file : files) { if (mySaveAsCb.isSelected() && myPathTf.getText().trim().length() > 0) { final File copy = new File(new File(myPathTf.getText()), file.getName()); - if (copy.mkdirs()) { - try { - final File fromFile = VfsUtil.virtualToIoFile(file); - if (fromFile.isFile()) { - FileUtil.copy(fromFile, copy); - } else { - FileUtil.copyDir(fromFile, copy); - } - model.addRoot(LocalFileSystem.getInstance().refreshAndFindFileByIoFile(copy), type); + if (!copy.getParentFile().exists() && !copy.getParentFile().mkdirs()) continue; + try { + final File fromFile = VfsUtil.virtualToIoFile(file); + if (fromFile.isFile()) { + FileUtil.copy(fromFile, copy); + } else { + FileUtil.copyDir(fromFile, copy); } - catch (IOException e1) { - //skip - } - continue; + model.addRoot(LocalFileSystem.getInstance().refreshAndFindFileByIoFile(copy), type); } + catch (IOException e1) { + LOG.error(e1); + } + + continue; } model.addRoot(file, type); diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java index c34393b8415a..7087c9eb2c17 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java @@ -32,11 +32,13 @@ import com.intellij.codeInsight.intention.QuickFixFactory; import com.intellij.openapi.project.IndexNotReadyException; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.infos.CandidateInfo; import com.intellij.psi.infos.MethodCandidateInfo; import com.intellij.psi.util.*; import com.intellij.xml.util.XmlStringUtil; +import org.intellij.lang.annotations.Language; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -558,7 +560,7 @@ public class HighlightMethodUtil { return createMismatchedArgumentsHtmlTooltip(list, parameters, methodName, substitutor, aClass); } - private static String createMismatchedArgumentsHtmlTooltip(PsiExpressionList list, + private static String createShortMismatchedArgumentsHtmlTooltip(PsiExpressionList list, PsiParameter[] parameters, String methodName, PsiSubstitutor substitutor, @@ -572,11 +574,86 @@ public class HighlightMethodUtil { Integer.valueOf(cols - parameters.length + 1), parensizedName, HighlightUtil.formatClass(aClass, false), createMismatchedArgsHtmlTooltipParamsRow(parameters, substitutor, expressions), - createMismatchedArgsHtmlTooltipExpressionsRow(expressions, parameters, substitutor, cols) + createMismatchedArgsHtmlTooltipArgumentsRow(expressions, parameters, substitutor, cols) ); } - private static String createMismatchedArgsHtmlTooltipExpressionsRow(final PsiExpression[] expressions, final PsiParameter[] parameters, + private static String esctrim(@NotNull String s) { + return StringUtil.first(XmlStringUtil.escapeString(s), 40, true); + } + + private static String createMismatchedArgumentsHtmlTooltip(PsiExpressionList list, + PsiParameter[] parameters, + String methodName, + PsiSubstitutor substitutor, + PsiClass aClass) { + if (Math.max(parameters.length, list.getExpressions().length) <= 2) { + return createShortMismatchedArgumentsHtmlTooltip(list, parameters, methodName, substitutor, aClass); + } + else { + return createLongMismatchedArgumentsHtmlTooltip(list, parameters, methodName, substitutor, aClass); + } + } + + private static String createLongMismatchedArgumentsHtmlTooltip(PsiExpressionList list, + PsiParameter[] parameters, + String methodName, + PsiSubstitutor substitutor, + PsiClass aClass) { + PsiExpression[] expressions = list.getExpressions(); + + @NonNls @Language("HTML") + String s = "" + + " "+ + " " + +" " + ; + + + for (int i = 0; i < Math.max(parameters.length,expressions.length); i++) { + PsiParameter parameter = i < parameters.length ? parameters[i] : null; + PsiExpression expression = i < expressions.length ? expressions[i] : null; + @NonNls String mismatchColor = showShortType(i, parameters, expressions, substitutor) ? null : "red"; + + s += ""; + s += ""; + + s += ""; + + s += ""; + + s += ""; + } + + s+= "
" + methodName + "() in " + HighlightUtil.formatClass(aClass, false) +" cannot be applied to:
Expected
Parameters:
Actual
Arguments:

"; + if (parameter != null) { + s += esctrim(parameter.getName()) +":"; + } + s += ""; + if (parameter != null) { + PsiType type = substitutor.substitute(parameter.getType()); + s += "" + + esctrim(showShortType(i, parameters, expressions, substitutor) + ? type.getPresentableText() + : HighlightUtil.formatType(type)) + + "" + ; + } + s += ""; + if (expression != null) { + PsiType type = expression.getType(); + s += "" + + esctrim(expression.getText()) + "  "+ + (mismatchColor == null || type == null || type == PsiType.NULL ? "" : "("+esctrim(HighlightUtil.formatType(type))+")") + + "" + ; + + } + s += "
"; + return s; + } + + private static String createMismatchedArgsHtmlTooltipArgumentsRow(final PsiExpression[] expressions, final PsiParameter[] parameters, final PsiSubstitutor substitutor, final int cols) { @NonNls String ms = ""; for (int i = 0; i < expressions.length; i++) { diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java index 1ab725bc535b..6a021df38048 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java @@ -69,6 +69,17 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh private final Map> mySingleImportedClasses = new THashMap>(); private final Map> mySingleImportedFields = new THashMap>(); private PsiFile myFile; + private final PsiElementVisitor REGISTER_REFERENCES_VISITOR = new PsiRecursiveElementWalkingVisitor() { + @Override public void visitElement(PsiElement element) { + super.visitElement(element); + for (PsiReference reference : element.getReferences()) { + PsiElement resolved = reference.resolve(); + if (resolved instanceof PsiNamedElement) { + myRefCountHolder.registerLocallyReferenced((PsiNamedElement)resolved); + } + } + } + }; @SuppressWarnings({"UnusedDeclaration"}) //in plugin.xml public HighlightVisitorImpl(Project project) { @@ -111,6 +122,14 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh } }, false); } + + private void registerReferencesFromInjectedFragments(final PsiElement element) { + InjectedLanguageUtil.enumerate(element, myFile, new PsiLanguageInjectionHost.InjectedPsiVisitor() { + public void visit(@NotNull final PsiFile injectedPsi, @NotNull final List places) { + injectedPsi.accept(REGISTER_REFERENCES_VISITOR); + } + }, false); + } public boolean analyze(final Runnable action, final boolean updateWholeFile, final PsiFile file) { myFile = file; @@ -269,6 +288,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh @Override public void visitComment(PsiComment comment) { super.visitComment(comment); if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkUnclosedComment(comment)); + if (!myHolder.hasErrorResults()) registerReferencesFromInjectedFragments(comment); } @Override public void visitContinueStatement(PsiContinueStatement statement) { @@ -499,6 +519,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh super.visitLiteralExpression(expression); if (myHolder.hasErrorResults()) return; myHolder.add(HighlightUtil.checkLiteralExpressionParsingError(expression)); + if (!myHolder.hasErrorResults()) registerReferencesFromInjectedFragments(expression); } @Override public void visitMethod(PsiMethod method) { diff --git a/java/java-impl/src/com/intellij/codeInspection/varScopeCanBeNarrowed/FieldCanBeLocalInspection.java b/java/java-impl/src/com/intellij/codeInspection/varScopeCanBeNarrowed/FieldCanBeLocalInspection.java index d6c709e8fa87..c78e0a180756 100644 --- a/java/java-impl/src/com/intellij/codeInspection/varScopeCanBeNarrowed/FieldCanBeLocalInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/varScopeCanBeNarrowed/FieldCanBeLocalInspection.java @@ -15,9 +15,9 @@ */ package com.intellij.codeInspection.varScopeCanBeNarrowed; +import com.intellij.codeInsight.CodeInsightUtil; import com.intellij.codeInsight.daemon.GroupNames; import com.intellij.codeInsight.daemon.ImplicitUsageProvider; -import com.intellij.codeInsight.CodeInsightUtil; import com.intellij.codeInspection.InspectionsBundle; import com.intellij.codeInspection.LocalQuickFix; import com.intellij.codeInspection.ProblemDescriptor; @@ -29,6 +29,7 @@ import com.intellij.openapi.editor.ScrollType; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Ref; import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.codeStyle.VariableKind; @@ -153,13 +154,13 @@ public class FieldCanBeLocalInspection extends BaseLocalInspectionTool { } } } - final Collection writtenVariables = ControlFlowUtil.getWrittenVariables(controlFlow, 0, controlFlow.getSize(), false); + final Ref> writtenVariables = new Ref>(); final List readBeforeWrites = ControlFlowUtil.getReadBeforeWrite(controlFlow); for (final PsiReferenceExpression readBeforeWrite : readBeforeWrites) { final PsiElement resolved = readBeforeWrite.resolve(); if (resolved instanceof PsiField) { final PsiField field = (PsiField)resolved; - if (writtenVariables.contains(field)){ + if (!(field.getType() instanceof PsiPrimitiveType) || getWrittenVariables(controlFlow, writtenVariables).contains(field)){ PsiElement parent = body.getParent(); if (!(parent instanceof PsiMethod) || !((PsiMethod)parent).isConstructor() || @@ -177,6 +178,13 @@ public class FieldCanBeLocalInspection extends BaseLocalInspectionTool { } } + private static Collection getWrittenVariables(ControlFlow controlFlow, Ref> writtenVariables) { + if (writtenVariables.get() == null) { + writtenVariables.set(ControlFlowUtil.getWrittenVariables(controlFlow, 0, controlFlow.getSize(), false)); + } + return writtenVariables.get(); + } + private static void removeFieldsReferencedFromInitializers(final PsiClass aClass, final Set candidates) { aClass.accept(new JavaRecursiveElementWalkingVisitor() { @Override public void visitMethod(PsiMethod method) { diff --git a/java/java-impl/src/com/intellij/ide/projectView/impl/nodes/PackageElement.java b/java/java-impl/src/com/intellij/ide/projectView/impl/nodes/PackageElement.java index a89076455542..870bbb702191 100644 --- a/java/java-impl/src/com/intellij/ide/projectView/impl/nodes/PackageElement.java +++ b/java/java-impl/src/com/intellij/ide/projectView/impl/nodes/PackageElement.java @@ -17,14 +17,17 @@ package com.intellij.ide.projectView.impl.nodes; import com.intellij.openapi.actionSystem.DataKey; import com.intellij.openapi.module.Module; +import com.intellij.openapi.ui.Queryable; import com.intellij.psi.PsiPackage; +import java.util.Map; + /** * @author Eugene Zhuravlev * Date: Sep 19, 2003 * Time: 3:51:02 PM */ -public final class PackageElement { +public final class PackageElement implements Queryable { public static final DataKey DATA_KEY = DataKey.create("package.element"); private final Module myModule; @@ -69,4 +72,11 @@ public final class PackageElement { public boolean isLibraryElement() { return myIsLibraryElement; } + + public void putInfo(Map info) { + PsiPackage pkg = getPackage(); + if (pkg instanceof Queryable) { + ((Queryable)pkg).putInfo(info); + } + } } diff --git a/java/java-impl/src/com/intellij/ide/todo/nodes/TodoPackageNode.java b/java/java-impl/src/com/intellij/ide/todo/nodes/TodoPackageNode.java index 0136a6d90ce3..7adfab1b46c3 100644 --- a/java/java-impl/src/com/intellij/ide/todo/nodes/TodoPackageNode.java +++ b/java/java-impl/src/com/intellij/ide/todo/nodes/TodoPackageNode.java @@ -46,10 +46,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.awt.*; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; +import java.util.*; public final class TodoPackageNode extends PackageElementNode implements HighlightedRegionProvider { private final ArrayList myHighlightedRegions; @@ -144,6 +141,12 @@ public final class TodoPackageNode extends PackageElementNode implements Highlig } } + @Override + public void apply(Map info) { + info.put("toDoFileCount", String.valueOf(getFileCount(getValue()))); + info.put("toDoItemCount", String.valueOf(getTodoItemCount(getValue()))); + } + private int getFileCount(final PackageElement packageElement) { int count = 0; if (getSettings().isFlattenPackages()) { diff --git a/java/java-impl/src/com/intellij/psi/impl/PsiClassImplUtil.java b/java/java-impl/src/com/intellij/psi/impl/PsiClassImplUtil.java index c9115d619abe..900283e9661f 100644 --- a/java/java-impl/src/com/intellij/psi/impl/PsiClassImplUtil.java +++ b/java/java-impl/src/com/intellij/psi/impl/PsiClassImplUtil.java @@ -21,6 +21,7 @@ import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.util.*; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; +import com.intellij.psi.augment.PsiAugmentProvider; import com.intellij.psi.filters.OrFilter; import com.intellij.psi.impl.compiled.ClsElementImpl; import com.intellij.psi.impl.source.PsiImmediateClassType; @@ -554,6 +555,10 @@ public class PsiClassImplUtil { if (!processor.execute(field, state)) return false; } } + + for (PsiField field : PsiAugmentProvider.collectAugments(aClass, PsiField.class)) { + if (!processor.execute(field, state)) return false; + } } if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclaractionKind.METHOD)) { @@ -566,6 +571,10 @@ public class PsiClassImplUtil { } if (!processor.execute(method, state)) return false; } + + for (PsiMethod method : PsiAugmentProvider.collectAugments(aClass, PsiMethod.class)) { + if (!processor.execute(method, state)) return false; + } } if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclaractionKind.CLASS)) { diff --git a/java/java-impl/src/com/intellij/psi/impl/compiled/ClsClassImpl.java b/java/java-impl/src/com/intellij/psi/impl/compiled/ClsClassImpl.java index 04e05f7b1cc8..22af7a58b33d 100644 --- a/java/java-impl/src/com/intellij/psi/impl/compiled/ClsClassImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/compiled/ClsClassImpl.java @@ -17,6 +17,7 @@ package com.intellij.psi.impl.compiled; import com.intellij.navigation.ItemPresentation; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.ui.Queryable; import com.intellij.openapi.util.Pair; import com.intellij.psi.*; import com.intellij.psi.impl.InheritanceImplUtil; @@ -27,6 +28,7 @@ import com.intellij.psi.impl.java.stubs.JavaStubElementTypes; import com.intellij.psi.impl.java.stubs.PsiClassStub; import com.intellij.psi.impl.source.ClassInnerStuffCache; import com.intellij.psi.impl.source.Constants; +import com.intellij.psi.impl.source.PsiClassImpl; import com.intellij.psi.impl.source.SourceTreeToPsiMap; import com.intellij.psi.impl.source.tree.TreeElement; import com.intellij.psi.javadoc.PsiDocComment; @@ -40,8 +42,9 @@ import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.util.Collection; import java.util.List; +import java.util.Map; -public class ClsClassImpl extends ClsRepositoryPsiElement> implements PsiClass, PsiQualifiedNamedElement { +public class ClsClassImpl extends ClsRepositoryPsiElement> implements PsiClass, PsiQualifiedNamedElement, Queryable { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.compiled.ClsClassImpl"); private final ClassInnerStuffCache innersCache = new ClassInnerStuffCache(this); @@ -518,4 +521,8 @@ public class ClsClassImpl extends ClsRepositoryPsiElement> imple return file == null ? null : (dir = file.getContainingDirectory()) == null ? null : JavaDirectoryService.getInstance().getPackage(dir); } + + public void putInfo(Map info) { + PsiClassImpl.putInfo(this, info); + } } diff --git a/java/java-impl/src/com/intellij/psi/impl/compiled/ClsFileImpl.java b/java/java-impl/src/com/intellij/psi/impl/compiled/ClsFileImpl.java index fd9ae54733c0..7e2bbef95a5d 100644 --- a/java/java-impl/src/com/intellij/psi/impl/compiled/ClsFileImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/compiled/ClsFileImpl.java @@ -28,6 +28,7 @@ import com.intellij.openapi.roots.OrderEntry; import com.intellij.openapi.roots.OrderRootType; import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.ui.Queryable; import com.intellij.openapi.util.Key; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.pom.java.LanguageLevel; @@ -37,6 +38,7 @@ import com.intellij.psi.impl.PsiManagerEx; import com.intellij.psi.impl.PsiManagerImpl; import com.intellij.psi.impl.java.stubs.PsiClassStub; import com.intellij.psi.impl.java.stubs.impl.PsiJavaFileStubImpl; +import com.intellij.psi.impl.source.PsiFileImpl; import com.intellij.psi.impl.source.PsiFileWithStubSupport; import com.intellij.psi.impl.source.SourceTreeToPsiMap; import com.intellij.psi.impl.source.resolve.FileContextUtil; @@ -52,8 +54,10 @@ import org.jetbrains.annotations.Nullable; import java.lang.ref.SoftReference; import java.util.List; +import java.util.Map; -public class ClsFileImpl extends ClsRepositoryPsiElement implements PsiJavaFile, PsiFileWithStubSupport, PsiFileEx { +public class ClsFileImpl extends ClsRepositoryPsiElement implements PsiJavaFile, PsiFileWithStubSupport, PsiFileEx, + Queryable { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.compiled.ClsFileImpl"); static final Object MIRROR_LOCK = new String("Mirror Lock"); @@ -440,4 +444,8 @@ public class ClsFileImpl extends ClsRepositoryPsiElement public PsiFile cacheCopy(final FileContent content) { return this; } + + public void putInfo(Map info) { + PsiFileImpl.putInfo(this, info); + } } diff --git a/java/java-impl/src/com/intellij/psi/impl/compiled/ClsJavaCodeReferenceElementImpl.java b/java/java-impl/src/com/intellij/psi/impl/compiled/ClsJavaCodeReferenceElementImpl.java index 5708a5780715..57adfbf23042 100644 --- a/java/java-impl/src/com/intellij/psi/impl/compiled/ClsJavaCodeReferenceElementImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/compiled/ClsJavaCodeReferenceElementImpl.java @@ -206,6 +206,7 @@ public class ClsJavaCodeReferenceElementImpl extends ClsElementImpl implements P || getManager().areElementsEquivalent(resolve(), element); } + @NotNull public Object[] getVariants() { throw new RuntimeException("Variants are not available for references to compiled code"); } diff --git a/java/java-impl/src/com/intellij/psi/impl/compiled/ClsReferenceExpressionImpl.java b/java/java-impl/src/com/intellij/psi/impl/compiled/ClsReferenceExpressionImpl.java index 61c128e1d647..2983154903b6 100644 --- a/java/java-impl/src/com/intellij/psi/impl/compiled/ClsReferenceExpressionImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/compiled/ClsReferenceExpressionImpl.java @@ -145,6 +145,7 @@ public class ClsReferenceExpressionImpl extends ClsElementImpl implements PsiRef return myPatternExpression.isReferenceTo(element); } + @NotNull public Object[] getVariants() { return myPatternExpression.getVariants(); } diff --git a/java/java-impl/src/com/intellij/psi/impl/compiled/ClsTypeParameterReferenceImpl.java b/java/java-impl/src/com/intellij/psi/impl/compiled/ClsTypeParameterReferenceImpl.java index e1c42bb22e73..2c9c154b800c 100644 --- a/java/java-impl/src/com/intellij/psi/impl/compiled/ClsTypeParameterReferenceImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/compiled/ClsTypeParameterReferenceImpl.java @@ -146,6 +146,7 @@ public class ClsTypeParameterReferenceImpl extends ClsElementImpl implements Psi throw new IncorrectOperationException(CAN_NOT_MODIFY_MESSAGE); } + @NotNull public Object[] getVariants() { throw new RuntimeException("Variants are not available for references to compiled code"); } diff --git a/java/java-impl/src/com/intellij/psi/impl/file/PsiPackageImpl.java b/java/java-impl/src/com/intellij/psi/impl/file/PsiPackageImpl.java index a6d10ea76f7c..b42290fa48d4 100644 --- a/java/java-impl/src/com/intellij/psi/impl/file/PsiPackageImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/file/PsiPackageImpl.java @@ -32,6 +32,7 @@ import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.roots.*; +import com.intellij.openapi.ui.Queryable; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VfsUtil; @@ -61,12 +62,9 @@ import org.jetbrains.annotations.NonNls; 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.*; -public class PsiPackageImpl extends PsiElementBase implements PsiPackage { +public class PsiPackageImpl extends PsiElementBase implements PsiPackage, Queryable { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.file.PsiPackageImpl"); private final PsiManagerEx myManager; @@ -643,4 +641,9 @@ public class PsiPackageImpl extends PsiElementBase implements PsiPackage { public PsiQualifiedNamedElement getContainer() { return getParentPackage(); } + + public void putInfo(Map info) { + info.put("packageName", getName()); + info.put("packageQualifiedName", getQualifiedName()); + } } diff --git a/java/java-impl/src/com/intellij/psi/impl/light/LightClassReference.java b/java/java-impl/src/com/intellij/psi/impl/light/LightClassReference.java index bbfba0ba65eb..ec5f6e4f8a13 100644 --- a/java/java-impl/src/com/intellij/psi/impl/light/LightClassReference.java +++ b/java/java-impl/src/com/intellij/psi/impl/light/LightClassReference.java @@ -228,6 +228,7 @@ public class LightClassReference extends LightElement implements PsiJavaCodeRefe return getManager().areElementsEquivalent(resolve(), element); } + @NotNull public Object[] getVariants() { throw new RuntimeException("Variants are not available for light references"); } diff --git a/java/java-impl/src/com/intellij/psi/impl/light/LightClassReferenceExpression.java b/java/java-impl/src/com/intellij/psi/impl/light/LightClassReferenceExpression.java index 9baa3097148a..add6df030299 100644 --- a/java/java-impl/src/com/intellij/psi/impl/light/LightClassReferenceExpression.java +++ b/java/java-impl/src/com/intellij/psi/impl/light/LightClassReferenceExpression.java @@ -48,6 +48,7 @@ public class LightClassReferenceExpression extends LightClassReference implement return getManager().areElementsEquivalent(resolve(), element); } + @NotNull public Object[] getVariants() { throw new RuntimeException("Variants are not available for light references"); } diff --git a/java/java-impl/src/com/intellij/psi/impl/light/LightMemberReference.java b/java/java-impl/src/com/intellij/psi/impl/light/LightMemberReference.java index 898718bffa88..b02a2c66fb82 100644 --- a/java/java-impl/src/com/intellij/psi/impl/light/LightMemberReference.java +++ b/java/java-impl/src/com/intellij/psi/impl/light/LightMemberReference.java @@ -144,6 +144,7 @@ public class LightMemberReference extends LightElement implements PsiJavaCodeRef return element instanceof PsiClass && element.getManager().areElementsEquivalent(resolve(), element); } + @NotNull public Object[] getVariants() { throw new RuntimeException("Variants are not available for light references"); } diff --git a/java/java-impl/src/com/intellij/psi/impl/light/LightPackageReference.java b/java/java-impl/src/com/intellij/psi/impl/light/LightPackageReference.java index 3c7d906efde6..c6b7e8c8a42d 100644 --- a/java/java-impl/src/com/intellij/psi/impl/light/LightPackageReference.java +++ b/java/java-impl/src/com/intellij/psi/impl/light/LightPackageReference.java @@ -114,6 +114,7 @@ public class LightPackageReference extends LightElement implements PsiJavaCodeRe return getManager().areElementsEquivalent(resolve(), element); } + @NotNull public Object[] getVariants() { throw new RuntimeException("Variants are not available for light references"); } diff --git a/java/java-impl/src/com/intellij/psi/impl/source/PsiClassImpl.java b/java/java-impl/src/com/intellij/psi/impl/source/PsiClassImpl.java index f71306d568fc..b3b167fd4a64 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/PsiClassImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/PsiClassImpl.java @@ -23,10 +23,10 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.roots.OrderEntry; import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.ui.Queryable; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; -import com.intellij.psi.augment.PsiAugmentProvider; import com.intellij.psi.impl.InheritanceImplUtil; import com.intellij.psi.impl.PsiClassImplUtil; import com.intellij.psi.impl.PsiImplUtil; @@ -51,7 +51,6 @@ import com.intellij.psi.stubs.IStubElementType; import com.intellij.psi.stubs.PsiFileStub; import com.intellij.psi.stubs.StubElement; import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -60,8 +59,9 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.util.Collection; import java.util.List; +import java.util.Map; -public class PsiClassImpl extends JavaStubPsiElement> implements PsiClass, PsiQualifiedNamedElement { +public class PsiClassImpl extends JavaStubPsiElement> implements PsiClass, PsiQualifiedNamedElement, Queryable { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.PsiClassImpl"); private final ClassInnerStuffCache innersCache = new ClassInnerStuffCache(this); @@ -302,16 +302,12 @@ public class PsiClassImpl extends JavaStubPsiElement> implements @NotNull public PsiField[] getFields() { - final PsiField[] owns = getStubOrPsiChildren(Constants.FIELD_BIT_SET, PsiField.ARRAY_FACTORY); - final List augments = PsiAugmentProvider.collectAugments(this, PsiField.class); - return ArrayUtil.mergeArrayAndCollection(owns, augments, PsiField.ARRAY_FACTORY); + return getStubOrPsiChildren(Constants.FIELD_BIT_SET, PsiField.ARRAY_FACTORY); } @NotNull public PsiMethod[] getMethods() { - final PsiMethod[] owns = getStubOrPsiChildren(Constants.METHOD_BIT_SET, PsiMethod.ARRAY_FACTORY); - final List augments = PsiAugmentProvider.collectAugments(this, PsiMethod.class); - return ArrayUtil.mergeArrayAndCollection(owns, augments, PsiMethod.ARRAY_FACTORY); + return getStubOrPsiChildren(Constants.METHOD_BIT_SET, PsiMethod.ARRAY_FACTORY); } @NotNull @@ -645,4 +641,17 @@ public class PsiClassImpl extends JavaStubPsiElement> implements return file == null ? null : (dir = file.getContainingDirectory()) == null ? null : JavaDirectoryService.getInstance().getPackage(dir); } + + public void putInfo(Map info) { + putInfo(this, info); + } + + public static void putInfo(PsiClass psiClass, Map info) { + info.put("className", psiClass.getName()); + info.put("qualifiedClassName", psiClass.getQualifiedName()); + PsiFile file = psiClass.getContainingFile(); + if (file instanceof Queryable) { + ((Queryable)file).putInfo(info); + } + } } diff --git a/java/java-impl/src/com/intellij/psi/impl/source/PsiEnumConstantImpl.java b/java/java-impl/src/com/intellij/psi/impl/source/PsiEnumConstantImpl.java index 958702a46928..75796c8dd24f 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/PsiEnumConstantImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/PsiEnumConstantImpl.java @@ -202,6 +202,7 @@ public class PsiEnumConstantImpl extends JavaStubPsiElement implem throw new IncorrectOperationException("Invalid operation"); } + @NotNull public Object[] getVariants() { return ArrayUtil.EMPTY_OBJECT_ARRAY; } diff --git a/java/java-impl/src/com/intellij/psi/impl/source/PsiFieldImpl.java b/java/java-impl/src/com/intellij/psi/impl/source/PsiFieldImpl.java index 4cec786ca1ac..186468ce2255 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/PsiFieldImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/PsiFieldImpl.java @@ -18,6 +18,7 @@ package com.intellij.psi.impl.source; import com.intellij.lang.ASTNode; import com.intellij.navigation.ItemPresentation; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.ui.Queryable; import com.intellij.psi.*; import com.intellij.psi.impl.*; import com.intellij.psi.impl.cache.InitializerTooLongException; @@ -44,9 +45,10 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; -public class PsiFieldImpl extends JavaStubPsiElement implements PsiField, PsiVariableEx { +public class PsiFieldImpl extends JavaStubPsiElement implements PsiField, PsiVariableEx, Queryable { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.PsiFieldImpl"); private volatile PatchedSoftReference myCachedType = null; @@ -432,4 +434,7 @@ public class PsiFieldImpl extends JavaStubPsiElement implements Ps return PsiImplUtil.getMemberUseScope(this); } + public void putInfo(Map info) { + info.put("fieldName", getName()); + } } diff --git a/java/java-impl/src/com/intellij/psi/impl/source/PsiImportStaticReferenceElementImpl.java b/java/java-impl/src/com/intellij/psi/impl/source/PsiImportStaticReferenceElementImpl.java index a61ed6a614ba..1528fd1b25dc 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/PsiImportStaticReferenceElementImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/PsiImportStaticReferenceElementImpl.java @@ -383,6 +383,7 @@ public class PsiImportStaticReferenceElementImpl extends CompositePsiElement imp PsiScopesUtil.resolveAndWalk(proc, this, null, true); } + @NotNull public Object[] getVariants() { // IMPLEMENT[dsl] return ArrayUtil.EMPTY_OBJECT_ARRAY; diff --git a/java/java-impl/src/com/intellij/psi/impl/source/PsiJavaCodeReferenceElementImpl.java b/java/java-impl/src/com/intellij/psi/impl/source/PsiJavaCodeReferenceElementImpl.java index 2ce9002a3470..1d6aff6e3c01 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/PsiJavaCodeReferenceElementImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/PsiJavaCodeReferenceElementImpl.java @@ -694,6 +694,7 @@ public class PsiJavaCodeReferenceElementImpl extends CompositePsiElement impleme myCachedTextSkipWhiteSpaceAndComments = null; } + @NotNull public Object[] getVariants() { final ElementFilter filter; switch (getKind()) { diff --git a/java/java-impl/src/com/intellij/psi/impl/source/PsiLabelReference.java b/java/java-impl/src/com/intellij/psi/impl/source/PsiLabelReference.java index c18ec0be8e82..fe1dac35655c 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/PsiLabelReference.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/PsiLabelReference.java @@ -86,6 +86,7 @@ public class PsiLabelReference implements PsiReference{ return resolve() == element; } + @NotNull public Object[] getVariants(){ final List result = new ArrayList(); PsiElement context = myStatement; diff --git a/java/java-impl/src/com/intellij/psi/impl/source/PsiMethodImpl.java b/java/java-impl/src/com/intellij/psi/impl/source/PsiMethodImpl.java index d9d2c04dc697..b67f023fc4fc 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/PsiMethodImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/PsiMethodImpl.java @@ -18,6 +18,7 @@ package com.intellij.psi.impl.source; import com.intellij.lang.ASTNode; import com.intellij.navigation.ItemPresentation; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.ui.Queryable; import com.intellij.psi.*; import com.intellij.psi.impl.ElementPresentationUtil; import com.intellij.psi.impl.PsiClassImplUtil; @@ -46,8 +47,9 @@ import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.util.List; +import java.util.Map; -public class PsiMethodImpl extends JavaStubPsiElement implements PsiMethod { +public class PsiMethodImpl extends JavaStubPsiElement implements PsiMethod, Queryable { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.PsiMethodImpl"); private PatchedSoftReference myCachedType = null; @@ -330,4 +332,8 @@ public class PsiMethodImpl extends JavaStubPsiElement implements public SearchScope getUseScope() { return PsiImplUtil.getMemberUseScope(this); } + + public void putInfo(Map info) { + info.put("methodName", getName()); + } } diff --git a/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/ImportHelper.java b/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/ImportHelper.java index 7168c8a5213b..aeca7ad64065 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/ImportHelper.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/ImportHelper.java @@ -671,7 +671,10 @@ public class ImportHelper{ PsiElement currentFileResolveScope = resolveResult.getCurrentFileResolveScope(); if (!(currentFileResolveScope instanceof PsiImportStatementBase)) continue; - if (context != null && currentFileResolveScope instanceof JspxImportStatement && context != ((JspxImportStatement)currentFileResolveScope).getDeclarationFile()) { + if (context != null && + (!currentFileResolveScope.isValid() || + currentFileResolveScope instanceof JspxImportStatement && + context != ((JspxImportStatement)currentFileResolveScope).getDeclarationFile())) { continue; } diff --git a/java/java-impl/src/com/intellij/psi/impl/source/javadoc/PsiDocMethodOrFieldRef.java b/java/java-impl/src/com/intellij/psi/impl/source/javadoc/PsiDocMethodOrFieldRef.java index 0798658ad3c3..350ee4ee1644 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/javadoc/PsiDocMethodOrFieldRef.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/javadoc/PsiDocMethodOrFieldRef.java @@ -116,6 +116,7 @@ public class PsiDocMethodOrFieldRef extends CompositePsiElement implements PsiDo } return new MyReference(method) { + @NotNull public PsiElement[] getVariants() { final List lst = new ArrayList(); for (PsiMethod method : methods) { @@ -240,6 +241,7 @@ public class PsiDocMethodOrFieldRef extends CompositePsiElement implements PsiDo return myReferencee == null ? JavaResolveResult.EMPTY_ARRAY : new JavaResolveResult[]{new CandidateInfo(myReferencee, PsiSubstitutor.EMPTY)}; } + @NotNull public PsiElement[] getVariants(){ final List vars = new ArrayList(); final PsiElement scope = getScope(); diff --git a/java/java-impl/src/com/intellij/psi/impl/source/javadoc/PsiDocParamRef.java b/java/java-impl/src/com/intellij/psi/impl/source/javadoc/PsiDocParamRef.java index 1c785c586311..992e92302a3a 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/javadoc/PsiDocParamRef.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/javadoc/PsiDocParamRef.java @@ -100,6 +100,7 @@ public class PsiDocParamRef extends CompositePsiElement implements PsiDocTagValu return getManager().areElementsEquivalent(resolve(), element); } + @NotNull public Object[] getVariants() { final PsiElement firstChild = getFirstChild(); if (firstChild instanceof PsiDocToken && ((PsiDocToken)firstChild).getTokenType().equals(JavaDocTokenType.DOC_TAG_VALUE_LT)) { diff --git a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/MethodPropertyReference.java b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/MethodPropertyReference.java index c0d884843bf2..a582a4d69936 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/MethodPropertyReference.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/MethodPropertyReference.java @@ -23,6 +23,7 @@ import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlAttributeValue; import com.intellij.psi.xml.XmlTag; import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** @@ -93,6 +94,7 @@ public class MethodPropertyReference extends BasicAttributeValueReference { return super.handleElementRename(newElementName); } + @NotNull public Object[] getVariants() { return JspSpiUtil.getMethodPropertyReferenceVariants(this, resolveClass(), myReadable); } diff --git a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/PsiPackageReference.java b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/PsiPackageReference.java index 57e715307b89..3dc30b5f557e 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/PsiPackageReference.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/PsiPackageReference.java @@ -46,6 +46,7 @@ public class PsiPackageReference extends PsiPolyVariantReferenceBase (PsiPackage)myReferenceSet.getReference(myIndex - 1).resolve(); } + @NotNull public Object[] getVariants() { final PsiPackage psiPackage = getContext(); if (psiPackage == null) return ArrayUtil.EMPTY_OBJECT_ARRAY; @@ -88,4 +89,4 @@ public class PsiPackageReference extends PsiPolyVariantReferenceBase public PackageReferenceSet getReferenceSet() { return myReferenceSet; } -} \ No newline at end of file +} diff --git a/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiNameValuePairImpl.java b/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiNameValuePairImpl.java index a24afb5db17b..9b0cf8fd6df9 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiNameValuePairImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiNameValuePairImpl.java @@ -185,6 +185,7 @@ public class PsiNameValuePairImpl extends CompositePsiElement implements PsiName return element instanceof PsiMethod && element.equals(resolve()); } + @NotNull public Object[] getVariants() { return ArrayUtil.EMPTY_OBJECT_ARRAY; } diff --git a/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiNewExpressionImpl.java b/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiNewExpressionImpl.java index 379787a85ad1..08b7ec622f15 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiNewExpressionImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiNewExpressionImpl.java @@ -160,6 +160,7 @@ public class PsiNewExpressionImpl extends ExpressionPsiElement implements PsiNew return null; } + @NotNull public Object[] getVariants() { return ArrayUtil.EMPTY_OBJECT_ARRAY; } diff --git a/java/java-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java b/java/java-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java index 1ca3b4b5de97..24d624ff2920 100644 --- a/java/java-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java +++ b/java/java-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java @@ -56,20 +56,23 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{ public CandidateInfo resolveConflict(List conflicts){ if (conflicts.isEmpty()) return null; if (conflicts.size() == 1) return conflicts.get(0); + + checkParametersNumber(conflicts, myActualParameterTypes.length, true); + if (conflicts.size() == 1) return conflicts.get(0); + checkSameSignatures(conflicts); - if (conflicts.size() == 1) return conflicts.get(0); + checkAccessLevels(conflicts); - if (conflicts.size() == 1) return conflicts.get(0); - checkParametersNumber(conflicts, myActualParameterTypes.length); + checkParametersNumber(conflicts, myActualParameterTypes.length, false); if (conflicts.size() == 1) return conflicts.get(0); final int applicabilityLevel = checkApplicability(conflicts); if (conflicts.size() == 1) return conflicts.get(0); - checkSpecifics(conflicts, applicabilityLevel); + checkSpecifics(conflicts, applicabilityLevel); if (conflicts.size() == 1) return conflicts.get(0); THashSet uniques = new THashSet(conflicts); @@ -196,9 +199,12 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{ return ((MethodCandidateInfo)info).isApplicable(); } - private static void checkParametersNumber(final List conflicts, final int argumentsCount) { + private static void checkParametersNumber(final List conflicts, + final int argumentsCount, + boolean checkForStaticAccessProblem) { boolean parametersNumberMatch = false; for (CandidateInfo info : conflicts) { + if (checkForStaticAccessProblem && !info.isStaticsScopeCorrect()) return; if (info instanceof MethodCandidateInfo) { final PsiMethod method = ((MethodCandidateInfo)info).getElement(); if (method.isVarArgs()) return; diff --git a/java/java-tests/testData/inspection/fieldCanBeLocal/localStateVar2InnerClass/expected.xml b/java/java-tests/testData/inspection/fieldCanBeLocal/localStateVar2InnerClass/expected.xml new file mode 100644 index 000000000000..4704d91e891d --- /dev/null +++ b/java/java-tests/testData/inspection/fieldCanBeLocal/localStateVar2InnerClass/expected.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/java/java-tests/testData/inspection/fieldCanBeLocal/localStateVar2InnerClass/src/Test.java b/java/java-tests/testData/inspection/fieldCanBeLocal/localStateVar2InnerClass/src/Test.java new file mode 100644 index 000000000000..155a4d1dfd6f --- /dev/null +++ b/java/java-tests/testData/inspection/fieldCanBeLocal/localStateVar2InnerClass/src/Test.java @@ -0,0 +1,11 @@ +import java.util.*; +public class Outer { + private Map value = new HashMap(); + + public class Inner { + + public Inner(String s) { + if (!value.containsKey(s)) value.put(s, ""); + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/fieldCanBeLocal/stateField/expected.xml b/java/java-tests/testData/inspection/fieldCanBeLocal/stateField/expected.xml new file mode 100644 index 000000000000..580a889dbfd6 --- /dev/null +++ b/java/java-tests/testData/inspection/fieldCanBeLocal/stateField/expected.xml @@ -0,0 +1,3 @@ + + + diff --git a/java/java-tests/testData/inspection/fieldCanBeLocal/stateField/src/Test.java b/java/java-tests/testData/inspection/fieldCanBeLocal/stateField/src/Test.java new file mode 100644 index 000000000000..79774c0c5ee9 --- /dev/null +++ b/java/java-tests/testData/inspection/fieldCanBeLocal/stateField/src/Test.java @@ -0,0 +1,8 @@ +import java.util.*; +public class Test { + private final Map myModuleToOutput = new HashMap(); + + void foo() { + if (myModuleToOutput.containsKey(null)) myModuleToOutput.put("", ""); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/WordCompletionTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/completion/WordCompletionTest.java index 772d769a79ab..a91ff5ed2da1 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/WordCompletionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/WordCompletionTest.java @@ -48,6 +48,7 @@ public class WordCompletionTest extends CompletionTestCase { return true; } + @NotNull public Object[] getVariants() { return new Object[]{"MySoftVariant"}; } @@ -66,6 +67,7 @@ public class WordCompletionTest extends CompletionTestCase { return false; } + @NotNull public Object[] getVariants() { return new Object[]{"MyHardVariant"}; } diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/FieldCanBeLocalTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/FieldCanBeLocalTest.java index 10630e3c49c4..24a8d482ba1b 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/FieldCanBeLocalTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/FieldCanBeLocalTest.java @@ -26,4 +26,6 @@ public class FieldCanBeLocalTest extends InspectionTestCase { public void testStaticAccess() throws Exception { doTest(); } public void testInnerClassConstructor() throws Exception { doTest(); } public void testLocalVar2InnerClass() throws Exception { doTest(); } + public void testStateField() throws Exception { doTest(); } + public void testLocalStateVar2InnerClass() throws Exception { doTest(); } } diff --git a/java/java-tests/testSrc/com/intellij/projectView/ClassNameConvertor.java b/java/java-tests/testSrc/com/intellij/projectView/ClassNameConvertor.java index 647a9e0b5320..8aea26ae98c6 100644 --- a/java/java-tests/testSrc/com/intellij/projectView/ClassNameConvertor.java +++ b/java/java-tests/testSrc/com/intellij/projectView/ClassNameConvertor.java @@ -6,8 +6,8 @@ import com.intellij.ide.projectView.TreeStructureProvider; import com.intellij.ide.projectView.ViewSettings; import com.intellij.ide.util.treeView.AbstractTreeNode; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Queryable; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import org.jetbrains.annotations.NotNull; @@ -37,6 +37,11 @@ class ClassNameConvertor implements TreeStructureProvider { return Collections.emptyList(); } + @Override + public String toTestString(Queryable.PrintInfo printInfo) { + return super.toTestString(printInfo) + " converted"; + } + public String getTestPresentation() { return name + " converted"; } diff --git a/java/java-tests/testSrc/com/intellij/projectView/JavaTreeStructureTest.java b/java/java-tests/testSrc/com/intellij/projectView/JavaTreeStructureTest.java index bc8cf98d8a7a..9397acc1e384 100644 --- a/java/java-tests/testSrc/com/intellij/projectView/JavaTreeStructureTest.java +++ b/java/java-tests/testSrc/com/intellij/projectView/JavaTreeStructureTest.java @@ -141,7 +141,7 @@ public class JavaTreeStructureTest extends TestSourceBasedTestCase { }); } - public void _testVisibilitySorter() throws Exception { + public void testVisibilitySorter() throws Exception { dotest(new CheckAction() { public void testClassStructure(StructureViewComponent structureViewComponent) { structureViewComponent.setActionActive(InheritedMembersFilter.ID, true); diff --git a/java/java-tests/testSrc/com/intellij/projectView/ProjectTreeStructureTest.java b/java/java-tests/testSrc/com/intellij/projectView/ProjectTreeStructureTest.java index 8245af6c29bd..8e98c92febdf 100644 --- a/java/java-tests/testSrc/com/intellij/projectView/ProjectTreeStructureTest.java +++ b/java/java-tests/testSrc/com/intellij/projectView/ProjectTreeStructureTest.java @@ -1,119 +1,129 @@ package com.intellij.projectView; +import com.intellij.openapi.ui.Queryable; + public class ProjectTreeStructureTest extends BaseProjectViewTestCase { + @Override + protected void setUp() throws Exception { + super.setUp(); + myPrintInfo = new Queryable.PrintInfo(new String[] {"className", "fileName", "fieldName", "methodName", "packageName"}); + } + public void test1() { getProjectTreeStructure().setProviders(new SameNamesJoiner(), new ClassNameConvertor(myProject)); - assertStructureEqual(getPackageDirectory(), "PsiDirectory: package1\n" + + assertStructureEqual(getPackageDirectory(), + "package1\n" + " Class2.java converted\n" + " Form1 joined\n" + " Form1.java converted\n" + - " PsiFile(plain text):Form1.form\n" + - " PsiFile(plain text):Form2.form\n"); + " Form1.form\n" + + " Form2.form\n"); } - public void _testStandardProviders() { + public void testStandardProviders() { useStandardProviders(); - assertStructureEqual(getPackageDirectory(), "PsiDirectory: package1\n" + - " PsiClass:Class1\n" + - " PsiClass:Form1\n" + - " PsiFile(plain text):Form1.form\n" + - " PsiFile(plain text):Form2.form\n" + - " PsiJavaFile:Class2.java\n" + - " PsiClass:Class2\n" + - " PsiClass:Class3\n" + - " PsiJavaFile:Class4.java\n"); + assertStructureEqual(getPackageDirectory(), "package1\n" + + " Class1\n" + + " Form1\n" + + " Form1.form\n" + + " Form2.form\n" + + " Class2.java\n" + + " Class2\n" + + " Class3\n" + + " Class4.java\n"); assertStructureEqual("Project\n" + " External Libraries\n" + " Library: < java 1.4 >\n" + - " PsiDirectory: jsp-api.jar\n" + - " PsiDirectory: META-INF\n" + - " PsiFile(plain text):MANIFEST.MF\n" + - " PsiDirectory: javax\n" + - " PsiDirectory: servlet\n" + - " PsiDirectory: jsp\n" + - " PsiClass:ErrorData\n" + - " PsiClass:HttpJspPage\n" + - " PsiClass:JspContext\n" + - " PsiClass:JspEngineInfo\n" + - " PsiClass:JspException\n" + - " PsiClass:JspFactory\n" + - " PsiClass:JspPage\n" + - " PsiClass:JspTagException\n" + " jsp-api.jar\n" + + " META-INF\n" + + " MANIFEST.MF\n" + + " javax\n" + + " servlet\n" + + " jsp\n" + + " ErrorData\n" + + " HttpJspPage\n" + + " JspContext\n" + + " JspEngineInfo\n" + + " JspException\n" + + " JspFactory\n" + + " JspPage\n" + + " JspTagException\n" ); getProjectTreeStructure().setProviders(); - assertStructureEqual(getPackageDirectory(), "PsiDirectory: package1\n" + - " PsiFile(plain text):Form1.form\n" + - " PsiFile(plain text):Form2.form\n" + - " PsiJavaFile:Class1.java\n" + - " PsiJavaFile:Class2.java\n" + - " PsiJavaFile:Class4.java\n" + - " PsiJavaFile:Form1.java\n"); + assertStructureEqual(getPackageDirectory(), + "package1\n" + + " Form1.form\n" + + " Form2.form\n" + + " Class1.java\n" + + " Class2.java\n" + + " Class4.java\n" + + " Form1.java\n"); assertStructureEqual("Project\n" + " External Libraries\n" + " Library: < java 1.4 >\n" + - " PsiDirectory: jsp-api.jar\n" + - " PsiDirectory: META-INF\n" + - " PsiFile(plain text):MANIFEST.MF\n" + - " PsiDirectory: javax\n" + - " PsiDirectory: servlet\n" + - " PsiDirectory: jsp\n" + - " PsiDirectory: el\n" + - " PsiFile:ELException.class\n" + - " PsiFile:ELParseException.class\n" + - " PsiFile:Expression.class\n" + - " PsiFile:ExpressionEvaluator.class\n" + - " PsiFile:FunctionMapper.class\n" + - " PsiFile:VariableResolver.class\n" + - " PsiDirectory: resources\n"); + " jsp-api.jar\n" + + " META-INF\n" + + " MANIFEST.MF\n" + + " javax\n" + + " servlet\n" + + " jsp\n" + + " el\n" + + " ELException.class\n" + + " ELParseException.class\n" + + " Expression.class\n" + + " ExpressionEvaluator.class\n" + + " FunctionMapper.class\n" + + " VariableResolver.class\n" + + " resources\n"); } public void testShowClassMembers() { useStandardProviders(); myShowMembers = false; - assertStructureEqual(getPackageDirectory(), "PsiDirectory: package1\n" + - " PsiClass:Class1\n" + - " PsiClass:Class2\n"); + assertStructureEqual(getPackageDirectory(), "package1\n" + + " Class1\n" + + " Class2\n"); myShowMembers = true; - assertStructureEqual(getPackageDirectory(), "PsiDirectory: package1\n" + - " PsiClass:Class1\n" + - " PsiClass:InnerClass\n" + - " PsiField:myInnerClassField\n" + - " PsiField:myField1\n" + - " PsiField:myField2\n" + - " PsiMethod:getValue\n" + - " PsiClass:Class2\n" + - " PsiClass:InnerClass1\n" + - " PsiClass:InnerClass12\n" + - " PsiClass:InnerClass13\n" + - " PsiClass:InnerClass14\n" + - " PsiClass:InnerClass15\n" + - " PsiField:myInnerClassField\n" + - " PsiField:myInnerClassField\n" + - " PsiField:myInnerClassField\n" + - " PsiField:myInnerClassField\n" + - " PsiField:myInnerClassField\n" + - " PsiClass:InnerClass2\n" + - " PsiClass:InnerClass22\n" + - " PsiClass:InnerClass23\n" + - " PsiClass:InnerClass24\n" + - " PsiClass:InnerClass25\n" + - " PsiField:myInnerClassField\n" + - " PsiField:myFieldToSelect\n" + - " PsiField:myInnerClassField\n" + - " PsiField:myInnerClassField\n" + - " PsiField:myInnerClassField\n" + - " PsiField:myField1\n" + - " PsiField:myField2\n" + - " PsiField:myField3\n" + - " PsiField:myField4\n" + - " PsiMethod:getValue\n", 100); + assertStructureEqual(getPackageDirectory(), "package1\n" + + " Class1\n" + + " InnerClass\n" + + " myInnerClassField\n" + + " myField1\n" + + " myField2\n" + + " getValue\n" + + " Class2\n" + + " InnerClass1\n" + + " InnerClass12\n" + + " InnerClass13\n" + + " InnerClass14\n" + + " InnerClass15\n" + + " myInnerClassField\n" + + " myInnerClassField\n" + + " myInnerClassField\n" + + " myInnerClassField\n" + + " myInnerClassField\n" + + " InnerClass2\n" + + " InnerClass22\n" + + " InnerClass23\n" + + " InnerClass24\n" + + " InnerClass25\n" + + " myInnerClassField\n" + + " myFieldToSelect\n" + + " myInnerClassField\n" + + " myInnerClassField\n" + + " myInnerClassField\n" + + " myField1\n" + + " myField2\n" + + " myField3\n" + + " myField4\n" + + " getValue\n", 100); } @@ -122,20 +132,20 @@ public class ProjectTreeStructureTest extends BaseProjectViewTestCase { public void testGetParentObject(){ useStandardProviders(); myShowMembers = true; - assertStructureEqual(getContentDirectory(), "PsiDirectory: getParentObject\n" + - " PsiDirectory: src\n" + - " PsiDirectory: com\n" + - " PsiDirectory: package1\n" + - " PsiClass:Class1\n" + - " PsiField:myField\n" + - " PsiMethod:method\n" + - " PsiClass:Form1\n" + - " PsiFile(plain text):Form1.form\n" + - " PsiFile(plain text):Form2.form\n" + - " PsiJavaFile:Class2.java\n" + - " PsiClass:Class2\n" + - " PsiClass:Class3\n" + - " PsiJavaFile:Class4.java\n"); + assertStructureEqual(getContentDirectory(), "getParentObject\n" + + " src\n" + + " com\n" + + " package1\n" + + " Class1\n" + + " myField\n" + + " method\n" + + " Form1\n" + + " Form1.form\n" + + " Form2.form\n" + + " Class2.java\n" + + " Class2\n" + + " Class3\n" + + " Class4.java\n"); checkContainsMethod(myStructure.getRootElement(), myStructure); diff --git a/java/java-tests/testSrc/com/intellij/projectView/ToDoTreeStructureTest.java b/java/java-tests/testSrc/com/intellij/projectView/ToDoTreeStructureTest.java index 2b3544f3bd73..f89eb8602dd4 100644 --- a/java/java-tests/testSrc/com/intellij/projectView/ToDoTreeStructureTest.java +++ b/java/java-tests/testSrc/com/intellij/projectView/ToDoTreeStructureTest.java @@ -4,6 +4,7 @@ import com.intellij.ide.todo.AllTodosTreeBuilder; import com.intellij.ide.todo.CurrentFileTodosTreeBuilder; import com.intellij.ide.todo.TodoTreeStructure; import com.intellij.ide.todo.nodes.TodoItemNode; +import com.intellij.openapi.ui.Queryable; import com.intellij.testFramework.IdeaTestUtil; import com.intellij.openapi.util.Disposer; import com.intellij.util.ui.tree.TreeUtil; @@ -14,6 +15,12 @@ import javax.swing.tree.DefaultTreeModel; public class ToDoTreeStructureTest extends BaseProjectViewTestCase { + @Override + protected void setUp() throws Exception { + super.setUp(); + myPrintInfo = new Queryable.PrintInfo(new String[] {"className", "fileName", "fieldName", "methodName", "packageName"}, new String[] {"toDoFileCount", "toDoItemCount"}); + } + public void testToDo1() throws Exception { AllTodosTreeBuilder all = new AllTodosTreeBuilder(new JTree(), new DefaultTreeModel(new DefaultMutableTreeNode()), myProject); all.init(); @@ -22,8 +29,8 @@ public class ToDoTreeStructureTest extends BaseProjectViewTestCase { ((TodoTreeStructure)myStructure).setFlattenPackages(true); assertStructureEqual("Root\n" + " Summary\n" + - " PsiPackage: package1.package2 (2 items in 1 file)\n" + - " PsiJavaFile:JavaClass.java\n" + + " package2 toDoFileCount=1,toDoItemCount=2\n" + + " JavaClass.java\n" + " Item: (62,78)\n" + " Item: (145,162)\n", null); @@ -32,26 +39,26 @@ public class ToDoTreeStructureTest extends BaseProjectViewTestCase { } //todo kirillk - public void _testToDo() throws Exception { + public void testToDo() throws Exception { AllTodosTreeBuilder all = new AllTodosTreeBuilder(new JTree(), new DefaultTreeModel(new DefaultMutableTreeNode()), myProject); all.init(); myStructure = all.getTreeStructure(); assertStructureEqual("Root\n" + " Summary\n" + - " PsiDirectory: toDo\n" + - " XmlFile:xmlFile.xml\n"+ + " toDo\n" + + " xmlFile.xml\n"+ " Item: (12,16)\n" + - " PsiPackage: package1 (4 items in 2 files)\n" + - " PsiPackage: package2 (2 items in 1 file)\n" + - " PsiJavaFile:JavaClass.java\n" + + " package1 toDoFileCount=2,toDoItemCount=4\n" + + " package2 toDoFileCount=1,toDoItemCount=2\n" + + " JavaClass.java\n" + " Item: (62,78)\n" + " Item: (145,162)\n" + - " PsiJavaFile:JavaClass.java\n" + + " JavaClass.java\n" + " Item: (52,68)\n" + " Item: (134,151)\n" + - " PsiPackage: package3 (2 items in 1 file)\n" + - " PsiJavaFile:JavaClass.java\n" + + " package3 toDoFileCount=1,toDoItemCount=2\n" + + " JavaClass.java\n" + " Item: (53,69)\n" + " Item: (136,153)\n", null); @@ -69,28 +76,12 @@ public class ToDoTreeStructureTest extends BaseProjectViewTestCase { builder.setFile(getSrcDirectory().findSubdirectory("package1").findFile("JavaClass.java")); builder.updateFromRoot(); myStructure = builder.getTreeStructure(); - assertStructureEqual("PsiJavaFile:JavaClass.java\n" + - " PsiJavaFile:JavaClass.java\n" + + assertStructureEqual("JavaClass.java\n" + + " JavaClass.java\n" + " Item: (52,68)\n" + " Item: (134,151)\n", null); - TreeUtil.expandAll(currentFileTree); - currentFileTree.getSelectionModel().setSelectionPath(currentFileTree.getPathForRow(4)); - - IdeaTestUtil.assertTreeEqual(currentFileTree, "-Root\n" + - " -Summary\n" + - " -JavaClass.java\n" + - " Item: (52,68)\n" + - " [Item: (134,151)]\n", true); - - IdeaTestUtil.waitForAlarm(600); - - IdeaTestUtil.assertTreeEqual(currentFileTree, "-Root\n" + - " -Summary\n" + - " -JavaClass.java\n" + - " Item: (52,68)\n" + - " [Item: (134,151)]\n", true); Disposer.dispose(builder); Disposer.dispose(all); } diff --git a/java/testFramework/src/com/intellij/projectView/BaseProjectViewTestCase.java b/java/testFramework/src/com/intellij/projectView/BaseProjectViewTestCase.java index 5e804539cbfd..df81c6468b2c 100644 --- a/java/testFramework/src/com/intellij/projectView/BaseProjectViewTestCase.java +++ b/java/testFramework/src/com/intellij/projectView/BaseProjectViewTestCase.java @@ -23,6 +23,7 @@ import com.intellij.ide.projectView.impl.*; import com.intellij.ide.projectView.impl.nodes.PackageElementNode; import com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode; import com.intellij.ide.util.treeView.*; +import com.intellij.openapi.ui.Queryable; import com.intellij.testFramework.IdeaTestUtil; import com.intellij.openapi.project.DumbAwareRunnable; import com.intellij.openapi.startup.StartupManager; @@ -50,6 +51,8 @@ public abstract class BaseProjectViewTestCase extends TestSourceBasedTestCase { protected boolean myShowMembers = false; private List myPanes = new ArrayList(); + protected Queryable.PrintInfo myPrintInfo; + protected void setUp() throws Exception { super.setUp(); @@ -113,7 +116,7 @@ public abstract class BaseProjectViewTestCase extends TestSourceBasedTestCase { private void assertStructureEqual(Object rootNode, String expected, int maxRowCount, Comparator comparator) { checkGetParentConsistency(rootNode); - StringBuffer actual = IdeaTestUtil.print(myStructure, rootNode, 0, comparator, maxRowCount, ' '); + StringBuffer actual = IdeaTestUtil.print(myStructure, rootNode, 0, comparator, maxRowCount, ' ', myPrintInfo); assertEquals(expected, actual.toString()); } diff --git a/platform/lang-api/src/com/intellij/openapi/roots/ProjectRootsTraversing.java b/platform/lang-api/src/com/intellij/openapi/roots/ProjectRootsTraversing.java index d6391c012be2..8649f0ac31dc 100644 --- a/platform/lang-api/src/com/intellij/openapi/roots/ProjectRootsTraversing.java +++ b/platform/lang-api/src/com/intellij/openapi/roots/ProjectRootsTraversing.java @@ -24,6 +24,7 @@ import com.intellij.openapi.util.UserDataHolderBase; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.PathUtil; import com.intellij.util.PathsList; +import org.jetbrains.annotations.NotNull; import java.util.HashSet; import java.util.List; @@ -125,11 +126,11 @@ public class ProjectRootsTraversing { myCurrentModuleManager = restored; } - public T getUserData(Key key) { + public T getUserData(@NotNull Key key) { return myUserData.getUserData(key); } - public void putUserData(Key key, T value) { + public void putUserData(@NotNull Key key, T value) { myUserData.putUserData(key, value); } diff --git a/platform/lang-api/src/com/intellij/psi/PsiReferenceBase.java b/platform/lang-api/src/com/intellij/psi/PsiReferenceBase.java index 3e150e937ab2..99aeb7473dd5 100644 --- a/platform/lang-api/src/com/intellij/psi/PsiReferenceBase.java +++ b/platform/lang-api/src/com/intellij/psi/PsiReferenceBase.java @@ -118,6 +118,7 @@ public abstract class PsiReferenceBase implements PsiRefer return resolveTo; } + @NotNull public Object[] getVariants() { return EMPTY_ARRAY; } diff --git a/platform/lang-api/src/com/intellij/psi/TokenType.java b/platform/lang-api/src/com/intellij/psi/TokenType.java index 795d8cd74301..86f287796823 100644 --- a/platform/lang-api/src/com/intellij/psi/TokenType.java +++ b/platform/lang-api/src/com/intellij/psi/TokenType.java @@ -16,7 +16,6 @@ package com.intellij.psi; import com.intellij.lang.Language; -import com.intellij.openapi.fileTypes.FileTypes; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.IFileElementType; diff --git a/platform/lang-api/src/com/intellij/psi/util/PsiUtilBase.java b/platform/lang-api/src/com/intellij/psi/util/PsiUtilBase.java index b4026f9fc6b5..34509ab6cb43 100644 --- a/platform/lang-api/src/com/intellij/psi/util/PsiUtilBase.java +++ b/platform/lang-api/src/com/intellij/psi/util/PsiUtilBase.java @@ -254,11 +254,11 @@ public class PsiUtilBase { return null; } - public T getUserData(Key key) { + public T getUserData(@NotNull Key key) { return null; } - public void putUserData(Key key, T value) { + public void putUserData(@NotNull Key key, T value) { } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java index 83942beb9bbc..549744170b64 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java @@ -47,6 +47,7 @@ import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.IndexNotReadyException; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Ref; @@ -556,7 +557,30 @@ public class CodeCompletionHandlerBase implements CodeInsightActionHandler { catch (Throwable e) { document.setText(""); if (((ApplicationEx)ApplicationManager.getApplication()).isInternal()) { - LOG.error("old text: " + oldCopyText + "; new text: " + newText + "; old document text: " + oldDocumentText, e); + final StringBuilder sb = new StringBuilder(); + boolean oldsAreSame = Comparing.equal(oldCopyText, oldDocumentText); + if (oldsAreSame) { + sb.append("oldCopyText == oldDocumentText"); + } + else { + sb.append("oldCopyText != oldDocumentText"); + sb.append("\n--- oldCopyText ------------------------------------------------\n").append(oldCopyText); + sb.append("\n--- oldDocumentText ------------------------------------------------\n").append(oldDocumentText); + } + if (Comparing.equal(oldCopyText, newText)) { + sb.insert(0, "newText == oldCopyText; "); + } + else if (!oldsAreSame && Comparing.equal(oldDocumentText, newText)) { + sb.insert(0, "newText == oldDocumentText; "); + } + else { + sb.insert(0, "newText != oldCopyText, oldDocumentText; "); + if (oldsAreSame) { + sb.append("\n--- oldCopyText ------------------------------------------------\n").append(oldCopyText); + } + sb.append("\n--- newText ------------------------------------------------\n").append(newText); + } + LOG.error(sb.toString(), e); } } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/IndentsPass.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/IndentsPass.java index 221f2dd4e26b..3c0c914d5e12 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/IndentsPass.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/IndentsPass.java @@ -21,10 +21,12 @@ package com.intellij.codeInsight.daemon.impl; import com.intellij.codeHighlighting.TextEditorHighlightingPass; import com.intellij.codeInsight.highlighting.BraceMatchingUtil; +import com.intellij.lang.LanguageParserDefinitions; import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.colors.EditorColors; import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.ex.EditorEx; +import com.intellij.openapi.editor.highlighter.EditorHighlighter; import com.intellij.openapi.editor.highlighter.HighlighterIterator; import com.intellij.openapi.editor.markup.CustomHighlighterRenderer; import com.intellij.openapi.editor.markup.HighlighterTargetArea; @@ -36,6 +38,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiFile; +import com.intellij.psi.tree.TokenSet; import com.intellij.util.containers.IntStack; import com.intellij.util.text.CharArrayUtil; import org.jetbrains.annotations.NotNull; @@ -82,9 +85,16 @@ public class IndentsPass extends TextEditorHighlightingPass implements DumbAware final VisualPosition startPosition = editor.offsetToVisualPosition(off); if (startPosition.column <= 0) return; - if (editor.getFoldingModel().isOffsetCollapsed(off)) return; + final FoldingModel foldingModel = editor.getFoldingModel(); + if (foldingModel.isOffsetCollapsed(off)) return; final int endOffset = highlighter.getEndOffset(); + + final FoldRegion headerRegion = foldingModel.getCollapsedRegionAtOffset(doc.getLineEndOffset(doc.getLineNumber(off))); + final FoldRegion tailRegion = foldingModel.getCollapsedRegionAtOffset(doc.getLineStartOffset(doc.getLineNumber(endOffset))); + + if (tailRegion != null && tailRegion == headerRegion) return; + final boolean selected; final IndentGuideDescriptor guide = editor.getIndentsModel().getCaretIndentGuide(); if (guide != null) { @@ -182,7 +192,7 @@ public class IndentsPass extends TextEditorHighlightingPass implements DumbAware private List buildDescriptors() { if (!myEditor.getSettings().isIndentGuidesShown()) return Collections.emptyList(); - int[] lineIndents = calcIndents(myDocument); + int[] lineIndents = calcIndents(); List descriptors = new ArrayList(); @@ -191,13 +201,14 @@ public class IndentsPass extends TextEditorHighlightingPass implements DumbAware lines.push(0); indents.push(0); + final CharSequence chars = myDocument.getCharsSequence(); for (int line = 1; line < lineIndents.length; line++) { int curIndent = lineIndents[line]; while (!indents.empty() && curIndent <= indents.peek()) { final int level = indents.pop(); int startLine = lines.pop(); - descriptors.add(new IndentGuideDescriptor(level, startLine, line)); + descriptors.add(createDescriptor(level, startLine, line, chars)); } int prevLine = line - 1; @@ -213,23 +224,47 @@ public class IndentsPass extends TextEditorHighlightingPass implements DumbAware final int level = indents.pop(); if (level > 0) { int startLine = lines.pop(); - descriptors.add(new IndentGuideDescriptor(level, startLine, myDocument.getLineCount())); + descriptors.add(createDescriptor(level, startLine, myDocument.getLineCount(), chars)); } } return descriptors; } - private int[] calcIndents(Document doc) { + private IndentGuideDescriptor createDescriptor(int level, int startLine, int endLine, CharSequence chars) { + while (startLine > 0 && isBlankLine(startLine, chars)) startLine--; + return new IndentGuideDescriptor(level, startLine, endLine); + } + + private boolean isBlankLine(int line, CharSequence chars) { + int startOffset = myDocument.getLineStartOffset(line); + return CharArrayUtil.shiftForward(chars, startOffset, " \t") >= myDocument.getLineEndOffset(line); + } + + private int[] calcIndents() { + final Document doc = myDocument; CharSequence chars = doc.getCharsSequence(); int[] lineIndents = new int[doc.getLineCount()]; + TokenSet comments = LanguageParserDefinitions.INSTANCE.forLanguage(myFile.getLanguage()).getCommentTokens(); + int prevColumn = -1; + final EditorHighlighter highlighter = myEditor.getHighlighter(); for (int line = 0; line < lineIndents.length; line++) { - int lineStart = myDocument.getLineStartOffset(line); - int lineEnd = myDocument.getLineEndOffset(line); + int lineStart = doc.getLineStartOffset(line); + int lineEnd = doc.getLineEndOffset(line); int nonWhitespaceOffset = CharArrayUtil.shiftForward(chars, lineStart, " \t"); if (nonWhitespaceOffset < lineEnd) { - lineIndents[line] = myEditor.calcColumnNumber(nonWhitespaceOffset, line); + final int column = myEditor.calcColumnNumber(nonWhitespaceOffset, line); + if (column < prevColumn) { + final HighlighterIterator it = highlighter.createIterator(nonWhitespaceOffset); + if (comments.contains(it.getTokenType())) { + lineIndents[line] = -1; + continue; + } + } + + lineIndents[line] = column; + prevColumn = column; } else { lineIndents[line] = -1; @@ -243,13 +278,17 @@ public class IndentsPass extends TextEditorHighlightingPass implements DumbAware } else { int startLine = line; - for (; line < lineIndents.length && lineIndents[line] == -1; line++); + while (line < lineIndents.length && lineIndents[line] == -1) { + //noinspection AssignmentToForLoopParameter + line++; + } + int bottomIndent = line < lineIndents.length ? lineIndents[line] : topIndent; int indent = Math.min(topIndent, bottomIndent); if (bottomIndent < topIndent) { - int nonWhitespaceOffset = CharArrayUtil.shiftForward(chars, myDocument.getLineStartOffset(line), " \t"); - HighlighterIterator iterator = myEditor.getHighlighter().createIterator(nonWhitespaceOffset); + int nonWhitespaceOffset = CharArrayUtil.shiftForward(chars, doc.getLineStartOffset(line), " \t"); + HighlighterIterator iterator = highlighter.createIterator(nonWhitespaceOffset); if (BraceMatchingUtil.isRBraceToken(iterator, chars, myFile.getFileType())) { indent = topIndent; } @@ -260,6 +299,7 @@ public class IndentsPass extends TextEditorHighlightingPass implements DumbAware lineIndents[blankLine] = Math.min(topIndent, indent); } + //noinspection AssignmentToForLoopParameter line--; // will be incremented back at the end of the loop; } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/navigation/actions/GotoDeclarationAction.java b/platform/lang-impl/src/com/intellij/codeInsight/navigation/actions/GotoDeclarationAction.java index 4978f3180d57..9d62e35ad33b 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/navigation/actions/GotoDeclarationAction.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/navigation/actions/GotoDeclarationAction.java @@ -20,6 +20,7 @@ import com.intellij.codeInsight.CodeInsightActionHandler; import com.intellij.codeInsight.CodeInsightBundle; import com.intellij.codeInsight.TargetElementUtilBase; import com.intellij.codeInsight.actions.BaseCodeInsightAction; +import com.intellij.codeInsight.hint.HintManager; import com.intellij.codeInsight.navigation.NavigationUtil; import com.intellij.featureStatistics.FeatureUsageTracker; import com.intellij.ide.util.DefaultPsiElementCellRenderer; @@ -65,10 +66,9 @@ public class GotoDeclarationAction extends BaseCodeInsightAction implements Code public void invoke(@NotNull final Project project, @NotNull Editor editor, @NotNull PsiFile file) { PsiDocumentManager.getInstance(project).commitAllDocuments(); - PsiElement element; try { int offset = editor.getCaretModel().getOffset(); - element = findTargetElement(project, editor, offset); + PsiElement element = findTargetElement(project, editor, offset); if (element == null) { FeatureUsageTracker.getInstance().triggerFeatureUsed("navigation.goto.declaration"); chooseAmbiguousTarget(editor, offset); @@ -109,7 +109,10 @@ public class GotoDeclarationAction extends BaseCodeInsightAction implements Code return true; } }; - chooseAmbiguousTarget(editor, offset,navigateProcessor, CodeInsightBundle.message("declaration.navigation.title")); + boolean found = chooseAmbiguousTarget(editor, offset, navigateProcessor, CodeInsightBundle.message("declaration.navigation.title")); + if (!found) { + HintManager.getInstance().showErrorHint(editor, "Cannot find declaration to go to"); + } } // returns true if processor is run or is going to be run after showing popup @@ -127,7 +130,7 @@ public class GotoDeclarationAction extends BaseCodeInsightAction implements Code processor.execute(element); return true; } - else if (candidates.size() > 1) { + if (candidates.size() > 1) { PsiElement[] elements = candidates.toArray(new PsiElement[candidates.size()]); final TextRange range = reference.getRangeInElement(); final String refText = range.substring(reference.getElement().getText()); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/CustomTemplateCallback.java b/platform/lang-impl/src/com/intellij/codeInsight/template/CustomTemplateCallback.java index 854c6d05290d..a62ab1038153 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/CustomTemplateCallback.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/CustomTemplateCallback.java @@ -43,23 +43,13 @@ public class CustomTemplateCallback { private Project myProject; private RangeMarker myGlobalMarker; private RangeMarker myEndOffsetMarker; - - //private final Map myCheckpoints = new HashMap(); private final Map myCheckpoints = new HashMap(); - /*private static class MyCheckpoint { - int myFixedLength = null; - RangeMarker myFixedOffset; - }*/ - public CustomTemplateCallback(Editor editor, PsiFile file) { myEditor = editor; myFile = file; myProject = file.getProject(); myTemplateManager = TemplateManagerImpl.getInstance(myProject); - } - - public void fixInitialEditorState() { myStartOffset = myEditor.getCaretModel().getOffset(); myGlobalMarker = myEditor.getDocument().createRangeMarker(myStartOffset, myStartOffset); myGlobalMarker.setGreedyToLeft(true); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateManagerImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateManagerImpl.java index d8baf07f3855..0a331403e0df 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateManagerImpl.java @@ -246,7 +246,6 @@ public class TemplateManagerImpl extends TemplateManager implements ProjectCompo int caretOffset = editor.getCaretModel().getOffset(); if (customLiveTemplate.isApplicable(file, caretOffset, false)) { final CustomTemplateCallback callback = new CustomTemplateCallback(editor, file); - callback.fixInitialEditorState(); String key = customLiveTemplate.computeTemplateKey(callback); if (key != null) { int offsetBeforeKey = caretOffset - key.length(); diff --git a/platform/lang-impl/src/com/intellij/codeInspection/reference/RefEntityImpl.java b/platform/lang-impl/src/com/intellij/codeInspection/reference/RefEntityImpl.java index 1f5492eb9c23..33e2bf36b7ea 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/reference/RefEntityImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/reference/RefEntityImpl.java @@ -28,6 +28,7 @@ import com.intellij.codeInspection.InspectionsBundle; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.util.Key; import gnu.trove.THashMap; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; @@ -90,7 +91,7 @@ public abstract class RefEntityImpl implements RefEntity { } @Nullable - public T getUserData(Key key){ + public T getUserData(@NotNull Key key){ synchronized(this){ if (myUserMap == null) return null; //noinspection unchecked @@ -106,7 +107,7 @@ public abstract class RefEntityImpl implements RefEntity { }); } - public void putUserData(Key key, T value){ + public void putUserData(@NotNull Key key, T value){ synchronized(this){ if (myUserMap == null){ if (value == null) return; diff --git a/platform/lang-impl/src/com/intellij/execution/ui/RunContentManagerImpl.java b/platform/lang-impl/src/com/intellij/execution/ui/RunContentManagerImpl.java index 232c64880345..90b65bfd1e26 100644 --- a/platform/lang-impl/src/com/intellij/execution/ui/RunContentManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/ui/RunContentManagerImpl.java @@ -578,50 +578,51 @@ public class RunContentManagerImpl implements RunContentManager, Disposable { } private void waitForProcess(final RunContentDescriptor descriptor) { - String progressTitle = ExecutionBundle.message("terminating.process.progress.title", descriptor.getDisplayName()); - ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() { - private ProgressIndicator myProgressIndicator; - private final Semaphore mySemaphore = new Semaphore(); + public void run() { + final Semaphore semaphore = new Semaphore(); + semaphore.down(); - private final Runnable myWaitThread = new Runnable() { - public void run() { - descriptor.getProcessHandler().waitFor(); - mySemaphore.up(); - } - }; - - private final Runnable myCancelListener = new Runnable() { - public void run() { - while(true) { - if(myProgressIndicator != null && (myProgressIndicator.isCanceled() || !myProgressIndicator.isRunning())) { - mySemaphore.up(); - break; - } + ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { + public void run() { + final ProcessHandler processHandler = descriptor.getProcessHandler(); try { - synchronized (this) { - wait(2000); + if (processHandler != null) { + processHandler.waitFor(); } } - catch (InterruptedException ignore) { + finally { + semaphore.up(); } } - } - }; + }); + + final ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator(); - public void run() { - myProgressIndicator = ProgressManager.getInstance().getProgressIndicator(); - if (myProgressIndicator != null) { - myProgressIndicator.setText(ExecutionBundle.message("waiting.for.vm.detach.progress.text")); + if (progressIndicator != null) { + progressIndicator.setText(ExecutionBundle.message("waiting.for.vm.detach.progress.text")); + ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { + public void run() { + while(true) { + if(progressIndicator.isCanceled() || !progressIndicator.isRunning()) { + semaphore.up(); + break; + } + try { + synchronized (this) { + wait(2000L); + } + } + catch (InterruptedException ignore) { + } + } + } + }); } - ApplicationManager.getApplication().executeOnPooledThread(myWaitThread); - ApplicationManager.getApplication().executeOnPooledThread(myCancelListener); - - mySemaphore.down(); - mySemaphore.waitFor(); + semaphore.waitFor(); } - }, progressTitle, true, myProject); + }, ExecutionBundle.message("terminating.process.progress.title", descriptor.getDisplayName()), true, myProject); } public interface MyRunContentListener extends EventListener { diff --git a/platform/lang-impl/src/com/intellij/find/actions/ShowUsagesAction.java b/platform/lang-impl/src/com/intellij/find/actions/ShowUsagesAction.java index 14be608e3edb..64110e91224d 100644 --- a/platform/lang-impl/src/com/intellij/find/actions/ShowUsagesAction.java +++ b/platform/lang-impl/src/com/intellij/find/actions/ShowUsagesAction.java @@ -184,6 +184,7 @@ public class ShowUsagesAction extends AnAction { usageViewSettings.GROUP_BY_MODULE = false; usageViewSettings.GROUP_BY_PACKAGE = false; usageViewSettings.GROUP_BY_USAGE_TYPE = false; + usageViewSettings.GROUP_BY_SCOPE = false; UsageViewManager manager = UsageViewManager.getInstance(handler.getProject()); final UsageViewImpl usageView = (UsageViewImpl)manager.createUsageView(UsageTarget.EMPTY_ARRAY, Usage.EMPTY_ARRAY, presentation, null); @@ -194,6 +195,7 @@ public class ShowUsagesAction extends AnAction { usageViewSettings.GROUP_BY_MODULE = save.GROUP_BY_MODULE; usageViewSettings.GROUP_BY_PACKAGE = save.GROUP_BY_PACKAGE; usageViewSettings.GROUP_BY_USAGE_TYPE = save.GROUP_BY_USAGE_TYPE; + usageViewSettings.GROUP_BY_SCOPE = save.GROUP_BY_SCOPE; } }); @@ -694,4 +696,4 @@ public class ShowUsagesAction extends AnAction { return null; } } -} \ No newline at end of file +} diff --git a/platform/lang-impl/src/com/intellij/ide/actions/GotoActionBase.java b/platform/lang-impl/src/com/intellij/ide/actions/GotoActionBase.java index 646cd246e267..0fda5957f06e 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/GotoActionBase.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/GotoActionBase.java @@ -48,7 +48,7 @@ public abstract class GotoActionBase extends AnAction { protected abstract void gotoActionPerformed(AnActionEvent e); - public final void update(final AnActionEvent event) { + public void update(final AnActionEvent event) { final Presentation presentation = event.getPresentation(); final DataContext dataContext = event.getDataContext(); final Project project = PlatformDataKeys.PROJECT.getData(dataContext); diff --git a/platform/lang-impl/src/com/intellij/ide/favoritesTreeView/FavoritesTreeViewPanel.java b/platform/lang-impl/src/com/intellij/ide/favoritesTreeView/FavoritesTreeViewPanel.java index a0575affbbff..526f7921f678 100644 --- a/platform/lang-impl/src/com/intellij/ide/favoritesTreeView/FavoritesTreeViewPanel.java +++ b/platform/lang-impl/src/com/intellij/ide/favoritesTreeView/FavoritesTreeViewPanel.java @@ -59,6 +59,7 @@ import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.tree.TreeUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; @@ -457,14 +458,15 @@ public class FavoritesTreeViewPanel extends JPanel implements DataProvider { } } - private PsiDirectory getDirectory() { + @Nullable + private PsiDirectory[] getSelectedDirectories() { if (myBuilder == null) return null; final Object[] selectedNodeElements = getSelectedNodeElements(); if (selectedNodeElements.length != 1) return null; for (FavoriteNodeProvider nodeProvider : Extensions.getExtensions(FavoriteNodeProvider.EP_NAME, myProject)) { final PsiElement psiElement = nodeProvider.getPsiElement(selectedNodeElements[0]); if (psiElement instanceof PsiDirectory) { - return (PsiDirectory)psiElement; + return new PsiDirectory[]{(PsiDirectory)psiElement}; } else if (psiElement instanceof PsiDirectoryContainer) { final String moduleName = nodeProvider.getElementModuleName(selectedNodeElements[0]); GlobalSearchScope searchScope = GlobalSearchScope.projectScope(myProject); @@ -474,16 +476,15 @@ public class FavoritesTreeViewPanel extends JPanel implements DataProvider { searchScope = GlobalSearchScope.moduleScope(module); } } - final PsiDirectory[] directories = ((PsiDirectoryContainer)psiElement).getDirectories(searchScope); - if (directories.length == 1) return directories[0]; + return ((PsiDirectoryContainer)psiElement).getDirectories(searchScope); } } return null; } public PsiDirectory[] getDirectories() { - PsiDirectory directory = getDirectory(); - return directory == null ? PsiDirectory.EMPTY_ARRAY : new PsiDirectory[]{directory}; + final PsiDirectory[] directories = getSelectedDirectories(); + return directories == null ? PsiDirectory.EMPTY_ARRAY : directories; } public PsiDirectory getOrChooseDirectory() { diff --git a/platform/lang-impl/src/com/intellij/ide/util/DefaultPsiElementCellRenderer.java b/platform/lang-impl/src/com/intellij/ide/util/DefaultPsiElementCellRenderer.java index 4581b24eb125..ea90cb413355 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/DefaultPsiElementCellRenderer.java +++ b/platform/lang-impl/src/com/intellij/ide/util/DefaultPsiElementCellRenderer.java @@ -20,7 +20,7 @@ import com.intellij.openapi.util.Iconable; import com.intellij.psi.PsiElement; import com.intellij.psi.presentation.java.SymbolPresentationUtil; -public class DefaultPsiElementCellRenderer extends PsiElementListCellRenderer { +public class DefaultPsiElementCellRenderer extends PsiElementListCellRenderer { protected int getIconFlags() { return Iconable.ICON_FLAG_VISIBILITY; } @@ -33,4 +33,4 @@ public class DefaultPsiElementCellRenderer extends PsiElementListCellRenderer { return SymbolPresentationUtil.getSymbolContainerText(element); } -} \ No newline at end of file +} diff --git a/platform/lang-impl/src/com/intellij/ide/util/PsiElementListCellRenderer.java b/platform/lang-impl/src/com/intellij/ide/util/PsiElementListCellRenderer.java index 278cd1f1e4e2..893b28f93dac 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/PsiElementListCellRenderer.java +++ b/platform/lang-impl/src/com/intellij/ide/util/PsiElementListCellRenderer.java @@ -99,12 +99,11 @@ public abstract class PsiElementListCellRenderer extends J } } - SimpleTextAttributes nameAttributes; if (isProblemFile) { attributes = TextAttributes.merge(new TextAttributes(color, null, Color.red, EffectType.WAVE_UNDERSCORE, Font.PLAIN),attributes); } - nameAttributes = attributes != null ? SimpleTextAttributes.fromTextAttributes(attributes):null; + SimpleTextAttributes nameAttributes = attributes != null ? SimpleTextAttributes.fromTextAttributes(attributes) : null; if (nameAttributes == null) nameAttributes = new SimpleTextAttributes(Font.PLAIN, color); diff --git a/platform/lang-impl/src/com/intellij/injected/editor/RangeMarkerWindow.java b/platform/lang-impl/src/com/intellij/injected/editor/RangeMarkerWindow.java index a7cecb3acf8b..134eb1c6b5b1 100644 --- a/platform/lang-impl/src/com/intellij/injected/editor/RangeMarkerWindow.java +++ b/platform/lang-impl/src/com/intellij/injected/editor/RangeMarkerWindow.java @@ -65,11 +65,11 @@ public class RangeMarkerWindow implements RangeMarkerEx { myHostMarker.setGreedyToRight(greedy); } - public T getUserData(final Key key) { + public T getUserData(@NotNull final Key key) { return myHostMarker.getUserData(key); } - public void putUserData(final Key key, final T value) { + public void putUserData(@NotNull final Key key, final T value) { myHostMarker.putUserData(key, value); } @@ -91,4 +91,4 @@ public class RangeMarkerWindow implements RangeMarkerEx { public boolean isGreedyToLeft() { return myHostMarker.isGreedyToLeft(); } -} \ No newline at end of file +} diff --git a/platform/lang-impl/src/com/intellij/mock/MockPsiManager.java b/platform/lang-impl/src/com/intellij/mock/MockPsiManager.java index 65ea995863cb..5458638bce6b 100644 --- a/platform/lang-impl/src/com/intellij/mock/MockPsiManager.java +++ b/platform/lang-impl/src/com/intellij/mock/MockPsiManager.java @@ -145,11 +145,11 @@ public class MockPsiManager extends PsiManagerEx { public void finishBatchFilesProcessingMode() { } - public T getUserData(Key key) { + public T getUserData(@NotNull Key key) { return null; } - public void putUserData(Key key, T value) { + public void putUserData(@NotNull Key key, T value) { } public boolean isDisposed() { @@ -250,4 +250,4 @@ public class MockPsiManager extends PsiManagerEx { public CacheManager getCacheManager() { return myCompositeCacheManager; } -} \ No newline at end of file +} diff --git a/platform/lang-impl/src/com/intellij/openapi/editor/actions/SelectWordAtCaretAction.java b/platform/lang-impl/src/com/intellij/openapi/editor/actions/SelectWordAtCaretAction.java index c7117d616122..1b1cf675cf1d 100644 --- a/platform/lang-impl/src/com/intellij/openapi/editor/actions/SelectWordAtCaretAction.java +++ b/platform/lang-impl/src/com/intellij/openapi/editor/actions/SelectWordAtCaretAction.java @@ -25,11 +25,16 @@ package com.intellij.openapi.editor.actions; import com.intellij.codeInsight.editorActions.SelectWordUtil; +import com.intellij.codeInsight.highlighting.BraceMatchingUtil; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.actionSystem.EditorActionHandler; -import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.editor.ex.EditorEx; +import com.intellij.openapi.editor.highlighter.HighlighterIterator; import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.text.CharArrayUtil; import java.util.ArrayList; import java.util.List; @@ -111,7 +116,23 @@ public class SelectWordAtCaretAction extends TextComponentEditorAction implement private static void selectWithGuide(Editor editor, IndentGuideDescriptor guide) { final Document doc = editor.getDocument(); int startOffset = editor.logicalPositionToOffset(new LogicalPosition(guide.startLine, 0)); - int endOffset = Math.min(editor.logicalPositionToOffset(new LogicalPosition(guide.endLine + 1, 0)), doc.getTextLength()); + int endOffset = Math.min(doc.getLineStartOffset(guide.endLine), doc.getTextLength()); + + final VirtualFile file = ((EditorEx)editor).getVirtualFile(); + if (file != null) { + // Make sure selection contains closing matching brace. + + final CharSequence chars = doc.getCharsSequence(); + int nonWhitespaceOffset = CharArrayUtil.shiftForward(chars, endOffset, " \t\n"); + HighlighterIterator iterator = ((EditorEx)editor).getHighlighter().createIterator(nonWhitespaceOffset); + if (BraceMatchingUtil.isRBraceToken(iterator, chars, file.getFileType())) { + if (((EditorEx)editor).calcColumnNumber(iterator.getStart(), doc.getLineNumber(iterator.getStart())) == guide.indentLevel) { + endOffset = iterator.getEnd(); + endOffset = CharArrayUtil.shiftForward(chars, endOffset, " \t"); + if (endOffset < chars.length() && chars.charAt(endOffset) == '\n') endOffset++; + } + } + } editor.getSelectionModel().setSelection(startOffset, endOffset); } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/file/PsiDirectoryImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/file/PsiDirectoryImpl.java index 92719e340b4a..38ac34eddc50 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/file/PsiDirectoryImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/file/PsiDirectoryImpl.java @@ -24,6 +24,7 @@ import com.intellij.openapi.editor.Document; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileEditor.impl.LoadTextUtil; import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.ui.Queryable; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.TextRange; @@ -54,8 +55,9 @@ import javax.swing.*; import java.io.IOException; import java.io.Writer; import java.util.ArrayList; +import java.util.Map; -public class PsiDirectoryImpl extends PsiElementBase implements PsiDirectory { +public class PsiDirectoryImpl extends PsiElementBase implements PsiDirectory, Queryable { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.file.PsiDirectoryImpl"); private final PsiManagerImpl myManager; @@ -563,5 +565,9 @@ public class PsiDirectoryImpl extends PsiElementBase implements PsiDirectory { protected Icon getElementIcon(final int flags) { return Icons.DIRECTORY_CLOSED_ICON; } + + public void putInfo(Map info) { + info.put("fileName", getName()); + } } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/PsiFileImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/source/PsiFileImpl.java index 1b536cff5fb6..ca5790dbb3e1 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/PsiFileImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/PsiFileImpl.java @@ -31,6 +31,7 @@ import com.intellij.openapi.editor.colors.TextAttributesKey; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Queryable; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vcs.FileStatus; @@ -64,12 +65,9 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.lang.ref.Reference; import java.lang.reflect.Array; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Set; +import java.util.*; -public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiFileWithStubSupport { +public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiFileWithStubSupport, Queryable { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.PsiFileImpl"); private IElementType myElementType; @@ -936,4 +934,13 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF FileBasedIndex.getInstance().requestReindex(vFile); } } + + public void putInfo(Map info) { + putInfo(this, info); + } + + public static void putInfo(PsiFile psiFile, Map info) { + info.put("fileName", psiFile.getName()); + info.put("fileType", psiFile.getFileType().toString()); + } } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/PsiMultiReference.java b/platform/lang-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/PsiMultiReference.java index 31b845def30a..c1670b02592f 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/PsiMultiReference.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/PsiMultiReference.java @@ -127,6 +127,7 @@ public class PsiMultiReference implements PsiPolyVariantReference { return false; } + @NotNull public Object[] getVariants() { Set variants = new HashSet(); for(PsiReference ref: myReferences) { diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/FileReferenceCharFilter.java b/platform/lang-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/FileReferenceCharFilter.java index 4d63cd561278..b4de601a0a3f 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/FileReferenceCharFilter.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/FileReferenceCharFilter.java @@ -35,6 +35,8 @@ public class FileReferenceCharFilter extends CharFilter{ final LookupElement item = lookup.getCurrentItem(); if (item != null && item.getObject() instanceof PsiFileSystemItem) { + if ('.' == c) return Result.ADD_TO_PREFIX; + final PsiReference reference = file.findReferenceAt(lookup.getEditor().getCaretModel().getOffset()); if (reference instanceof FileReference) return Result.HIDE_LOOKUP; diff --git a/platform/lvcs-impl/src/com/intellij/history/integration/patches/PatchCreator.java b/platform/lvcs-impl/src/com/intellij/history/integration/patches/PatchCreator.java index 7994f5961fa1..2f4dc002f43f 100644 --- a/platform/lvcs-impl/src/com/intellij/history/integration/patches/PatchCreator.java +++ b/platform/lvcs-impl/src/com/intellij/history/integration/patches/PatchCreator.java @@ -18,7 +18,7 @@ package com.intellij.history.integration.patches; import com.intellij.history.integration.IdeaGateway; import com.intellij.openapi.diff.impl.patch.FilePatch; -import com.intellij.openapi.diff.impl.patch.PatchBuilder; +import com.intellij.openapi.diff.impl.patch.TextPatchBuilder; import com.intellij.openapi.diff.impl.patch.UnifiedDiffWriter; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.Change; @@ -35,7 +35,7 @@ public class PatchCreator { throws IOException, VcsException { Writer writer = new OutputStreamWriter(new FileOutputStream(filePath)); try { - List patches = PatchBuilder.buildPatch(changes, gw.getBaseDir(), false, isReverse); + List patches = TextPatchBuilder.buildPatch(changes, gw.getBaseDir(), isReverse); String lineSeparator = CodeStyleSettingsManager.getInstance(gw.getProject()).getCurrentSettings().getLineSeparator(); UnifiedDiffWriter.write(patches, writer, lineSeparator); } diff --git a/platform/lvcs-impl/testSrc/com/intellij/historyIntegrTests/PatchingTestCase.java b/platform/lvcs-impl/testSrc/com/intellij/historyIntegrTests/PatchingTestCase.java index 6d89eb3df885..a36384218eb1 100644 --- a/platform/lvcs-impl/testSrc/com/intellij/historyIntegrTests/PatchingTestCase.java +++ b/platform/lvcs-impl/testSrc/com/intellij/historyIntegrTests/PatchingTestCase.java @@ -16,8 +16,10 @@ package com.intellij.historyIntegrTests; +import com.intellij.openapi.diff.impl.patch.BinaryFilePatch; import com.intellij.openapi.diff.impl.patch.FilePatch; import com.intellij.openapi.diff.impl.patch.PatchReader; +import com.intellij.openapi.diff.impl.patch.PatchVirtualFileReader; import com.intellij.openapi.diff.impl.patch.formove.PatchApplier; import com.intellij.openapi.vfs.VirtualFile; @@ -44,7 +46,7 @@ public abstract class PatchingTestCase extends IntegrationTestCase { protected void applyPatch() throws Exception { List patches = new ArrayList(); - PatchReader reader = new PatchReader(getFS().refreshAndFindFileByPath(patchFilePath)); + PatchReader reader = PatchVirtualFileReader.create(getFS().refreshAndFindFileByPath(patchFilePath)); while (true) { FilePatch p = reader.readNextPatch(); @@ -52,6 +54,6 @@ public abstract class PatchingTestCase extends IntegrationTestCase { patches.add(p); } - new PatchApplier(myProject, root, patches, null, null).execute(); + new PatchApplier(myProject, root, patches, null, null).execute(); } } diff --git a/platform/platform-api/src/com/intellij/execution/process/OSProcessHandler.java b/platform/platform-api/src/com/intellij/execution/process/OSProcessHandler.java index bdc7a7dfd412..8668e4d292a7 100644 --- a/platform/platform-api/src/com/intellij/execution/process/OSProcessHandler.java +++ b/platform/platform-api/src/com/intellij/execution/process/OSProcessHandler.java @@ -91,18 +91,19 @@ public class OSProcessHandler extends ProcessHandler { public ProcessWaitFor(final Process process) { myWaitSemaphore.down(); - final Runnable action = new Runnable() { + + myWaitForThreadFuture = executeOnPooledThread(new Runnable() { public void run() { try { myExitCode = process.waitFor(); } catch (InterruptedException ignored) { } - myWaitSemaphore.up(); + finally { + myWaitSemaphore.up(); + } } - }; - - myWaitForThreadFuture = executeOnPooledThread(action); + }); } public int waitFor() { diff --git a/platform/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeNode.java b/platform/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeNode.java index d18ed42417c6..45a7a5324bf0 100644 --- a/platform/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeNode.java +++ b/platform/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeNode.java @@ -21,6 +21,7 @@ import com.intellij.openapi.editor.colors.CodeInsightColors; import com.intellij.openapi.editor.colors.TextAttributesKey; import com.intellij.openapi.ide.CopyPasteManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Queryable; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.vcs.FileStatus; import org.jetbrains.annotations.NonNls; @@ -29,8 +30,9 @@ import org.jetbrains.annotations.Nullable; import java.awt.*; import java.util.Collection; +import java.util.Map; -public abstract class AbstractTreeNode extends PresentableNodeDescriptor implements NavigationItem { +public abstract class AbstractTreeNode extends PresentableNodeDescriptor implements NavigationItem, Queryable.Contributor { private AbstractTreeNode myParent; private T myValue; private NodeDescriptor myParentDescriptor; @@ -140,6 +142,23 @@ public abstract class AbstractTreeNode extends PresentableNodeDescriptor impl myValue = value; } + @Nullable + public String toTestString(Queryable.PrintInfo printInfo) { + if (getValue() instanceof Queryable) { + String text = Queryable.Util.print((Queryable)getValue(), printInfo, this); + if (text != null) return text; + } + + return getTestPresentation(); + } + + public void apply(Map info) { + } + + /** + * @deprecated use toTestString + * @return + */ @Nullable @NonNls public String getTestPresentation() { if (myName != null) { diff --git a/platform/platform-api/src/com/intellij/openapi/ui/Queryable.java b/platform/platform-api/src/com/intellij/openapi/ui/Queryable.java new file mode 100644 index 000000000000..93b5c85b2d49 --- /dev/null +++ b/platform/platform-api/src/com/intellij/openapi/ui/Queryable.java @@ -0,0 +1,98 @@ +/* + * Copyright 2000-2009 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.openapi.ui; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.LinkedHashMap; +import java.util.Map; + +public interface Queryable { + + void putInfo(Map info); + + static class PrintInfo { + String[] myIdKeys; + String[] myInfoKeys; + + public PrintInfo() { + this(null, null); + } + + public PrintInfo(@Nullable String[] idKeys) { + this(idKeys, null); + } + + public PrintInfo(@Nullable String[] idKeys, @Nullable String[] infoKeys) { + myIdKeys = idKeys; + myInfoKeys = infoKeys; + } + } + + static class Util { + @Nullable + public static String print(@NotNull Queryable ui, @Nullable PrintInfo printInfo, @Nullable Contributor contributor) { + PrintInfo print = printInfo != null ? printInfo : new PrintInfo(); + + LinkedHashMap map = new LinkedHashMap(); + ui.putInfo(map); + + if (contributor != null) { + contributor.apply(map); + } + + String id = null; + + String[] names = print.myIdKeys != null ? print.myIdKeys : new String[] {"name"}; + for (String eachKey : names) { + String eachValue = map.get(eachKey); + if (eachValue != null) { + id = eachValue; + } + } + + if (map.size() > 0) { + id = map.values().iterator().next(); + } + + StringBuffer info = new StringBuffer(); + if (print.myInfoKeys != null) { + for (String eachKey : print.myInfoKeys) { + String eachValue = map.get(eachKey); + if (eachValue != null) { + if (info.length() > 0) { + info.append(","); + } + info.append(eachKey).append("=").append(eachValue); + } + } + } + + return id + (info.length() > 0 ? " " + info.toString() : ""); + } + + @Nullable + public static String print(@NotNull Queryable ui, @Nullable PrintInfo printInfo) { + return print(ui, printInfo, null); + } + } + + interface Contributor { + void apply(Map info); + } + +} \ No newline at end of file diff --git a/platform/platform-api/src/com/intellij/openapi/util/process/ProcessCloseUtil.java b/platform/platform-api/src/com/intellij/openapi/util/process/ProcessCloseUtil.java index 7081c5fa1beb..6f6db5543ffd 100644 --- a/platform/platform-api/src/com/intellij/openapi/util/process/ProcessCloseUtil.java +++ b/platform/platform-api/src/com/intellij/openapi/util/process/ProcessCloseUtil.java @@ -19,8 +19,8 @@ import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.util.concurrency.Semaphore; -import java.util.concurrent.Future; import java.io.IOException; +import java.util.concurrent.Future; public class ProcessCloseUtil { private static final long ourSynchronousWaitTimeout = 1000; @@ -34,25 +34,32 @@ public class ProcessCloseUtil { outerSemaphore.down(); final Application application = ApplicationManager.getApplication(); - final Future future = application.executeOnPooledThread(new Runnable() { + application.executeOnPooledThread(new Runnable() { public void run() { - final Semaphore semaphore = new Semaphore(); - semaphore.down(); + try { + final Semaphore semaphore = new Semaphore(); + semaphore.down(); - final Runnable closeRunnable = new Runnable() { - public void run() { - closeProcessImpl(process); - semaphore.up(); + final Runnable closeRunnable = new Runnable() { + public void run() { + try { + closeProcessImpl(process); + } + finally { + semaphore.up(); + } + } + }; + + final Future innerFuture = application.executeOnPooledThread(closeRunnable); + semaphore.waitFor(ourAsynchronousWaitTimeout); + if ( ! (innerFuture.isDone() || innerFuture.isCancelled())) { + innerFuture.cancel(true); // will call interrupt() } - }; - - final Future innerFuture = application.executeOnPooledThread(closeRunnable); - semaphore.waitFor(ourAsynchronousWaitTimeout); - if ( ! (innerFuture.isDone() || innerFuture.isCancelled())) { - innerFuture.cancel(true); // will call interrupt() } - - outerSemaphore.up(); + finally { + outerSemaphore.up(); + } } }); diff --git a/platform/platform-api/src/com/intellij/ui/tabs/TabInfo.java b/platform/platform-api/src/com/intellij/ui/tabs/TabInfo.java index 39a680408f58..63eeb5c50303 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/TabInfo.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/TabInfo.java @@ -16,7 +16,7 @@ package com.intellij.ui.tabs; import com.intellij.openapi.actionSystem.ActionGroup; -import com.intellij.openapi.ui.TestableUi; +import com.intellij.openapi.ui.Queryable; import com.intellij.openapi.util.IconLoader; import com.intellij.ui.SimpleColoredText; import com.intellij.ui.SimpleTextAttributes; @@ -29,7 +29,7 @@ import java.beans.PropertyChangeSupport; import java.lang.ref.WeakReference; import java.util.Map; -public final class TabInfo implements TestableUi { +public final class TabInfo implements Queryable { public static final String ACTION_GROUP = "actionGroup"; public static final String ICON = "icon"; @@ -80,7 +80,7 @@ public final class TabInfo implements TestableUi { private boolean myEnabled = true; private Color myTabColor = null; - private TestableUi myTestableUi; + private Queryable myQueryable; public TabInfo(final JComponent component) { myComponent = component; @@ -343,14 +343,14 @@ public final class TabInfo implements TestableUi { return myTabColor; } - public TabInfo setTestableUi(TestableUi testableUi) { - myTestableUi = testableUi; + public TabInfo setTestableUi(Queryable queryable) { + myQueryable = queryable; return this; } public void putInfo(Map info) { - if (myTestableUi != null) { - myTestableUi.putInfo(info); + if (myQueryable != null) { + myQueryable.putInfo(info); } } } diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java index 85054ea3447e..c5ab21249932 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java @@ -19,8 +19,8 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Queryable; import com.intellij.openapi.ui.ShadowAction; -import com.intellij.openapi.ui.TestableUi; import com.intellij.openapi.util.*; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.wm.FocusCommand; @@ -56,7 +56,7 @@ import java.util.*; import java.util.List; public class JBTabsImpl extends JComponent - implements JBTabs, PropertyChangeListener, TimerListener, DataProvider, PopupMenuListener, Disposable, JBTabsPresentation, TestableUi { + implements JBTabs, PropertyChangeListener, TimerListener, DataProvider, PopupMenuListener, Disposable, JBTabsPresentation, Queryable { static DataKey NAVIGATION_ACTIONS_KEY = DataKey.create("JBTabs"); diff --git a/platform/platform-api/src/com/intellij/ui/treeStructure/Tree.java b/platform/platform-api/src/com/intellij/ui/treeStructure/Tree.java index 4af5395f6b23..8145957398b3 100644 --- a/platform/platform-api/src/com/intellij/ui/treeStructure/Tree.java +++ b/platform/platform-api/src/com/intellij/ui/treeStructure/Tree.java @@ -17,7 +17,7 @@ package com.intellij.ui.treeStructure; import com.intellij.Patches; import com.intellij.ide.util.treeView.*; -import com.intellij.openapi.ui.TestableUi; +import com.intellij.openapi.ui.Queryable; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.wm.impl.content.GraphicsConfig; @@ -40,7 +40,7 @@ import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Map; -public class Tree extends JTree implements Autoscroll, TestableUi { +public class Tree extends JTree implements Autoscroll, Queryable { private AsyncProcessIcon myBusyIcon; private boolean myBusy; diff --git a/platform/platform-api/src/com/intellij/util/ui/DialogUtil.java b/platform/platform-api/src/com/intellij/util/ui/DialogUtil.java index a40134fee4f7..a09c07867f41 100644 --- a/platform/platform-api/src/com/intellij/util/ui/DialogUtil.java +++ b/platform/platform-api/src/com/intellij/util/ui/DialogUtil.java @@ -25,7 +25,12 @@ import javax.swing.*; * @since 5.1 */ public class DialogUtil { + public static void registerMnemonic(AbstractButton button) { + registerMnemonic(button, UIUtil.MNEMONIC); + } + + public static void registerMnemonic(AbstractButton button, char mn) { String text = button.getText(); if (text != null) { @@ -34,7 +39,7 @@ public class DialogUtil { int index = -1; for (int i = 0; i < text.length(); i++) { char ch = text.charAt(i); - if (ch != UIUtil.MNEMONIC) { + if (ch != mn) { realText.append(ch); } else if (i + 1 < text.length()) { diff --git a/platform/platform-api/src/com/intellij/util/ui/OptionsDialog.java b/platform/platform-api/src/com/intellij/util/ui/OptionsDialog.java index f1b84baca9c1..a3fd2f94eb6a 100644 --- a/platform/platform-api/src/com/intellij/util/ui/OptionsDialog.java +++ b/platform/platform-api/src/com/intellij/util/ui/OptionsDialog.java @@ -28,7 +28,7 @@ import java.awt.*; public abstract class OptionsDialog extends DialogWrapper { - private JCheckBox myCheckBoxDoNotShowDialog; + protected JCheckBox myCheckBoxDoNotShowDialog; protected String getDoNotShowMessage() { return CommonBundle.message("dialog.options.do.not.show"); @@ -68,6 +68,7 @@ public abstract class OptionsDialog extends DialogWrapper { final JPanel panel = addDoNotShowCheckBox(southPanel, myCheckBoxDoNotShowDialog); myCheckBoxDoNotShowDialog.setSelected(!isToBeShown()); + DialogUtil.registerMnemonic(myCheckBoxDoNotShowDialog, '&'); return panel; } diff --git a/platform/platform-impl/src/com/intellij/openapi/command/impl/DocumentReferenceByVirtualFile.java b/platform/platform-impl/src/com/intellij/openapi/command/impl/DocumentReferenceByVirtualFile.java index 25c33c45aa49..dc2c2643658f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/command/impl/DocumentReferenceByVirtualFile.java +++ b/platform/platform-impl/src/com/intellij/openapi/command/impl/DocumentReferenceByVirtualFile.java @@ -31,7 +31,7 @@ public class DocumentReferenceByVirtualFile implements DocumentReference { @Nullable public Document getDocument() { - assert myFile.isValid() : "should not be called on references to deleted files"; + assert myFile.isValid() : "should not be called on references to deleted file: " + myFile; return FileDocumentManager.getInstance().getDocument(myFile); } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorComponentImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorComponentImpl.java index e6c07a9ca7ad..5a7076f1913f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorComponentImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorComponentImpl.java @@ -21,7 +21,7 @@ import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.impl.ApplicationImpl; import com.intellij.openapi.editor.ex.util.EditorUtil; -import com.intellij.openapi.ui.TestableUi; +import com.intellij.openapi.ui.Queryable; import com.intellij.openapi.ui.TypingTarget; import com.intellij.openapi.util.ActionCallback; @@ -35,7 +35,7 @@ import java.util.Map; /** * */ -public class EditorComponentImpl extends JComponent implements Scrollable, DataProvider, TestableUi, TypingTarget { +public class EditorComponentImpl extends JComponent implements Scrollable, DataProvider, Queryable, TypingTarget { private final EditorImpl myEditor; public EditorComponentImpl(EditorImpl editor) { 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 66ae17517baf..aae9bffc6093 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 @@ -53,7 +53,7 @@ import com.intellij.openapi.editor.markup.*; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.TestableUi; +import com.intellij.openapi.ui.Queryable; import com.intellij.openapi.util.*; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; @@ -101,7 +101,7 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -public final class EditorImpl extends UserDataHolderBase implements EditorEx, HighlighterClient, TestableUi { +public final class EditorImpl extends UserDataHolderBase implements EditorEx, HighlighterClient, Queryable { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.editor.impl.EditorImpl"); private static final Key DND_COMMAND_KEY = Key.create("DndCommand"); public static final Key DO_DOCUMENT_UPDATE_TEST = Key.create("DoDocumentUpdateTest"); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EmptyMarkupModel.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EmptyMarkupModel.java index 907486af825e..eb86a65e1c29 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EmptyMarkupModel.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EmptyMarkupModel.java @@ -54,11 +54,11 @@ public class EmptyMarkupModel implements MarkupModelEx { return RangeHighlighter.EMPTY_ARRAY; } - public T getUserData(Key key) { + public T getUserData(@NotNull Key key) { return null; } - public void putUserData(Key key, T value) { + public void putUserData(@NotNull Key key, T value) { } public void dispose() { diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/IndentsModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/IndentsModelImpl.java index ddfa1218e4c3..59527d4d3734 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/IndentsModelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/IndentsModelImpl.java @@ -19,8 +19,9 @@ */ package com.intellij.openapi.editor.impl; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.editor.*; +import com.intellij.openapi.editor.IndentGuideDescriptor; +import com.intellij.openapi.editor.IndentsModel; +import com.intellij.openapi.editor.LogicalPosition; import java.util.ArrayList; import java.util.List; @@ -37,9 +38,12 @@ public class IndentsModelImpl implements IndentsModel { final LogicalPosition pos = myEditor.getCaretModel().getLogicalPosition(); final int column = pos.column; final int line = pos.line; - for (IndentGuideDescriptor indent : myIndents) { - if (column == indent.indentLevel && line >= indent.startLine && line < indent.endLine) { - return indent; + + if (column > 0) { + for (IndentGuideDescriptor indent : myIndents) { + if (column == indent.indentLevel && line >= indent.startLine && line < indent.endLine) { + return indent; + } } } return null; diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java index 4ba35866079a..b801faaf87a3 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java @@ -29,7 +29,7 @@ import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.ShadowAction; -import com.intellij.openapi.ui.TestableUi; +import com.intellij.openapi.ui.Queryable; import com.intellij.openapi.util.*; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.wm.IdeFocusManager; @@ -222,7 +222,7 @@ final class EditorTabbedContainer implements Disposable { if (tab != null) return; tab = new TabInfo(comp).setText(calcTabTitle(myProject, file)).setIcon(icon).setTooltipText(tooltip).setObject(file).setTabColor(calcTabColor(myProject, file)); - tab.setTestableUi(new MyTestableUi(tab)); + tab.setTestableUi(new MyQueryable(tab)); final DefaultActionGroup tabActions = new DefaultActionGroup(); tabActions.add(new CloseTab(comp, tab)); @@ -231,11 +231,11 @@ final class EditorTabbedContainer implements Disposable { myTabs.addTab(tab, indexToInsert); } - private class MyTestableUi implements TestableUi { + private class MyQueryable implements Queryable { private TabInfo myTab; - public MyTestableUi(TabInfo tab) { + public MyQueryable(TabInfo tab) { myTab = tab; } diff --git a/platform/platform-impl/src/com/intellij/openapi/progress/util/SmoothProgressAdapter.java b/platform/platform-impl/src/com/intellij/openapi/progress/util/SmoothProgressAdapter.java index 7b22b3b1e736..950d6ea57978 100644 --- a/platform/platform-impl/src/com/intellij/openapi/progress/util/SmoothProgressAdapter.java +++ b/platform/platform-impl/src/com/intellij/openapi/progress/util/SmoothProgressAdapter.java @@ -142,8 +142,12 @@ public class SmoothProgressAdapter extends BlockingProgressIndicator { } ); - super.stop(); // should be last to not leaveModal before closing the dialog - semaphore.up(); + try { + super.stop(); // should be last to not leaveModal before closing the dialog + } + finally { + semaphore.up(); + } } public synchronized void setText(String text) { diff --git a/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.java b/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.java index 4e4a68361ce1..24005c0169ec 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.java @@ -227,10 +227,10 @@ public class DumbServiceImpl extends DumbService { final Semaphore semaphore = new Semaphore(); semaphore.down(); runWhenSmart(new Runnable() { - public void run() { - semaphore.up(); - } - }); + public void run() { + semaphore.up(); + } + }); semaphore.waitFor(); } diff --git a/platform/platform-impl/src/com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.java b/platform/platform-impl/src/com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.java index c6355f453877..ba030419efd7 100644 --- a/platform/platform-impl/src/com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.java @@ -32,7 +32,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.DialogWrapperDialog; import com.intellij.openapi.ui.DialogWrapperPeer; -import com.intellij.openapi.ui.TestableUi; +import com.intellij.openapi.ui.Queryable; import com.intellij.openapi.ui.popup.StackingPopupDispatcher; import com.intellij.openapi.util.*; import com.intellij.openapi.util.registry.Registry; @@ -458,7 +458,7 @@ public class DialogWrapperPeerImpl extends DialogWrapperPeer implements FocusTra } - private static class MyDialog extends JDialog implements DialogWrapperDialog, DataProvider, FocusTrackback.Provider, TestableUi { + private static class MyDialog extends JDialog implements DialogWrapperDialog, DataProvider, FocusTrackback.Provider, Queryable { private final WeakReference myDialogWrapper; /** * Initial size of the dialog. When the dialog is being closed and diff --git a/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/AssertFocused.java b/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/AssertFocused.java index f146d4a4dc77..22c60e3f2ead 100644 --- a/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/AssertFocused.java +++ b/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/AssertFocused.java @@ -15,7 +15,7 @@ */ package com.intellij.openapi.ui.playback.commands; -import com.intellij.openapi.ui.TestableUi; +import com.intellij.openapi.ui.Queryable; import com.intellij.openapi.ui.playback.PlaybackRunner; import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.wm.IdeFocusManager; @@ -77,8 +77,8 @@ public class AssertFocused extends AbstractCommand { Component eachParent = owner; final LinkedHashMap actual = new LinkedHashMap(); while (eachParent != null) { - if (eachParent instanceof TestableUi) { - ((TestableUi)eachParent).putInfo(actual); + if (eachParent instanceof Queryable) { + ((Queryable)eachParent).putInfo(actual); } eachParent = eachParent.getParent(); diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerImpl.java b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerImpl.java index 697ce7a7cc9b..85a52d461d70 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerImpl.java @@ -152,7 +152,7 @@ public class VirtualFilePointerImpl extends UserDataHolderBase implements Virtua myLastUpdated = fsModCount; if (myFile == null) { - LOG.assertTrue(myUrl != null); + LOG.assertTrue(myUrl != null, "Both file & url are null"); myFile = myVirtualFileManager.findFileByUrl(myUrl); if (myFile != null) { myUrl = null; diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/InternalDecorator.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/InternalDecorator.java index 6e07ab8b7eac..200c4aef79ff 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/InternalDecorator.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/InternalDecorator.java @@ -28,7 +28,7 @@ import com.intellij.openapi.keymap.ex.KeymapManagerEx; import com.intellij.openapi.keymap.ex.WeakKeymapManagerListener; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.TestableUi; +import com.intellij.openapi.ui.Queryable; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.IconLoader; import com.intellij.openapi.util.SystemInfo; @@ -59,7 +59,7 @@ import java.util.Map; * @author Eugene Belyaev * @author Vladimir Kondratyev */ -public final class InternalDecorator extends JPanel implements TestableUi, TypeSafeDataProvider { +public final class InternalDecorator extends JPanel implements Queryable, TypeSafeDataProvider { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.wm.impl.InternalDecorator"); private static final int DIVIDER_WIDTH = 5; diff --git a/platform/platform-resources/src/META-INF/XmlPlugin.xml b/platform/platform-resources/src/META-INF/XmlPlugin.xml index 98b39a2de6cf..0b04b7fe2ad7 100644 --- a/platform/platform-resources/src/META-INF/XmlPlugin.xml +++ b/platform/platform-resources/src/META-INF/XmlPlugin.xml @@ -289,7 +289,7 @@ implementationClass="com.intellij.codeInsight.hint.XmlImplementationTextSelectioner"/> - + diff --git a/platform/testFramework/src/com/intellij/mock/MockDocument.java b/platform/testFramework/src/com/intellij/mock/MockDocument.java index 6eee48594df8..07ff7eb4db50 100644 --- a/platform/testFramework/src/com/intellij/mock/MockDocument.java +++ b/platform/testFramework/src/com/intellij/mock/MockDocument.java @@ -156,12 +156,12 @@ public class MockDocument implements DocumentEx { } @SuppressWarnings({"unchecked"}) - public T getUserData(Key key) { + public T getUserData(@NotNull Key key) { return (T)myUserData.get(key); } @SuppressWarnings({"unchecked"}) - public void putUserData(Key key, T value) { + public void putUserData(@NotNull Key key, T value) { myUserData.put(key, value); } diff --git a/platform/testFramework/src/com/intellij/testFramework/EditorTestUtil.java b/platform/testFramework/src/com/intellij/testFramework/EditorTestUtil.java new file mode 100644 index 000000000000..64d6170f3ec8 --- /dev/null +++ b/platform/testFramework/src/com/intellij/testFramework/EditorTestUtil.java @@ -0,0 +1,52 @@ +/* + * Copyright 2000-2010 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.testFramework; + +import com.intellij.ide.DataManager; +import com.intellij.openapi.actionSystem.IdeActions; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.actionSystem.EditorActionHandler; +import com.intellij.openapi.editor.actionSystem.EditorActionManager; +import com.intellij.openapi.editor.actionSystem.TypedAction; + +/** + * User: Maxim.Mossienko + * Date: 15.03.2010 + * Time: 21:00:49 + */ +public class EditorTestUtil { + public static final char BACKSPACE_FAKE_CHAR = '\uFFFF'; + public static final char SMART_ENTER_FAKE_CHAR = '\uFFFE'; + + public static void performTypingAction(Editor editor, char c) { + EditorActionManager actionManager = EditorActionManager.getInstance(); + if (c == BACKSPACE_FAKE_CHAR) { + EditorActionHandler actionHandler = actionManager.getActionHandler(IdeActions.ACTION_EDITOR_BACKSPACE); + actionHandler.execute(editor, DataManager.getInstance().getDataContext()); + } else if (c == SMART_ENTER_FAKE_CHAR) { + EditorActionHandler actionHandler = actionManager.getActionHandler(IdeActions.ACTION_EDITOR_COMPLETE_STATEMENT); + actionHandler.execute(editor, DataManager.getInstance().getDataContext()); + } + else if (c == '\n') { + EditorActionHandler actionHandler = actionManager.getActionHandler(IdeActions.ACTION_EDITOR_ENTER); + actionHandler.execute(editor, DataManager.getInstance().getDataContext()); + } + else { + TypedAction action = actionManager.getTypedAction(); + action.actionPerformed(editor, c, DataManager.getInstance().getDataContext()); + } + } +} diff --git a/platform/testFramework/src/com/intellij/testFramework/PlatformTestUtil.java b/platform/testFramework/src/com/intellij/testFramework/PlatformTestUtil.java index 3ec6a5aa7281..44f9263ad9c7 100644 --- a/platform/testFramework/src/com/intellij/testFramework/PlatformTestUtil.java +++ b/platform/testFramework/src/com/intellij/testFramework/PlatformTestUtil.java @@ -29,6 +29,7 @@ import com.intellij.openapi.extensions.ExtensionPoint; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.extensions.ExtensionsArea; +import com.intellij.openapi.ui.Queryable; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Disposer; @@ -64,9 +65,13 @@ public class PlatformTestUtil { }); } - protected static String toString(Object node) { + protected static String toString(Object node, Queryable.PrintInfo printInfo) { if (node instanceof AbstractTreeNode) { - return ((AbstractTreeNode)node).getTestPresentation(); + if (printInfo != null) { + return ((AbstractTreeNode)node).toTestString(printInfo); + } else { + return ((AbstractTreeNode)node).getTestPresentation(); + } } else if (node == null) { return "NULL"; @@ -95,7 +100,7 @@ public class PlatformTestUtil { final Object userObject = defaultMutableTreeNode.getUserObject(); String nodeText; if (userObject != null) { - nodeText = toString(userObject); + nodeText = toString(userObject, null); } else { nodeText = defaultMutableTreeNode + ""; @@ -207,14 +212,34 @@ public class PlatformTestUtil { return Comparing.equal(who, SystemProperties.getUserName(), false); } + /** + * @deprecated use {@link #print(AbstractTreeStructure structure, + Object node, + int currentLevel, + Comparator comparator, + int maxRowCount, + char paddingChar, + String[] dumpNames)} + */ public static StringBuffer print(AbstractTreeStructure structure, Object node, int currentLevel, Comparator comparator, int maxRowCount, char paddingChar) { + + return print(structure, node, currentLevel, comparator, maxRowCount, paddingChar, null); + } + + public static StringBuffer print(AbstractTreeStructure structure, + Object node, + int currentLevel, + Comparator comparator, + int maxRowCount, + char paddingChar, + Queryable.PrintInfo printInfo) { StringBuffer buffer = new StringBuffer(); - doPrint(buffer, currentLevel, node, structure, comparator, maxRowCount, 0, paddingChar); + doPrint(buffer, currentLevel, node, structure, comparator, maxRowCount, 0, paddingChar, printInfo); return buffer; } @@ -226,10 +251,22 @@ public class PlatformTestUtil { int maxRowCount, int currentLine, char paddingChar) { + return doPrint(buffer, currentLevel, node, structure, comparator, maxRowCount, currentLine, paddingChar, null); + } + + private static int doPrint(StringBuffer buffer, + int currentLevel, + Object node, + AbstractTreeStructure structure, + Comparator comparator, + int maxRowCount, + int currentLine, + char paddingChar, + Queryable.PrintInfo printInfo) { if (currentLine >= maxRowCount && maxRowCount != -1) return currentLine; StringUtil.repeatSymbol(buffer, paddingChar, currentLevel); - buffer.append(toString(node)).append("\n"); + buffer.append(toString(node, printInfo)).append("\n"); currentLine++; Object[] children = structure.getChildElements(node); @@ -239,7 +276,7 @@ public class PlatformTestUtil { children = ArrayUtil.toObjectArray(list); } for (Object child : children) { - currentLine = doPrint(buffer, currentLevel + 1, child, structure, comparator, maxRowCount, currentLine, paddingChar); + currentLine = doPrint(buffer, currentLevel + 1, child, structure, comparator, maxRowCount, currentLine, paddingChar, printInfo); } return currentLine; @@ -253,7 +290,7 @@ public class PlatformTestUtil { StringBuilder result = new StringBuilder(); for (Iterator iterator = c.iterator(); iterator.hasNext();) { Object each = iterator.next(); - result.append(toString(each)); + result.append(toString(each, null)); if (iterator.hasNext()) { result.append("\n"); } @@ -265,7 +302,7 @@ public class PlatformTestUtil { public static String print(ListModel model) { StringBuilder result = new StringBuilder(); for (int i = 0; i < model.getSize(); i++) { - result.append(toString(model.getElementAt(i))); + result.append(toString(model.getElementAt(i), null)); result.append("\n"); } return result.toString(); diff --git a/platform/usageView/src/com/intellij/usages/UsageViewSettings.java b/platform/usageView/src/com/intellij/usages/UsageViewSettings.java index cdaf24566a3e..a5de4380aa78 100644 --- a/platform/usageView/src/com/intellij/usages/UsageViewSettings.java +++ b/platform/usageView/src/com/intellij/usages/UsageViewSettings.java @@ -49,6 +49,7 @@ public class UsageViewSettings implements PersistentStateComponent rules = new ArrayList(); rules.add(new NonCodeUsageGroupingRule()); + if (UsageViewSettings.getInstance().GROUP_BY_SCOPE) { + rules.add(new UsageScopeGroupingRule()); + } if (UsageViewSettings.getInstance().GROUP_BY_USAGE_TYPE) { rules.add(new UsageTypeGroupingRule()); } @@ -88,6 +91,7 @@ public class UsageGroupingRuleProviderImpl implements UsageGroupingRuleProvider } }); + final GroupByScopeAction groupByScopeAction = new GroupByScopeAction(impl); if(view.getPresentation().isCodeUsages()) { final GroupByUsageTypeAction groupByUsageTypeAction = new GroupByUsageTypeAction(impl); groupByUsageTypeAction.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_T, InputEvent.CTRL_DOWN_MASK)), component); @@ -104,15 +108,17 @@ public class UsageGroupingRuleProviderImpl implements UsageGroupingRuleProvider return new AnAction[] { groupByUsageTypeAction, + groupByScopeAction, groupByModuleTypeAction, groupByPackageAction, - groupByFileStructureAction + groupByFileStructureAction, }; } else { return new AnAction[] { + groupByScopeAction, groupByModuleTypeAction, - groupByFileStructureAction + groupByFileStructureAction, }; } } @@ -129,6 +135,18 @@ public class UsageGroupingRuleProviderImpl implements UsageGroupingRuleProvider } } + private static class GroupByScopeAction extends RuleAction { + private GroupByScopeAction(UsageViewImpl view) { + super(view, "Group by test/production", Icons.TEST_SOURCE_FOLDER); + } + protected boolean getOptionValue() { + return UsageViewSettings.getInstance().GROUP_BY_SCOPE; + } + protected void setOptionValue(boolean value) { + UsageViewSettings.getInstance().GROUP_BY_SCOPE = value; + } + } + private static class GroupByModuleTypeAction extends RuleAction { private GroupByModuleTypeAction(UsageViewImpl view) { super(view, UsageViewBundle.message("action.group.by.module"), IconLoader.getIcon("/objectBrowser/showModules.png")); diff --git a/platform/usageView/src/com/intellij/usages/impl/rules/UsageScopeGroupingRule.java b/platform/usageView/src/com/intellij/usages/impl/rules/UsageScopeGroupingRule.java new file mode 100644 index 000000000000..77e667f01aef --- /dev/null +++ b/platform/usageView/src/com/intellij/usages/impl/rules/UsageScopeGroupingRule.java @@ -0,0 +1,101 @@ +/* + * Copyright 2000-2010 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.usages.impl.rules; + +import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.vcs.FileStatus; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiElement; +import com.intellij.psi.util.PsiUtilBase; +import com.intellij.usages.Usage; +import com.intellij.usages.UsageGroup; +import com.intellij.usages.UsageView; +import com.intellij.usages.rules.PsiElementUsage; +import com.intellij.usages.rules.UsageGroupingRule; +import com.intellij.util.Icons; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +/** + * @author max + */ +public class UsageScopeGroupingRule implements UsageGroupingRule { + public UsageGroup groupUsage(Usage usage) { + if (!(usage instanceof PsiElementUsage)) { + return null; + } + PsiElementUsage elementUsage = (PsiElementUsage)usage; + + PsiElement element = elementUsage.getElement(); + VirtualFile virtualFile = PsiUtilBase.getVirtualFile(element); + + if (virtualFile == null) { + return null; + } + boolean isInTest = ProjectRootManager.getInstance(element.getProject()).getFileIndex().isInTestSourceContent(virtualFile); + return isInTest ? TEST : PRODUCTION; + } + + private static final UsageScopeGroup TEST = new UsageScopeGroup(true); + private static final UsageScopeGroup PRODUCTION = new UsageScopeGroup(false); + private static class UsageScopeGroup implements UsageGroup { + private final boolean isTest; + + private UsageScopeGroup(boolean isTest) { + this.isTest = isTest; + } + + public void update() { + } + + public Icon getIcon(boolean isOpen) { + return isTest ? Icons.TEST_SOURCE_FOLDER : Icons.SOURCE_FOLDERS_ICON; + } + + @NotNull + public String getText(UsageView view) { + return isTest ? "Test" : "Production"; + } + + public FileStatus getFileStatus() { + return null; + } + + public boolean isValid() { return true; } + public void navigate(boolean focus) { } + public boolean canNavigate() { return false; } + + public boolean canNavigateToSource() { + return false; + } + + public int compareTo(UsageGroup usageGroup) { + return getText(null).compareTo(usageGroup.getText(null)); + } + + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof UsageScopeGroup)) return false; + final UsageScopeGroup usageTypeGroup = (UsageScopeGroup)o; + return isTest == usageTypeGroup.isTest; + } + + public int hashCode() { + return isTest ? 0 : 1; + } + } +} diff --git a/platform/usageView/src/com/intellij/usages/impl/rules/UsageType.java b/platform/usageView/src/com/intellij/usages/impl/rules/UsageType.java index 09b96d088862..3a720fd16a3e 100644 --- a/platform/usageView/src/com/intellij/usages/impl/rules/UsageType.java +++ b/platform/usageView/src/com/intellij/usages/impl/rules/UsageType.java @@ -17,7 +17,7 @@ package com.intellij.usages.impl.rules; import com.intellij.usageView.UsageViewBundle; -public class UsageType { +public final class UsageType { public static final UsageType CLASS_INSTANCE_OF = new UsageType(UsageViewBundle.message("usage.type.instanceof")); public static final UsageType CLASS_IMPORT = new UsageType(UsageViewBundle.message("usage.type.import")); public static final UsageType CLASS_CAST_TO = new UsageType(UsageViewBundle.message("usage.type.cast.target")); diff --git a/platform/usageView/src/com/intellij/usages/impl/rules/UsageTypeGroupingRule.java b/platform/usageView/src/com/intellij/usages/impl/rules/UsageTypeGroupingRule.java index fbd45e4e071f..fba4bdf0b878 100644 --- a/platform/usageView/src/com/intellij/usages/impl/rules/UsageTypeGroupingRule.java +++ b/platform/usageView/src/com/intellij/usages/impl/rules/UsageTypeGroupingRule.java @@ -72,9 +72,6 @@ public class UsageTypeGroupingRule implements UsageGroupingRule { return null; } - - - private class UsageTypeGroup implements UsageGroup { private final UsageType myUsageType; diff --git a/platform/util/src/com/intellij/openapi/diff/impl/patch/PatchHunk.java b/platform/util/src/com/intellij/openapi/diff/impl/patch/PatchHunk.java new file mode 100644 index 000000000000..3ca09d8d68f3 --- /dev/null +++ b/platform/util/src/com/intellij/openapi/diff/impl/patch/PatchHunk.java @@ -0,0 +1,89 @@ +/* + * Copyright 2000-2010 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: yole + * Date: 15.11.2006 + * Time: 20:20:15 + */ +package com.intellij.openapi.diff.impl.patch; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class PatchHunk { + private final int myStartLineBefore; + private final int myEndLineBefore; + private final int myStartLineAfter; + private final int myEndLineAfter; + private final List myLines = new ArrayList(); + + public PatchHunk(final int startLineBefore, final int endLineBefore, final int startLineAfter, final int endLineAfter) { + myStartLineBefore = startLineBefore; + myEndLineBefore = endLineBefore; + myStartLineAfter = startLineAfter; + myEndLineAfter = endLineAfter; + } + + public int getStartLineBefore() { + return myStartLineBefore; + } + + public int getEndLineBefore() { + return myEndLineBefore; + } + + public int getStartLineAfter() { + return myStartLineAfter; + } + + public int getEndLineAfter() { + return myEndLineAfter; + } + + public void addLine(final PatchLine line) { + myLines.add(line); + } + + public List getLines() { + return Collections.unmodifiableList(myLines); + } + + public boolean isNewContent() { + return myStartLineBefore == -1 && myEndLineBefore == -1; + } + + public boolean isDeletedContent() { + return myStartLineAfter == -1 && myEndLineAfter == -1; + } + + public String getText() { + StringBuilder builder = new StringBuilder(); + for(PatchLine line: myLines) { + builder.append(line.getText()).append("\n"); + } + return builder.toString(); + } + + public boolean isNoNewLineAtEnd() { + if (myLines.size() == 0) { + return false; + } + return myLines.get(myLines.size()-1).isSuppressNewLine(); + } +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchLine.java b/platform/util/src/com/intellij/openapi/diff/impl/patch/PatchLine.java similarity index 100% rename from platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchLine.java rename to platform/util/src/com/intellij/openapi/diff/impl/patch/PatchLine.java diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/processing/DiffFragmentsProcessor.java b/platform/util/src/com/intellij/openapi/diff/impl/processing/DiffFragmentsProcessor.java similarity index 100% rename from platform/platform-impl/src/com/intellij/openapi/diff/impl/processing/DiffFragmentsProcessor.java rename to platform/util/src/com/intellij/openapi/diff/impl/processing/DiffFragmentsProcessor.java diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/processing/LineFragmentsCollector.java b/platform/util/src/com/intellij/openapi/diff/impl/processing/LineFragmentsCollector.java similarity index 100% rename from platform/platform-impl/src/com/intellij/openapi/diff/impl/processing/LineFragmentsCollector.java rename to platform/util/src/com/intellij/openapi/diff/impl/processing/LineFragmentsCollector.java diff --git a/platform/util/src/com/intellij/openapi/util/UserDataHolderBase.java b/platform/util/src/com/intellij/openapi/util/UserDataHolderBase.java index b2acd934eed1..056dee0267ff 100644 --- a/platform/util/src/com/intellij/openapi/util/UserDataHolderBase.java +++ b/platform/util/src/com/intellij/openapi/util/UserDataHolderBase.java @@ -70,12 +70,12 @@ public class UserDataHolderBase implements UserDataHolderEx, Cloneable { } } - public T getUserData(Key key) { + public T getUserData(@NotNull Key key) { final Map map = myUserMap; return map == null ? null : (T)map.get(key); } - public void putUserData(Key key, T value) { + public void putUserData(@NotNull Key key, T value) { Map map = getOrCreateMap(); if (value == null) { diff --git a/platform/util/src/com/intellij/util/ui/UIUtil.java b/platform/util/src/com/intellij/util/ui/UIUtil.java index a70d4b8ab392..84fdb70b1581 100644 --- a/platform/util/src/com/intellij/util/ui/UIUtil.java +++ b/platform/util/src/com/intellij/util/ui/UIUtil.java @@ -42,10 +42,8 @@ import java.awt.*; import java.awt.event.*; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.util.*; import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.regex.Pattern; @@ -1064,5 +1062,28 @@ public class UIUtil { return g instanceof PrintGraphics; } + public static int getSelectedButton(ButtonGroup group) { + Enumeration enumeration = group.getElements(); + int i = 0; + while (enumeration.hasMoreElements()) { + AbstractButton button = enumeration.nextElement(); + if (group.isSelected(button.getModel())) { + return i; + } + i++; + } + return -1; + } + + public static void setSelectedButton(ButtonGroup group, int index) { + Enumeration enumeration = group.getElements(); + int i = 0; + while (enumeration.hasMoreElements()) { + AbstractButton button = enumeration.nextElement(); + group.setSelected(button.getModel(), index == i); + i++; + } + } + } diff --git a/platform/util/testSrc/com/intellij/util/Assertion.java b/platform/util/testSrc/com/intellij/util/Assertion.java index e71a26bc4225..d8be62de91ea 100644 --- a/platform/util/testSrc/com/intellij/util/Assertion.java +++ b/platform/util/testSrc/com/intellij/util/Assertion.java @@ -238,7 +238,6 @@ public class Assertion extends Assert { public void size(int size, Collection collection) { if (collection.size() != size) { System.err.println("Expected: " + size + " actual: " + collection.size()); - compareUnordered(ArrayUtil.EMPTY_OBJECT_ARRAY, collection); } Assert.assertEquals(size, collection.size()); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/BinaryFilePatch.java b/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/BinaryFilePatch.java similarity index 72% rename from platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/BinaryFilePatch.java rename to platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/BinaryFilePatch.java index 31ee275760f8..6fe7ff86f336 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/BinaryFilePatch.java +++ b/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/BinaryFilePatch.java @@ -15,10 +15,6 @@ */ package com.intellij.openapi.diff.impl.patch; -import com.intellij.openapi.vfs.VirtualFile; - -import java.io.IOException; - /** * @author yole */ @@ -31,15 +27,6 @@ public class BinaryFilePatch extends FilePatch { myAfterContent = afterContent; } - protected void applyCreate(final VirtualFile newFile) throws IOException, ApplyPatchException { - newFile.setBinaryContent(myAfterContent); - } - - protected ApplyPatchStatus applyChange(final VirtualFile fileToPatch) throws IOException, ApplyPatchException { - fileToPatch.setBinaryContent(myAfterContent); - return ApplyPatchStatus.SUCCESS; - } - public boolean isNewFile() { return myBeforeContent == null; } @@ -47,4 +34,8 @@ public class BinaryFilePatch extends FilePatch { public boolean isDeletedFile() { return myAfterContent == null; } + + public byte[] getAfterContent() { + return myAfterContent; + } } diff --git a/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/FilePatch.java b/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/FilePatch.java new file mode 100644 index 000000000000..b1f8fb3dd621 --- /dev/null +++ b/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/FilePatch.java @@ -0,0 +1,83 @@ +/* + * Copyright 2000-2009 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: yole + * Date: 15.11.2006 + * Time: 17:53:24 + */ +package com.intellij.openapi.diff.impl.patch; + +import com.intellij.openapi.util.text.StringUtil; + +public abstract class FilePatch { + private String myBeforeName; + private String myAfterName; + private String myBeforeVersionId; + private String myAfterVersionId; + + public String getBeforeName() { + return myBeforeName; + } + + public String getAfterName() { + return myAfterName; + } + + public String getBeforeFileName() { + String[] pathNameComponents = myBeforeName.split("/"); + return pathNameComponents [pathNameComponents.length-1]; + } + + public String getAfterFileName() { + String[] pathNameComponents = myAfterName.split("/"); + return pathNameComponents [pathNameComponents.length-1]; + } + + public void setBeforeName(final String fileName) { + myBeforeName = fileName; + } + + public void setAfterName(final String fileName) { + myAfterName = fileName; + } + + public String getBeforeVersionId() { + return myBeforeVersionId; + } + + public void setBeforeVersionId(final String beforeVersionId) { + myBeforeVersionId = beforeVersionId; + } + + public String getAfterVersionId() { + return myAfterVersionId; + } + + public void setAfterVersionId(final String afterVersionId) { + myAfterVersionId = afterVersionId; + } + + public String getAfterNameRelative(int skipDirs) { + String[] components = myAfterName.split("/"); + return StringUtil.join(components, skipDirs, components.length, "/"); + } + + public abstract boolean isNewFile(); + + public abstract boolean isDeletedFile(); +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchReader.java b/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/PatchReader.java similarity index 96% rename from platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchReader.java rename to platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/PatchReader.java index 7baf2fe36210..288b39586d53 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchReader.java +++ b/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/PatchReader.java @@ -22,17 +22,14 @@ */ package com.intellij.openapi.diff.impl.patch; -import com.intellij.openapi.fileEditor.impl.LoadTextUtil; import com.intellij.openapi.util.text.LineTokenizer; -import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.Nullable; -import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.List; -import java.util.ArrayList; public class PatchReader { @NonNls public static final String NO_NEWLINE_SIGNATURE = "\\ No newline at end of file"; @@ -48,12 +45,6 @@ public class PatchReader { @NonNls private static final Pattern ourContextBeforeHunkStartPattern = Pattern.compile("\\*\\*\\* (\\d+),(\\d+) \\*\\*\\*\\*"); @NonNls private static final Pattern ourContextAfterHunkStartPattern = Pattern.compile("--- (\\d+),(\\d+) ----"); - public PatchReader(VirtualFile virtualFile) throws IOException { - byte[] patchContents = virtualFile.contentsToByteArray(); - CharSequence patchText = LoadTextUtil.getTextByBinaryPresentation(patchContents, virtualFile); - myLines = LineTokenizer.tokenize(patchText, false); - } - public PatchReader(CharSequence patchContent) { myLines = LineTokenizer.tokenize(patchContent, false); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchSyntaxException.java b/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/PatchSyntaxException.java similarity index 100% rename from platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchSyntaxException.java rename to platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/PatchSyntaxException.java diff --git a/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/TextFilePatch.java b/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/TextFilePatch.java new file mode 100644 index 000000000000..e92baf5dd8d2 --- /dev/null +++ b/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/TextFilePatch.java @@ -0,0 +1,63 @@ +/* + * Copyright 2000-2009 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.openapi.diff.impl.patch; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * @author yole + */ +public class TextFilePatch extends FilePatch { + private final List myHunks; + + public void addHunk(final PatchHunk hunk) { + myHunks.add(hunk); + } + + public List getHunks() { + return Collections.unmodifiableList(myHunks); + } + + public TextFilePatch() { + myHunks = new ArrayList(); + } + + private TextFilePatch(final TextFilePatch patch) { + setBeforeVersionId(patch.getBeforeVersionId()); + setAfterVersionId(patch.getAfterVersionId()); + setBeforeName(patch.getBeforeName()); + setAfterName(patch.getAfterName()); + myHunks = patch.myHunks; + } + + public TextFilePatch pathsOnlyCopy() { + return new TextFilePatch(this); + } + + public boolean isNewFile() { + return myHunks.size() == 1 && myHunks.get(0).isNewContent(); + } + + public String getNewFileText() { + return myHunks.get(0).getText(); + } + + public boolean isDeletedFile() { + return myHunks.size() == 1 && myHunks.get(0).isDeletedContent(); + } +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchBuilder.java b/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/TextPatchBuilder.java similarity index 98% rename from platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchBuilder.java rename to platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/TextPatchBuilder.java index 606fa0bf8363..27be1b147901 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchBuilder.java +++ b/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/TextPatchBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2010 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. @@ -44,11 +44,11 @@ import java.util.List; /** * @author yole */ -public class PatchBuilder { +public class TextPatchBuilder { private static final int CONTEXT_LINES = 3; @NonNls private static final String REVISION_NAME_TEMPLATE = "(revision {0})"; - private PatchBuilder() { + private TextPatchBuilder() { } private static void checkCanceled(final ProgressIndicator ind) { @@ -57,8 +57,7 @@ public class PatchBuilder { } } - public static List buildPatch(final Collection changes, final String basePath, final boolean allowRename, - final boolean reversePatch) throws VcsException { + public static List buildPatch(final Collection changes, final String basePath, final boolean reversePatch) throws VcsException { final ProgressIndicator ind = ProgressManager.getInstance().getProgressIndicator(); List result = new ArrayList(); diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/UnifiedDiffWriter.java b/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/UnifiedDiffWriter.java similarity index 100% rename from platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/UnifiedDiffWriter.java rename to platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/UnifiedDiffWriter.java index 44d3a8475061..037cbb0c925e 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/UnifiedDiffWriter.java +++ b/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/UnifiedDiffWriter.java @@ -26,8 +26,8 @@ import org.jetbrains.annotations.NonNls; import java.io.IOException; import java.io.Writer; -import java.util.Collection; import java.text.MessageFormat; +import java.util.Collection; public class UnifiedDiffWriter { @NonNls private static final String INDEX_SIGNATURE = "Index: {0}{1}"; diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/VcsConfiguration.java b/platform/vcs-api/src/com/intellij/openapi/vcs/VcsConfiguration.java index 63159e43c35c..59dc044c95c5 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/VcsConfiguration.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/VcsConfiguration.java @@ -63,6 +63,7 @@ public final class VcsConfiguration implements PersistentStateComponent public boolean PERFORM_ROLLBACK_IN_BACKGROUND = false; public volatile boolean CHECK_LOCALLY_CHANGED_CONFLICTS_IN_BACKGROUND = false; public VcsShowConfirmationOption.Value MOVE_TO_FAILED_COMMIT_CHANGELIST = VcsShowConfirmationOption.Value.SHOW_CONFIRMATION; + public VcsShowConfirmationOption.Value REMOVE_EMPTY_INACTIVE_CHANGELISTS = VcsShowConfirmationOption.Value.SHOW_CONFIRMATION; public boolean ENABLE_BACKGROUND_PROCESSES = false; public int CHANGED_ON_SERVER_INTERVAL = 60; diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/VcsShowConfirmationOption.java b/platform/vcs-api/src/com/intellij/openapi/vcs/VcsShowConfirmationOption.java index 4c43ad78a904..d077e7621ee4 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/VcsShowConfirmationOption.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/VcsShowConfirmationOption.java @@ -27,7 +27,10 @@ public interface VcsShowConfirmationOption { myId = id; } - + public int getId() { + return myId; + } + public String toString() { return String.valueOf(myId); } diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ChangeListListener.java b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ChangeListListener.java index 3c5398f44f58..3d30fcc55261 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ChangeListListener.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ChangeListListener.java @@ -21,6 +21,9 @@ import java.util.Collection; /** * @author max + * + * @see com.intellij.openapi.vcs.changes.ChangeListManager#addChangeListListener(ChangeListListener) + * @see com.intellij.openapi.vcs.changes.ChangeListManager#removeChangeListListener(ChangeListListener) */ public interface ChangeListListener extends EventListener { void changeListAdded(ChangeList list); diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchVirtualFileReader.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchVirtualFileReader.java new file mode 100644 index 000000000000..d416c384cb44 --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchVirtualFileReader.java @@ -0,0 +1,32 @@ +/* + * Copyright 2000-2010 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.openapi.diff.impl.patch; + +import com.intellij.openapi.fileEditor.impl.LoadTextUtil; +import com.intellij.openapi.vfs.VirtualFile; + +import java.io.IOException; + +public class PatchVirtualFileReader { + private PatchVirtualFileReader() { + } + + public static PatchReader create(final VirtualFile virtualFile) throws IOException { + final byte[] patchContents = virtualFile.contentsToByteArray(); + final CharSequence patchText = LoadTextUtil.getTextByBinaryPresentation(patchContents, virtualFile); + return new PatchReader(patchText); + } +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/TextFilePatch.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/TextFilePatch.java deleted file mode 100644 index 502bac91a554..000000000000 --- a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/TextFilePatch.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2000-2009 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.openapi.diff.impl.patch; - -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.fileEditor.impl.LoadTextUtil; -import com.intellij.openapi.fileEditor.FileDocumentManager; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.util.text.LineTokenizer; - -import java.util.List; -import java.util.ArrayList; -import java.util.Collections; -import java.io.IOException; - -/** - * @author yole - */ -public class TextFilePatch extends FilePatch { - private final List myHunks; - - public void addHunk(final PatchHunk hunk) { - myHunks.add(hunk); - } - - public List getHunks() { - return Collections.unmodifiableList(myHunks); - } - - public TextFilePatch() { - myHunks = new ArrayList(); - } - - private TextFilePatch(final TextFilePatch patch) { - setBeforeVersionId(patch.getBeforeVersionId()); - setAfterVersionId(patch.getAfterVersionId()); - setBeforeName(patch.getBeforeName()); - setAfterName(patch.getAfterName()); - myHunks = patch.myHunks; - } - - public TextFilePatch pathsOnlyCopy() { - return new TextFilePatch(this); - } - - protected ApplyPatchStatus applyChange(final VirtualFile fileToPatch) throws IOException, ApplyPatchException { - byte[] fileContents = fileToPatch.contentsToByteArray(); - CharSequence text = LoadTextUtil.getTextByBinaryPresentation(fileContents, fileToPatch); - StringBuilder newText = new StringBuilder(); - ApplyPatchStatus status = applyModifications(text, newText); - if (status != ApplyPatchStatus.ALREADY_APPLIED) { - final Document document = FileDocumentManager.getInstance().getDocument(fileToPatch); - if (document == null) { - throw new ApplyPatchException("Failed to set contents for updated file " + fileToPatch.getPath()); - } - document.setText(newText.toString()); - FileDocumentManager.getInstance().saveDocument(document); - } - return status; - } - - public ApplyPatchStatus applyModifications(final CharSequence text, final StringBuilder newText) throws ApplyPatchException { - if (myHunks.size() == 0) { - return ApplyPatchStatus.SUCCESS; - } - List lines = new ArrayList(); - Collections.addAll(lines, LineTokenizer.tokenize(text, false)); - ApplyPatchStatus result = null; - for(PatchHunk hunk: myHunks) { - result = ApplyPatchStatus.and(result, hunk.apply(lines)); - } - for(int i=0; i { + public ApplyBinaryFilePatch(BinaryFilePatch patch) { + super(patch); + } + + protected void applyCreate(final VirtualFile newFile) throws IOException, ApplyPatchException { + newFile.setBinaryContent(myPatch.getAfterContent()); + } + + protected ApplyPatchStatus applyChange(final VirtualFile fileToPatch) throws IOException, ApplyPatchException { + fileToPatch.setBinaryContent(myPatch.getAfterContent()); + return ApplyPatchStatus.SUCCESS; + } +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyBinaryShelvedFilePatch.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyBinaryShelvedFilePatch.java new file mode 100644 index 000000000000..076cf64dc1e7 --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyBinaryShelvedFilePatch.java @@ -0,0 +1,38 @@ +/* + * Copyright 2000-2010 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.openapi.diff.impl.patch.apply; + +import com.intellij.openapi.diff.impl.patch.ApplyPatchException; +import com.intellij.openapi.diff.impl.patch.ApplyPatchStatus; +import com.intellij.openapi.vcs.changes.shelf.ShelveChangesManager; +import com.intellij.openapi.vfs.VirtualFile; + +import java.io.IOException; + +public class ApplyBinaryShelvedFilePatch extends ApplyFilePatchBase { + public ApplyBinaryShelvedFilePatch(ShelveChangesManager.ShelvedBinaryFilePatch patch) { + super(patch); + } + + @Override + protected ApplyPatchStatus applyChange(VirtualFile fileToPatch) throws IOException, ApplyPatchException { + return null; + } + + @Override + protected void applyCreate(VirtualFile newFile) throws IOException, ApplyPatchException { + } +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyFilePatch.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyFilePatch.java new file mode 100644 index 000000000000..03e8a5639362 --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyFilePatch.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000-2010 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.openapi.diff.impl.patch.apply; + +import com.intellij.openapi.diff.impl.patch.ApplyPatchContext; +import com.intellij.openapi.diff.impl.patch.ApplyPatchException; +import com.intellij.openapi.diff.impl.patch.ApplyPatchStatus; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; + +public interface ApplyFilePatch { + ApplyPatchStatus apply(VirtualFile fileToPatch, ApplyPatchContext context, Project project) throws IOException, ApplyPatchException; + + ApplyPatchStatus applyImpl(VirtualFile fileToPatch, Project project) throws IOException, ApplyPatchException; + + @Nullable + VirtualFile findFileToPatch(@NotNull ApplyPatchContext context) throws IOException; +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/FilePatch.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyFilePatchBase.java similarity index 74% rename from platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/FilePatch.java rename to platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyFilePatchBase.java index d33a87f8c292..502dc684693f 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/FilePatch.java +++ b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyFilePatchBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2010 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. @@ -13,18 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.intellij.openapi.diff.impl.patch.apply; -/* - * Created by IntelliJ IDEA. - * User: yole - * Date: 15.11.2006 - * Time: 17:53:24 - */ -package com.intellij.openapi.diff.impl.patch; - +import com.intellij.openapi.diff.impl.patch.*; import com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.util.text.LineTokenizer; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.FilePathImpl; import com.intellij.openapi.vfs.VirtualFile; @@ -32,77 +26,39 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; -public abstract class FilePatch { - private String myBeforeName; - private String myAfterName; - private String myBeforeVersionId; - private String myAfterVersionId; +public abstract class ApplyFilePatchBase implements ApplyFilePatch { + protected final T myPatch; - public String getBeforeName() { - return myBeforeName; + public ApplyFilePatchBase(T patch) { + myPatch = patch; } - public String getAfterName() { - return myAfterName; + public T getPatch() { + return myPatch; } - public String getBeforeFileName() { - String[] pathNameComponents = myBeforeName.split("/"); - return pathNameComponents [pathNameComponents.length-1]; - } - - public String getAfterFileName() { - String[] pathNameComponents = myAfterName.split("/"); - return pathNameComponents [pathNameComponents.length-1]; - } - - public void setBeforeName(final String fileName) { - myBeforeName = fileName; - } - - public void setAfterName(final String fileName) { - myAfterName = fileName; - } - - public String getBeforeVersionId() { - return myBeforeVersionId; - } - - public void setBeforeVersionId(final String beforeVersionId) { - myBeforeVersionId = beforeVersionId; - } - - public String getAfterVersionId() { - return myAfterVersionId; - } - - public void setAfterVersionId(final String afterVersionId) { - myAfterVersionId = afterVersionId; - } - - public String getAfterNameRelative(int skipDirs) { - String[] components = myAfterName.split("/"); - return StringUtil.join(components, skipDirs, components.length, "/"); - } - - public FilePath getTarget(final VirtualFile file) { - if (isNewFile()) { - return new FilePathImpl(file, getBeforeFileName(), false); + private FilePath getTarget(final VirtualFile file) { + if (myPatch.isNewFile()) { + return new FilePathImpl(file, myPatch.getBeforeFileName(), false); } return new FilePathImpl(file); } - public ApplyPatchStatus apply(final VirtualFile fileToPatch, final ApplyPatchContext context, final Project project) throws IOException, ApplyPatchException { + public ApplyPatchStatus apply(final VirtualFile fileToPatch, final ApplyPatchContext context, final Project project) throws + IOException, ApplyPatchException { context.addAffectedFile(getTarget(fileToPatch)); return applyImpl(fileToPatch, project); } public ApplyPatchStatus applyImpl(final VirtualFile fileToPatch, final Project project) throws IOException, ApplyPatchException { - if (isNewFile()) { + if (myPatch.isNewFile()) { applyCreate(fileToPatch); } - else if (isDeletedFile()) { + else if (myPatch.isDeletedFile()) { FileEditorManagerImpl.getInstance(project).closeFile(fileToPatch); fileToPatch.delete(this); } @@ -113,11 +69,12 @@ public abstract class FilePatch { } protected abstract void applyCreate(VirtualFile newFile) throws IOException, ApplyPatchException; + @Nullable protected abstract ApplyPatchStatus applyChange(VirtualFile fileToPatch) throws IOException, ApplyPatchException; @Nullable public VirtualFile findFileToPatch(@NotNull ApplyPatchContext context) throws IOException { - return findPatchTarget(context, myBeforeName, myAfterName, isNewFile()); + return findPatchTarget(context, myPatch.getBeforeName(), myPatch.getAfterName(), myPatch.isNewFile()); } @Nullable @@ -217,7 +174,25 @@ public abstract class FilePatch { return patchedDir; } - public abstract boolean isNewFile(); - - public abstract boolean isDeletedFile(); + @Nullable + public static ApplyPatchStatus applyModifications(final TextFilePatch patch, final CharSequence text, final StringBuilder newText) throws + ApplyPatchException { + final List hunks = patch.getHunks(); + if (hunks.isEmpty()) { + return ApplyPatchStatus.SUCCESS; + } + List lines = new ArrayList(); + Collections.addAll(lines, LineTokenizer.tokenize(text, false)); + ApplyPatchStatus result = null; + for(PatchHunk hunk: hunks) { + result = ApplyPatchStatus.and(result, new ApplyPatchHunk(hunk).apply(lines)); + } + for(int i=0; i myLines = new ArrayList(); +public class ApplyPatchHunk { + private final PatchHunk myHunk; - public PatchHunk(final int startLineBefore, final int endLineBefore, final int startLineAfter, final int endLineAfter) { - myStartLineBefore = startLineBefore; - myEndLineBefore = endLineBefore; - myStartLineAfter = startLineAfter; - myEndLineAfter = endLineAfter; + public ApplyPatchHunk(final PatchHunk hunk) { + myHunk = hunk; } - - public int getStartLineBefore() { - return myStartLineBefore; - } - - public int getEndLineBefore() { - return myEndLineBefore; - } - - public int getStartLineAfter() { - return myStartLineAfter; - } - - public int getEndLineAfter() { - return myEndLineAfter; - } - - public void addLine(final PatchLine line) { - myLines.add(line); - } - - public List getLines() { - return Collections.unmodifiableList(myLines); - } - + public ApplyPatchStatus apply(final List lines) throws ApplyPatchException { List originalLines = new ArrayList(lines); try { @@ -77,9 +43,10 @@ public class PatchHunk { } private ApplyPatchStatus tryApply(final List lines, boolean acceptPartial) throws ApplyPatchException { + final List hunkLines = myHunk.getLines(); ApplyPatchStatus result = null; - int curLine = findStartLine(lines); - for(PatchLine line: myLines) { + int curLine = findStartLine(hunkLines, lines); + for(PatchLine line: hunkLines) { final String patchLineText = line.getText(); switch (line.getType()) { case CONTEXT: @@ -130,15 +97,16 @@ public class PatchHunk { } } - private int findStartLine(final List lines) throws ApplyPatchException { - int totalContextLines = countContextLines(); - if (getLinesProcessingContext(lines, myStartLineBefore) == totalContextLines) { - return myStartLineBefore; + private int findStartLine(final List hunkLines, final List lines) throws ApplyPatchException { + int totalContextLines = countContextLines(hunkLines); + final int startLineBefore = myHunk.getStartLineBefore(); + if (getLinesProcessingContext(hunkLines, lines, startLineBefore) == totalContextLines) { + return startLineBefore; } int maxContextStartLine = -1; int maxContextLines = 0; for(int i=0;i< lines.size(); i++) { - int contextLines = getLinesProcessingContext(lines, i); + int contextLines = getLinesProcessingContext(hunkLines, lines, i); if (contextLines == totalContextLines) { return i; } @@ -153,9 +121,9 @@ public class PatchHunk { return maxContextStartLine; } - private int countContextLines() { + private int countContextLines(final List hunkLines) { int count = 0; - for(PatchLine line: myLines) { + for(PatchLine line: hunkLines) { if (line.getType() == PatchLine.Type.CONTEXT || line.getType() == PatchLine.Type.REMOVE) { count++; } @@ -163,9 +131,9 @@ public class PatchHunk { return count; } - private int getLinesProcessingContext(final List lines, int startLine) { + private int getLinesProcessingContext(final List hunkLines, final List lines, int startLine) { int count = 0; - for(PatchLine line: myLines) { + for(PatchLine line: hunkLines) { PatchLine.Type type = line.getType(); if (type == PatchLine.Type.REMOVE || type == PatchLine.Type.CONTEXT) { // TODO: smarter algorithm (search outward from non-context lines) @@ -178,27 +146,4 @@ public class PatchHunk { } return count; } - - public boolean isNewContent() { - return myStartLineBefore == -1 && myEndLineBefore == -1; - } - - public boolean isDeletedContent() { - return myStartLineAfter == -1 && myEndLineAfter == -1; - } - - public String getText() { - StringBuilder builder = new StringBuilder(); - for(PatchLine line: myLines) { - builder.append(line.getText()).append("\n"); - } - return builder.toString(); - } - - public boolean isNoNewLineAtEnd() { - if (myLines.size() == 0) { - return false; - } - return myLines.get(myLines.size()-1).isSuppressNewLine(); - } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyTextFilePatch.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyTextFilePatch.java new file mode 100644 index 000000000000..307cfda70cbd --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyTextFilePatch.java @@ -0,0 +1,59 @@ +/* + * Copyright 2000-2010 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.openapi.diff.impl.patch.apply; + +import com.intellij.openapi.diff.impl.patch.ApplyPatchException; +import com.intellij.openapi.diff.impl.patch.ApplyPatchStatus; +import com.intellij.openapi.diff.impl.patch.TextFilePatch; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.fileEditor.impl.LoadTextUtil; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; + +public class ApplyTextFilePatch extends ApplyFilePatchBase { + public ApplyTextFilePatch(final TextFilePatch patch) { + super(patch); + } + + @Nullable + protected ApplyPatchStatus applyChange(final VirtualFile fileToPatch) throws IOException, ApplyPatchException { + byte[] fileContents = fileToPatch.contentsToByteArray(); + CharSequence text = LoadTextUtil.getTextByBinaryPresentation(fileContents, fileToPatch); + StringBuilder newText = new StringBuilder(); + ApplyPatchStatus status = ApplyFilePatchBase.applyModifications(myPatch, text, newText); + if (status != ApplyPatchStatus.ALREADY_APPLIED) { + final Document document = FileDocumentManager.getInstance().getDocument(fileToPatch); + if (document == null) { + throw new ApplyPatchException("Failed to set contents for updated file " + fileToPatch.getPath()); + } + document.setText(newText.toString()); + FileDocumentManager.getInstance().saveDocument(document); + } + return status; + } + + protected void applyCreate(final VirtualFile newFile) throws IOException, ApplyPatchException { + final Document document = FileDocumentManager.getInstance().getDocument(newFile); + if (document == null) { + throw new ApplyPatchException("Failed to set contents for new file " + newFile.getPath()); + } + document.setText(myPatch.getNewFileText()); + FileDocumentManager.getInstance().saveDocument(document); + } +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/formove/CustomBinaryPatchApplier.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/formove/CustomBinaryPatchApplier.java index cdbcfe35f13b..fc735ac8924f 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/formove/CustomBinaryPatchApplier.java +++ b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/formove/CustomBinaryPatchApplier.java @@ -17,6 +17,7 @@ package com.intellij.openapi.diff.impl.patch.formove; import com.intellij.openapi.diff.impl.patch.ApplyPatchStatus; import com.intellij.openapi.diff.impl.patch.FilePatch; +import com.intellij.openapi.diff.impl.patch.apply.ApplyFilePatchBase; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; @@ -24,9 +25,9 @@ import org.jetbrains.annotations.NotNull; import java.io.IOException; import java.util.List; -public interface CustomBinaryPatchApplier { +public interface CustomBinaryPatchApplier { @NotNull - ApplyPatchStatus apply(List> patches) throws IOException; + ApplyPatchStatus apply(List>> patches) throws IOException; @NotNull List getAppliedPatches(); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/formove/PatchApplier.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/formove/PatchApplier.java index eaa713eb261b..7ff2c143a0a0 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/formove/PatchApplier.java +++ b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/formove/PatchApplier.java @@ -21,6 +21,8 @@ import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.diff.impl.patch.ApplyPatchContext; import com.intellij.openapi.diff.impl.patch.ApplyPatchStatus; import com.intellij.openapi.diff.impl.patch.FilePatch; +import com.intellij.openapi.diff.impl.patch.apply.ApplyFilePatchBase; +import com.intellij.openapi.diff.impl.patch.apply.ApplyTextFilePatch; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.fileTypes.FileTypes; import com.intellij.openapi.fileTypes.ex.FileTypeChooser; @@ -52,25 +54,25 @@ import java.util.List; /** * for patches. for shelve. */ -public class PatchApplier { +public class PatchApplier { private final Project myProject; private final VirtualFile myBaseDirectory; private final List myPatches; - private final CustomBinaryPatchApplier myCustomForBinaries; + private final CustomBinaryPatchApplier myCustomForBinaries; private final LocalChangeList myTargetChangeList; private final List myRemainingPatches; - private final PathsVerifier myVerifier; + private final PathsVerifier myVerifier; public PatchApplier(final Project project, final VirtualFile baseDirectory, final List patches, - final LocalChangeList targetChangeList, final CustomBinaryPatchApplier customForBinaries) { + final LocalChangeList targetChangeList, final CustomBinaryPatchApplier customForBinaries) { myProject = project; myBaseDirectory = baseDirectory; myPatches = patches; myTargetChangeList = targetChangeList; myCustomForBinaries = customForBinaries; myRemainingPatches = new ArrayList(); - myVerifier = new PathsVerifier(myProject, myBaseDirectory, myPatches, new PathsVerifier.BaseMapper() { + myVerifier = new PathsVerifier(myProject, myBaseDirectory, myPatches, new PathsVerifier.BaseMapper() { @Nullable public VirtualFile getFile(FilePatch patch, String path) { return PathMerger.getFile(myBaseDirectory, path); @@ -137,7 +139,7 @@ public class PatchApplier { return; } - final List> textPatches = myVerifier.getTextPatches(); + final List> textPatches = myVerifier.getTextPatches(); if (! fileTypesAreOk(textPatches)) { return; } @@ -184,8 +186,8 @@ public class PatchApplier { } @Nullable - private ApplyPatchStatus actualApply(final PathsVerifier verifier) { - final List> textPatches = verifier.getTextPatches(); + private ApplyPatchStatus actualApply(final PathsVerifier verifier) { + final List> textPatches = verifier.getTextPatches(); final ApplyPatchContext context = new ApplyPatchContext(myBaseDirectory, 0, true, true); ApplyPatchStatus status = null; @@ -195,7 +197,7 @@ public class PatchApplier { if (myCustomForBinaries == null) { status = applyList(verifier.getBinaryPatches(), context, status); } else { - final List> binaryPatches = verifier.getBinaryPatches(); + final List>> binaryPatches = verifier.getBinaryPatches(); ApplyPatchStatus patchStatus = myCustomForBinaries.apply(binaryPatches); final List appliedPatches = myCustomForBinaries.getAppliedPatches(); moveForCustomBinaries(binaryPatches, appliedPatches); @@ -211,24 +213,24 @@ public class PatchApplier { return status; } - private void moveForCustomBinaries(final List> patches, + private void moveForCustomBinaries(final List>> patches, final List appliedPatches) throws IOException { - for (Pair patch : patches) { - if (appliedPatches.contains(patch.getSecond())) { + for (Pair> patch : patches) { + if (appliedPatches.contains(patch.getSecond().getPatch())) { myVerifier.doMoveIfNeeded(patch.getFirst()); } } } - private ApplyPatchStatus applyList(final List> patches, final ApplyPatchContext context, + private > ApplyPatchStatus applyList(final List> patches, final ApplyPatchContext context, ApplyPatchStatus status) throws IOException { - for (Pair patch : patches) { + for (Pair patch : patches) { ApplyPatchStatus patchStatus = ApplyPatchAction.applyOnly(myProject, patch.getSecond(), context, patch.getFirst()); myVerifier.doMoveIfNeeded(patch.getFirst()); status = ApplyPatchStatus.and(status, patchStatus); if (patchStatus != ApplyPatchStatus.FAILURE) { - myRemainingPatches.remove(patch.getSecond()); + myRemainingPatches.remove(patch.getSecond().getPatch()); } else { // interrupt if failure return status; @@ -259,8 +261,8 @@ public class PatchApplier { return (! readonlyStatus.hasReadonlyFiles()); } - private boolean fileTypesAreOk(final List> textPatches) { - for (Pair textPatch : textPatches) { + private boolean fileTypesAreOk(final List> textPatches) { + for (Pair textPatch : textPatches) { final VirtualFile file = textPatch.getFirst(); if (! file.isDirectory()) { FileType fileType = file.getFileType(); diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/formove/PathsVerifier.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/formove/PathsVerifier.java index c5bf4a99724c..d2d57c6ac303 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/formove/PathsVerifier.java +++ b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/formove/PathsVerifier.java @@ -15,8 +15,12 @@ */ package com.intellij.openapi.diff.impl.patch.formove; +import com.intellij.openapi.diff.impl.patch.BinaryFilePatch; import com.intellij.openapi.diff.impl.patch.FilePatch; import com.intellij.openapi.diff.impl.patch.TextFilePatch; +import com.intellij.openapi.diff.impl.patch.apply.ApplyFilePatchBase; +import com.intellij.openapi.diff.impl.patch.apply.ApplyFilePatchFactory; +import com.intellij.openapi.diff.impl.patch.apply.ApplyTextFilePatch; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Pair; @@ -25,6 +29,7 @@ import com.intellij.openapi.vcs.FilePathImpl; import com.intellij.openapi.vcs.ProjectLevelVcsManager; import com.intellij.openapi.vcs.VcsBundle; import com.intellij.openapi.vcs.changes.patch.RelativePathCalculator; +import com.intellij.openapi.vcs.changes.shelf.ShelveChangesManager; import com.intellij.openapi.vcs.impl.ExcludedFileIndex; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; @@ -34,7 +39,7 @@ import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.util.*; -public class PathsVerifier { +public class PathsVerifier { // in private final Project myProject; private final VirtualFile myBaseDirectory; @@ -44,8 +49,8 @@ public class PathsVerifier { private final List myBeforePaths; private final List myCreatedDirectories; // out - private final List> myTextPatches; - private final List> myBinaryPatches; + private final List> myTextPatches; + private final List>> myBinaryPatches; private final List myWritableFiles; private final BaseMapper myBaseMapper; @@ -58,8 +63,8 @@ public class PathsVerifier { myMovedFiles = new HashMap(); myBeforePaths = new ArrayList(); myCreatedDirectories = new ArrayList(); - myTextPatches = new ArrayList>(); - myBinaryPatches = new ArrayList>(); + myTextPatches = new ArrayList>(); + myBinaryPatches = new ArrayList>>(); myWritableFiles = new ArrayList(); } @@ -284,11 +289,14 @@ public class PathsVerifier { } private void addPatch(final FilePatch patch, final VirtualFile file) { - final Pair patchPair = new Pair(file, patch); + final Pair patchPair = new Pair(file, ApplyFilePatchFactory.createGeneral(patch)); if (patch instanceof TextFilePatch) { - myTextPatches.add(patchPair); + myTextPatches.add(new Pair(file, ApplyFilePatchFactory.create((TextFilePatch) patch))); } else { - myBinaryPatches.add(patchPair); + final ApplyFilePatchBase applyBinaryPatch = (ApplyFilePatchBase) ((patch instanceof BinaryFilePatch) ? ApplyFilePatchFactory + .create((BinaryFilePatch) patch) : + ApplyFilePatchFactory.create((ShelveChangesManager.ShelvedBinaryFilePatch) patch)); + myBinaryPatches.add(new Pair>(file, applyBinaryPatch)); } myWritableFiles.add(file); } @@ -410,11 +418,11 @@ public class PathsVerifier { return child; } - public List> getTextPatches() { + public List> getTextPatches() { return myTextPatches; } - public List> getBinaryPatches() { + public List>> getBinaryPatches() { return myBinaryPatches; } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java index 6a201f6dda92..6b11ca594982 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java @@ -29,6 +29,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.*; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.*; import com.intellij.openapi.vcs.changes.conflicts.ChangelistConflictTracker; import com.intellij.openapi.vcs.changes.ui.CommitHelper; @@ -107,7 +108,7 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec return (ChangeListManagerImpl) project.getComponent(ChangeListManager.class); } - public ChangeListManagerImpl(final Project project) { + public ChangeListManagerImpl(Project project, final VcsConfiguration config) { myProject = project; myChangesViewManager = ChangesViewManager.getInstance(myProject); myFileStatusManager = FileStatusManager.getInstance(myProject); @@ -120,6 +121,45 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec myModifier = new Modifier(myWorker, myDelayedNotificator); myConflictTracker = new ChangelistConflictTracker(project, this, myFileStatusManager, EditorNotifications.getInstance(project)); + + myListeners.addListener(new ChangeListAdapter() { + @Override + public void defaultListChanged(final ChangeList oldDefaultList, ChangeList newDefaultList) { + if (!ApplicationManager.getApplication().isUnitTestMode() && + oldDefaultList instanceof LocalChangeList && + oldDefaultList.getChanges().isEmpty()) { + + invokeAfterUpdate(new Runnable() { + public void run() { + switch (config.REMOVE_EMPTY_INACTIVE_CHANGELISTS) { + + case SHOW_CONFIRMATION: + VcsConfirmationDialog dialog = new VcsConfirmationDialog(myProject, new VcsShowConfirmationOption() { + public Value getValue() { + return config.REMOVE_EMPTY_INACTIVE_CHANGELISTS; + } + + public void setValue(Value value) { + config.REMOVE_EMPTY_INACTIVE_CHANGELISTS = value; + } + }, "The empty changelist '" + StringUtil.last(oldDefaultList.getName(), 30, true) + "' is no longer active.
" + + "Do you want to remove it?", "&Remember my choice"); + dialog.show(); + if (!dialog.isOK()) { + return; + } + break; + case DO_NOTHING_SILENTLY: + return; + case DO_ACTION_SILENTLY: + break; + } + removeChangeList((LocalChangeList)oldDefaultList); + } + }, InvokeAfterUpdateMode.SILENT, null, null); + } + } + }); } public void projectOpened() { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsConfirmationDialog.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsConfirmationDialog.java new file mode 100644 index 000000000000..149c6c9cb089 --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsConfirmationDialog.java @@ -0,0 +1,90 @@ +/* + * Copyright 2000-2010 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.openapi.vcs.changes; + +import com.intellij.CommonBundle; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.vcs.VcsShowConfirmationOption; +import com.intellij.util.ui.OptionsDialog; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; + +/** + * @author Dmitry Avdeev + */ +public class VcsConfirmationDialog extends OptionsDialog { + + private final VcsShowConfirmationOption myOption; + private final String myMessage; + private final String myDoNotShowMessage; + + protected VcsConfirmationDialog(Project project, VcsShowConfirmationOption option, String message, String doNotShowMessage) { + super(project); + myOption = option; + myMessage = message; + myDoNotShowMessage = doNotShowMessage; + setTitle("Confirmation"); + init(); + } + + @Override + protected boolean isToBeShown() { + return myOption.getValue() == VcsShowConfirmationOption.Value.SHOW_CONFIRMATION; + } + + @Override + protected void setToBeShown(boolean value, boolean onOk) { + myOption.setValue(value ? VcsShowConfirmationOption.Value.SHOW_CONFIRMATION : onOk ? VcsShowConfirmationOption.Value.DO_ACTION_SILENTLY : VcsShowConfirmationOption.Value.DO_NOTHING_SILENTLY); + } + + @Override + protected boolean shouldSaveOptionsOnCancel() { + return true; + } + + @Override + protected JComponent createCenterPanel() { + JPanel panel = new JPanel(new BorderLayout(15, 0)); + panel.add(new JLabel(myMessage)); + panel.add(new JLabel(Messages.getQuestionIcon()), BorderLayout.WEST); + return panel; + } + + @Override + protected String getDoNotShowMessage() { + return myDoNotShowMessage; + } + + @Override + protected Action[] createActions() { + return new Action[] { + new AbstractAction(CommonBundle.getYesButtonText()) { + { + putValue(DEFAULT_ACTION, Boolean.TRUE); + } + public void actionPerformed(ActionEvent e) { + doOKAction(); + } + }, new AbstractAction(CommonBundle.getNoButtonText()) { + public void actionPerformed(ActionEvent e) { + doCancelAction(); + } + }}; + } +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/RevertCommittedStuffAbstractAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/RevertCommittedStuffAbstractAction.java index 395b4cbe6919..d953a1650c71 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/RevertCommittedStuffAbstractAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/RevertCommittedStuffAbstractAction.java @@ -18,8 +18,9 @@ package com.intellij.openapi.vcs.changes.actions; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.PlatformDataKeys; +import com.intellij.openapi.diff.impl.patch.BinaryFilePatch; import com.intellij.openapi.diff.impl.patch.FilePatch; -import com.intellij.openapi.diff.impl.patch.PatchBuilder; +import com.intellij.openapi.diff.impl.patch.TextPatchBuilder; import com.intellij.openapi.diff.impl.patch.formove.PatchApplier; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; @@ -70,13 +71,13 @@ abstract class RevertCommittedStuffAbstractAction extends AnAction implements Du List patches; try { - patches = PatchBuilder.buildPatch(changesList, baseDir.getPresentableUrl(), false, true); + patches = TextPatchBuilder.buildPatch(changesList, baseDir.getPresentableUrl(), true); } catch (VcsException ex) { Messages.showErrorDialog(project, "Failed to revert changes: " + ex.getMessage(), VcsBundle.message("revert.changes.title")); return; } - new PatchApplier(project, baseDir, patches, chooser.getSelectedList(), null).execute(); + new PatchApplier(project, baseDir, patches, chooser.getSelectedList(), null).execute(); } public void update(final AnActionEvent e) { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchAction.java index c96dacad9137..353b5223ce4f 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchAction.java @@ -32,6 +32,7 @@ import com.intellij.openapi.diff.DiffManager; import com.intellij.openapi.diff.DiffRequestFactory; import com.intellij.openapi.diff.MergeRequest; import com.intellij.openapi.diff.impl.patch.*; +import com.intellij.openapi.diff.impl.patch.apply.ApplyFilePatchBase; import com.intellij.openapi.diff.impl.patch.formove.PatchApplier; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileEditor.impl.LoadTextUtil; @@ -72,7 +73,7 @@ public class ApplyPatchAction extends AnAction { final Collection appliers = new LinkedList(); for (VirtualFile base : patchGroups.keySet()) { final PatchApplier patchApplier = - new PatchApplier(project, base, ObjectsConvertor.convert(patchGroups.get(base), new Convertor() { + new PatchApplier(project, base, ObjectsConvertor.convert(patchGroups.get(base), new Convertor() { public FilePatch convert(FilePatchInProgress o) { return o.getPatch(); } @@ -107,20 +108,21 @@ public class ApplyPatchAction extends AnAction { return sb.toString(); } - public static ApplyPatchStatus applyOnly(final Project project, final FilePatch patch, final ApplyPatchContext context, final VirtualFile file) { + public static ApplyPatchStatus applyOnly(final Project project, final ApplyFilePatchBase patch, final ApplyPatchContext context, final VirtualFile file) { + final T patchBase = patch.getPatch(); try { return patch.apply(file, context, project); } catch(ApplyPatchException ex) { - if (!patch.isNewFile() && !patch.isDeletedFile() && patch instanceof TextFilePatch) { + if (!patchBase.isNewFile() && !patchBase.isDeletedFile() && patchBase instanceof TextFilePatch) { //final VirtualFile beforeRename = (pathBeforeRename == null) ? file : pathBeforeRename; - ApplyPatchStatus mergeStatus = mergeAgainstBaseVersion(project, file, new FilePathImpl(file), (TextFilePatch) patch, + ApplyPatchStatus mergeStatus = mergeAgainstBaseVersion(project, file, new FilePathImpl(file), (TextFilePatch) patchBase, ApplyPatchMergeRequestFactory.INSTANCE); if (mergeStatus != null) { return mergeStatus; } } - Messages.showErrorDialog(project, VcsBundle.message("patch.apply.error", patch.getBeforeName(), ex.getMessage()), + Messages.showErrorDialog(project, VcsBundle.message("patch.apply.error", patchBase.getBeforeName(), ex.getMessage()), VcsBundle.message("patch.apply.dialog.title")); } catch (Exception ex) { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDialog.form b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDialog.form deleted file mode 100644 index fc880aeb17e9..000000000000 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDialog.form +++ /dev/null @@ -1,126 +0,0 @@ - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDialog.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDialog.java deleted file mode 100644 index 136892925fe6..000000000000 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDialog.java +++ /dev/null @@ -1,735 +0,0 @@ -/* - * Copyright 2000-2009 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.openapi.vcs.changes.patch; - -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.CommonShortcuts; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.command.CommandProcessor; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.diff.impl.patch.*; -import com.intellij.openapi.diff.impl.patch.formove.PathMerger; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.fileChooser.FileChooserDescriptor; -import com.intellij.openapi.fileEditor.FileDocumentManager; -import com.intellij.openapi.fileTypes.FileType; -import com.intellij.openapi.fileTypes.FileTypeManager; -import com.intellij.openapi.fileTypes.FileTypes; -import com.intellij.openapi.fileTypes.StdFileTypes; -import com.intellij.openapi.help.HelpManager; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.ui.TextFieldWithBrowseButton; -import com.intellij.openapi.util.Comparing; -import com.intellij.openapi.util.Computable; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.vcs.FilePath; -import com.intellij.openapi.vcs.FilePathImpl; -import com.intellij.openapi.vcs.FileStatus; -import com.intellij.openapi.vcs.VcsBundle; -import com.intellij.openapi.vcs.changes.*; -import com.intellij.openapi.vcs.changes.actions.ShowDiffAction; -import com.intellij.openapi.vcs.changes.ui.ChangeListChooserPanel; -import com.intellij.openapi.vfs.LocalFileSystem; -import com.intellij.openapi.vfs.VfsUtil; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.ui.CollectionListModel; -import com.intellij.ui.ColoredListCellRenderer; -import com.intellij.ui.DocumentAdapter; -import com.intellij.ui.SimpleTextAttributes; -import com.intellij.util.Alarm; -import com.intellij.util.ArrayUtil; -import com.intellij.util.Consumer; -import com.intellij.util.ui.UIUtil; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.PropertyKey; - -import javax.swing.*; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import javax.swing.event.DocumentEvent; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.io.File; -import java.io.IOException; -import java.util.*; -import java.util.List; - -/** - * @author yole - */ -public class ApplyPatchDialog extends DialogWrapper { - private final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.changes.patch.ApplyPatchDialog"); - - private JPanel myRootPanel; - private TextFieldWithBrowseButton myFileNameField; - private JLabel myStatusLabel; - private TextFieldWithBrowseButton myBaseDirectoryField; - private JSpinner myStripLeadingDirectoriesSpinner; - private JList myPatchContentsList; - private ChangeListChooserPanel myChangeListChooser; - private JButton myShowDiffButton; - private List myPatches; - private Collection myPatchesFailedToLoad; - private final Alarm myLoadPatchAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD); - private final Alarm myVerifyPatchAlarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD); - private String myLoadPatchError = null; - private String myDetectedBaseDirectory = null; - private int myDetectedStripLeadingDirs = -1; - private final Project myProject; - private boolean myInnerChange; - private LocalChangeList mySelectedChangeList; - - private final Map, String> myMoveRenameInfo; - - public ApplyPatchDialog(Project project) { - super(project, true); - myProject = project; - setTitle(VcsBundle.message("patch.apply.dialog.title")); - final FileChooserDescriptor descriptor = new FileChooserDescriptor(true, false, false, false, false, false) { - @Override - public boolean isFileSelectable(VirtualFile file) { - return file.getFileType() == StdFileTypes.PATCH || file.getFileType() == FileTypes.PLAIN_TEXT; - } - }; - myMoveRenameInfo = new HashMap, String>(); - myFileNameField.addBrowseFolderListener(VcsBundle.message("patch.apply.select.title"), "", project, descriptor); - myFileNameField.getTextField().getDocument().addDocumentListener(new DocumentAdapter() { - protected void textChanged(DocumentEvent e) { - updateOKAction(); - myStatusLabel.setForeground(UIUtil.getLabelForeground()); - myStatusLabel.setText(VcsBundle.message("patch.load.progress")); - myPatches = null; - myMoveRenameInfo.clear(); - myLoadPatchAlarm.cancelAllRequests(); - myLoadPatchAlarm.addRequest(new Runnable() { - public void run() { - checkLoadPatches(true); - } - }, 400); - } - }); - - myBaseDirectoryField.setText(project.getBaseDir().getPresentableUrl()); - myBaseDirectoryField.addBrowseFolderListener(VcsBundle.message("patch.apply.select.base.directory.title"), "", project, - new FileChooserDescriptor(false, true, false, false, false, false)); - myBaseDirectoryField.getTextField().getDocument().addDocumentListener(new DocumentAdapter() { - protected void textChanged(final DocumentEvent e) { - if (!myInnerChange) { - queueVerifyPatchPaths(); - } - } - }); - - myStripLeadingDirectoriesSpinner.setModel(new SpinnerNumberModel(0, 0, 256, 1)); - myStripLeadingDirectoriesSpinner.addChangeListener(new ChangeListener() { - public void stateChanged(final ChangeEvent e) { - if (!myInnerChange) { - queueVerifyPatchPaths(); - } - } - }); - - myPatchContentsList.setCellRenderer(new PatchCellRendererPanel()); - - ChangeListManager changeListManager = ChangeListManager.getInstance(project); - myChangeListChooser.setChangeLists(changeListManager.getChangeListsCopy()); - myChangeListChooser.setDefaultSelection(changeListManager.getDefaultChangeList()); - myChangeListChooser.init(project); - init(); - updateOKAction(); - myShowDiffButton.addActionListener(new ActionListener() { - public void actionPerformed(final ActionEvent e) { - showDiff(); - } - }); - myPatchContentsList.addMouseListener(new MouseAdapter() { - public void mouseClicked(final MouseEvent e) { - if (e.getButton() == 1 && e.getClickCount() == 2) { - showDiff(); - } - } - }); - - new AnAction() { - @Override - public void actionPerformed(AnActionEvent e) { - showDiff(); - } - }.registerCustomShortcutSet(CommonShortcuts.getDiff(), myRootPanel, myDisposable); - } - - private void showDiff() { - List changes = new ArrayList(); - ApplyPatchContext context = getApplyPatchContext().getPrepareContext(); - Object[] selection = myPatchContentsList.getSelectedValues(); - if (selection.length == 0) { - if (myPatches == null) return; - selection = ArrayUtil.toObjectArray(myPatches); - } - for(Object o: selection) { - final TextFilePatch patch = (TextFilePatch) o; - try { - if (patch.isNewFile()) { - final FilePath newFilePath = FilePathImpl.createNonLocal(patch.getAfterName(), false); - final String content = patch.getNewFileText(); - ContentRevision revision = new SimpleContentRevision(content, newFilePath, patch.getAfterVersionId()); - changes.add(new Change(null, revision)); - } else if ((! patch.isDeletedFile()) && (patch.getBeforeName() != null) && (patch.getAfterName() != null) && - (! patch.getBeforeName().equals(patch.getAfterName()))) { - - final VirtualFile baseDirectory = getBaseDirectory(); - final VirtualFile beforeFile = PathMerger.getFile(baseDirectory, patch.getBeforeName()); - - if (beforeFile != null) { - final List tail = new ArrayList(); - final VirtualFile partFile = PathMerger.getFile(baseDirectory, patch.getAfterName(), tail); - final StringBuilder sb = new StringBuilder(partFile.getPath()); - for (String s : tail) { - if (sb.charAt(sb.length() - 1) != '/') { - sb.append('/'); - } - sb.append(s); - } - - final Change change = - changeForPath(beforeFile, patch, FilePathImpl.createNonLocal(FileUtil.toSystemIndependentName(sb.toString()), false)); - if (change != null) { - changes.add(change); - } - } else { - Messages.showErrorDialog(myProject, "Cannot show difference: cannot find file " + patch.getBeforeName(), - VcsBundle.message("patch.apply.dialog.title")); - } - } - else { - final VirtualFile fileToPatch = patch.findFileToPatch(context); - if (fileToPatch != null) { - final FilePathImpl filePath = new FilePathImpl(fileToPatch); - final CurrentContentRevision currentRevision = new CurrentContentRevision(filePath); - if (patch.isDeletedFile()) { - changes.add(new Change(currentRevision, null)); - } - else { - final Change change = changeForPath(fileToPatch, patch, null); - if (change != null) { - changes.add(change); - } - } - } - } - } - catch (Exception e) { - Messages.showErrorDialog(myProject, "Error loading changes for " + patch.getAfterFileName() + ": " + e.getMessage(), - VcsBundle.message("patch.apply.dialog.title")); - return; - } - } - ShowDiffAction.showDiffForChange(changes.toArray(new Change[changes.size()]), 0, myProject, - ShowDiffAction.DiffExtendUIFactory.NONE, false); - } - - @Nullable - private Change changeForPath(final VirtualFile fileToPatch, final TextFilePatch patch, final FilePath newFilePath) { - try { - final FilePathImpl filePath = new FilePathImpl(fileToPatch); - final CurrentContentRevision currentRevision = new CurrentContentRevision(filePath); - final Document doc = FileDocumentManager.getInstance().getDocument(fileToPatch); - String baseContent = doc.getText(); - StringBuilder newText = new StringBuilder(); - patch.applyModifications(baseContent, newText); - ContentRevision revision = new SimpleContentRevision(newText.toString(), (newFilePath == null) ? filePath : newFilePath, patch.getAfterVersionId()); - return new Change(currentRevision, revision); - } catch (ApplyPatchException e) { - ApplyPatchContext context = new ApplyPatchContext(getBaseDirectory(), 0, false, false); - // just show diff here. maybe refactor further.. - ApplyPatchAction.mergeAgainstBaseVersion(myProject, fileToPatch, context, patch, ApplyPatchAction.ApplyPatchMergeRequestFactory.INSTANCE_READ_ONLY); - return null; - } - } - - @Override - @NonNls - protected String getDimensionServiceKey() { - return "vcs.ApplyPatchDialog"; - } - - private void queueVerifyPatchPaths() { - myStatusLabel.setForeground(UIUtil.getLabelForeground()); - myStatusLabel.setText(VcsBundle.message("apply.patch.progress.verifying")); - myVerifyPatchAlarm.cancelAllRequests(); - myVerifyPatchAlarm.addRequest(new Runnable() { - public void run() { - try { - if (myPatches != null) { - verifyPatchPaths(); - } - } - catch(Exception ex) { - LOG.error(ex); - } - } - }, 400); - } - - public void setFileName(String fileName) { - myFileNameField.setText(fileName); - checkLoadPatches(false); - } - - private void checkLoadPatches(final boolean async) { - final String fileName = myFileNameField.getText().replace(File.separatorChar, '/'); - final VirtualFile patchFile = ApplicationManager.getApplication().runWriteAction(new Computable() { - public VirtualFile compute() { - final VirtualFile file = LocalFileSystem.getInstance().refreshAndFindFileByPath(fileName); - if (file != null) { - file.refresh(false, false); - if (file.isDirectory()) { - // we are looking for file not directory - return null; - } - } - return file; - } - }); - if (patchFile == null) { - queueUpdateStatus("Cannot find patch file"); - return; - } - myChangeListChooser.setDefaultName(patchFile.getNameWithoutExtension().replace('_', ' ').trim()); - if (async) { - ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { - public void run() { - loadPatchesFromFile(patchFile); - } - }); - } - else { - loadPatchesFromFile(patchFile); - } - } - - private void loadPatchesFromFile(final VirtualFile patchFile) { - myPatches = new ArrayList(); - myPatchesFailedToLoad = new HashSet(); - ApplicationManager.getApplication().runReadAction(new Runnable() { - public void run() { - if (!patchFile.isValid()) { - queueUpdateStatus("Cannot find patch file"); - return; - } - PatchReader reader; - try { - reader = new PatchReader(patchFile); - } - catch (IOException e) { - queueUpdateStatus(VcsBundle.message("patch.apply.open.error", e.getMessage())); - return; - } - while(true) { - FilePatch patch; - try { - patch = reader.readNextPatch(); - } - catch (PatchSyntaxException e) { - if (e.getLine() >= 0) { - queueUpdateStatus(VcsBundle.message("patch.apply.load.error.line", e.getMessage(), e.getLine())); - } - else { - queueUpdateStatus(VcsBundle.message("patch.apply.load.error", e.getMessage())); - } - return; - } - if (patch == null) { - break; - } - - final String beforeName = patch.getBeforeName(); - final String afterName = patch.getAfterName(); - final String movedMessage = RelativePathCalculator.getMovedString(beforeName, afterName); - if (movedMessage != null) { - myMoveRenameInfo.put(new Pair(beforeName, afterName), movedMessage); - } - myPatches.add(patch); - } - if (myPatches.isEmpty()) { - queueUpdateStatus(VcsBundle.message("patch.apply.no.patches.found")); - return; - } - - autoDetectBaseDirectory(); - queueUpdateStatus(null); - } - }); - } - - private void autoDetectBaseDirectory() { - boolean autodetectFailed = false; - for(FilePatch patch: myPatches) { - VirtualFile baseDir = myDetectedBaseDirectory == null - ? getBaseDirectory() - : LocalFileSystem.getInstance().findFileByPath(myDetectedBaseDirectory.replace(File.separatorChar, '/')); - int skipTopDirs = myDetectedStripLeadingDirs >= 0 ? myDetectedStripLeadingDirs : 0; - VirtualFile fileToPatch; - try { - fileToPatch = patch.findFileToPatch(new ApplyPatchContext(baseDir, skipTopDirs, false, false)); - } - catch (IOException e) { - myPatchesFailedToLoad.add(patch); - continue; - } - if (fileToPatch == null) { - boolean success = false; - if (!autodetectFailed) { - String oldDetectedBaseDirectory = myDetectedBaseDirectory; - int oldDetectedStripLeadingDirs = myDetectedStripLeadingDirs; - success = detectDirectory(patch); - if (success) { - if ((oldDetectedBaseDirectory != null && !Comparing.equal(oldDetectedBaseDirectory, myDetectedBaseDirectory)) || - (oldDetectedStripLeadingDirs >= 0 && oldDetectedStripLeadingDirs != myDetectedStripLeadingDirs)) { - myDetectedBaseDirectory = null; - myDetectedStripLeadingDirs = -1; - autodetectFailed = true; - } - } - } - if (!success) { - myPatchesFailedToLoad.add(patch); - } - } - } - } - - private boolean detectDirectory(final FilePatch patch) { - if (patch.getBeforeName().equals(patch.getAfterName()) && patch.isNewFile()) { - return false; - } else { - boolean success = detectDirectoryByName(patch.getBeforeName()); - if (! success) { - success = detectDirectoryByName(patch.getAfterName()); - } - return success; - } - } - - private Collection verifyPatchPaths() { - final ApplyPatchContext context = getApplyPatchContext(); - myPatchesFailedToLoad.clear(); - for(FilePatch patch: myPatches) { - try { - if (context.getBaseDir() == null || patch.findFileToPatch(context) == null) { - myPatchesFailedToLoad.add(patch); - } - } - catch (IOException e) { - myPatchesFailedToLoad.add(patch); - } - } - SwingUtilities.invokeLater(new Runnable() { - public void run() { - myPatchContentsList.repaint(); - myStatusLabel.setText(""); - } - }); - return context.getMissingDirectories(); - } - - private boolean detectDirectoryByName(final String patchFileName) { - PatchBaseDirectoryDetector detector = PatchBaseDirectoryDetector.getInstance(myProject); - if (detector == null) return false; - final PatchBaseDirectoryDetector.Result result = detector.detectBaseDirectory(patchFileName); - if (result == null) return false; - myDetectedBaseDirectory = result.baseDir; - myDetectedStripLeadingDirs = result.stripDirs; - return true; - } - - private void queueUpdateStatus(final String s) { - if (!SwingUtilities.isEventDispatchThread()) { - SwingUtilities.invokeLater(new Runnable() { - public void run() { - queueUpdateStatus(s); - } - }); - return; - } - updateStatus(s); - } - - private void updateStatus(String s) { - myInnerChange = true; - try { - if (myDetectedBaseDirectory != null) { - myBaseDirectoryField.setText(myDetectedBaseDirectory); - myDetectedBaseDirectory = null; - } - if (myDetectedStripLeadingDirs != -1) { - myStripLeadingDirectoriesSpinner.setValue(myDetectedStripLeadingDirs); - myDetectedStripLeadingDirs = -1; - } - } - finally { - myInnerChange = false; - } - myLoadPatchError = s; - if (s == null) { - myStatusLabel.setForeground(UIUtil.getLabelForeground()); - myStatusLabel.setText(buildPatchSummary()); - } - else { - myStatusLabel.setText(s); - myStatusLabel.setForeground(Color.red); - } - updatePatchTableModel(); - updateOKAction(); - } - - private void updatePatchTableModel() { - if (myPatches != null) { - myPatchContentsList.setModel(new CollectionListModel(myPatches)); - } - else { - myPatchContentsList.setModel(new DefaultListModel()); - } - myShowDiffButton.setEnabled(myPatches != null && myPatches.size() > 0); - } - - private String buildPatchSummary() { - int newFiles = 0; - int changedFiles = 0; - int deletedFiles = 0; - for(FilePatch patch: myPatches) { - if (patch.isNewFile()) { - newFiles++; - } - else if (patch.isDeletedFile()) { - deletedFiles++; - } - else { - changedFiles++; - } - } - StringBuilder summaryBuilder = new StringBuilder("").append(VcsBundle.message("apply.patch.summary.title")).append(" "); - appendSummary(changedFiles, 0, summaryBuilder, "patch.summary.changed.files"); - appendSummary(newFiles, changedFiles, summaryBuilder, "patch.summary.new.files"); - appendSummary(deletedFiles, changedFiles + newFiles, summaryBuilder, "patch.summary.deleted.files"); - summaryBuilder.append(""); - return summaryBuilder.toString(); - } - - private static void appendSummary(final int count, final int prevCount, final StringBuilder summaryBuilder, - @PropertyKey(resourceBundle = "messages.VcsBundle") final String key) { - if (count > 0) { - if (prevCount > 0) { - summaryBuilder.append(", "); - } - summaryBuilder.append(VcsBundle.message(key, count)); - } - } - - @Override - protected void dispose() { - myLoadPatchAlarm.dispose(); - myVerifyPatchAlarm.dispose(); - super.dispose(); - } - - private void updateOKAction() { - setOKActionEnabled(myFileNameField.getText().length() > 0 && myLoadPatchError == null); - } - - @Override - protected void doOKAction() { - if (myPatches == null) { - myLoadPatchAlarm.cancelAllRequests(); - checkLoadPatches(false); - } - if (myLoadPatchError == null) { - mySelectedChangeList = myChangeListChooser.getSelectedList(myProject); - if (mySelectedChangeList == null) return; - final Collection missingDirs = verifyPatchPaths(); - if (missingDirs.size() > 0 && !checkCreateMissingDirs(missingDirs)) return; - if (getBaseDirectory() == null) { - Messages.showErrorDialog(getContentPane(), "Could not find patch base directory " + myBaseDirectoryField.getText()); - return; - } - super.doOKAction(); - } - } - - private boolean checkCreateMissingDirs(final Collection missingDirs) { - StringBuilder messageBuilder = new StringBuilder(VcsBundle.message("apply.patch.create.dirs.prompt.header")); - for(String missingDir: missingDirs) { - messageBuilder.append(missingDir).append("\r\n"); - } - messageBuilder.append(VcsBundle.message("apply.patch.create.dirs.prompt.footer")); - int rc = Messages.showYesNoCancelDialog(myProject, messageBuilder.toString(), VcsBundle.message("patch.apply.dialog.title"), - Messages.getQuestionIcon()); - if (rc == 0) { - CommandProcessor.getInstance().executeCommand(myProject, new Runnable() { - public void run() { - for(String dir: missingDirs) { - try { - VfsUtil.createDirectories(dir); - } - catch (IOException e) { - Messages.showErrorDialog(myProject, "Error creating directories: " + e.getMessage(), - VcsBundle.message("patch.apply.dialog.title")); - } - } - } - }, "Creating directories for new files in patch", null); - } - else if (rc != 1) { - return false; - } - return true; - } - - @Nullable - protected JComponent createCenterPanel() { - return myRootPanel; - } - - public List getPatches() { - return myPatches; - } - - private VirtualFile getBaseDirectory() { - if (ApplicationManager.getApplication().isDispatchThread()) { - return LocalFileSystem.getInstance().refreshAndFindFileByPath(FileUtil.toSystemIndependentName(myBaseDirectoryField.getText())); - } - return LocalFileSystem.getInstance().findFileByPath(FileUtil.toSystemIndependentName(myBaseDirectoryField.getText())); - } - - private int getStripLeadingDirectories() { - return ((Integer) myStripLeadingDirectoriesSpinner.getValue()).intValue(); - } - - public ApplyPatchContext getApplyPatchContext() { - return new ApplyPatchContext(getBaseDirectory(), getStripLeadingDirectories(), false, false); - } - - public LocalChangeList getSelectedChangeList() { - return mySelectedChangeList; - } - - private static String getChangeType(final FilePatch filePatch) { - if (filePatch.isNewFile()) return VcsBundle.message("change.type.new"); - if (filePatch.isDeletedFile()) return VcsBundle.message("change.type.deleted"); - return VcsBundle.message("change.type.modified"); - } - - protected void doHelpAction() { - HelpManager.getInstance().invokeHelp("reference.dialogs.vcs.patch.apply"); - } - - protected Action[] createActions() { - return new Action[]{ getOKAction(), getCancelAction(), getHelpAction() }; - } - - private void createUIComponents() { - myChangeListChooser = new ChangeListChooserPanel(null, new Consumer() { - public void consume(final String errorMessage) { - setOKActionEnabled(errorMessage == null); - setErrorText(errorMessage); - } - }); - } - - private class PatchCellRendererPanel extends JPanel implements ListCellRenderer { - private final PatchCellRenderer myRenderer; - private final JLabel myFileTypeLabel; - - public PatchCellRendererPanel() { - super(new BorderLayout()); - setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 2)); - myRenderer = new PatchCellRenderer(); - add(myRenderer, BorderLayout.CENTER); - myFileTypeLabel = new JLabel(); - myFileTypeLabel.setHorizontalAlignment(JLabel.RIGHT); - add(myFileTypeLabel, BorderLayout.EAST); - } - - public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { - FilePatch patch = (FilePatch) value; - myRenderer.getListCellRendererComponent(list, value, index, isSelected, false); - myFileTypeLabel.setText("(" + getChangeType(patch) + ")"); - if (isSelected) { - setBackground(UIUtil.getListSelectionBackground()); - setForeground(UIUtil.getListSelectionForeground()); - myFileTypeLabel.setForeground(UIUtil.getListSelectionForeground()); - } - else { - setBackground(UIUtil.getListBackground()); - setForeground(UIUtil.getListForeground()); - myFileTypeLabel.setForeground(Color.gray); - } - return this; - } - } - - private class PatchCellRenderer extends ColoredListCellRenderer { - private final SimpleTextAttributes myNewAttributes = new SimpleTextAttributes(0, FileStatus.ADDED.getColor()); - private final SimpleTextAttributes myDeletedAttributes = new SimpleTextAttributes(0, FileStatus.DELETED.getColor()); - private final SimpleTextAttributes myModifiedAttributes = new SimpleTextAttributes(0, FileStatus.MODIFIED.getColor()); - - private boolean assumeProblemWillBeFixed(final FilePatch filePatch) { - // if some of the files are valid, assume that "red" new files will be fixed by creating directories - if (myPatches == null || myPatchesFailedToLoad == null) return false; - return (filePatch.isNewFile() && myPatchesFailedToLoad.size() != myPatches.size()); - } - - protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) { - FilePatch filePatch = (FilePatch) value; - String name = filePatch.getAfterNameRelative(getStripLeadingDirectories()); - - final FileType fileType = FileTypeManager.getInstance().getFileTypeByFileName(name); - setIcon(fileType.getIcon()); - - if (myPatchesFailedToLoad.contains(filePatch) && !assumeProblemWillBeFixed(filePatch)) { - append(name, SimpleTextAttributes.ERROR_ATTRIBUTES); - } - else if (filePatch.isNewFile()) { - append(name, myNewAttributes); - } - else if (filePatch.isDeletedFile()) { - append(name, myDeletedAttributes); - } - else { - append(name, myModifiedAttributes); - } - - final String afterPath = filePatch.getAfterName(); - final String beforePath = filePatch.getBeforeName(); - - if ((beforePath != null) && (afterPath != null) && (! beforePath.equals(afterPath))) { - final String message = myMoveRenameInfo.get(new Pair(beforePath, afterPath)); - if (message != null) { - append(message, SimpleTextAttributes.REGULAR_ATTRIBUTES); - } - } - } - } -} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDifferentiatedDialog.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDifferentiatedDialog.java index 1a840a924823..d0a1e0f6508a 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDifferentiatedDialog.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDifferentiatedDialog.java @@ -17,10 +17,7 @@ package com.intellij.openapi.vcs.changes.patch; import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.actionSystem.*; -import com.intellij.openapi.diff.impl.patch.FilePatch; -import com.intellij.openapi.diff.impl.patch.PatchReader; -import com.intellij.openapi.diff.impl.patch.PatchSyntaxException; -import com.intellij.openapi.diff.impl.patch.TextFilePatch; +import com.intellij.openapi.diff.impl.patch.*; import com.intellij.openapi.fileChooser.FileChooser; import com.intellij.openapi.fileChooser.FileChooserDescriptor; import com.intellij.openapi.fileChooser.FileChooserDialog; @@ -213,7 +210,7 @@ public class ApplyPatchDifferentiatedDialog extends DialogWrapper { } PatchReader reader; try { - reader = new PatchReader(patchFile); + reader = PatchVirtualFileReader.create(patchFile); } catch (IOException e) { //todo diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchForBaseRevisionTexts.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchForBaseRevisionTexts.java index 9e7722e1135a..d1c5902efc6d 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchForBaseRevisionTexts.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchForBaseRevisionTexts.java @@ -18,13 +18,13 @@ package com.intellij.openapi.vcs.changes.patch; import com.intellij.openapi.diff.impl.patch.ApplyPatchException; import com.intellij.openapi.diff.impl.patch.ApplyPatchStatus; import com.intellij.openapi.diff.impl.patch.TextFilePatch; +import com.intellij.openapi.diff.impl.patch.apply.ApplyFilePatchBase; import com.intellij.openapi.fileEditor.impl.LoadTextUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.Processor; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class ApplyPatchForBaseRevisionTexts { @@ -58,7 +58,7 @@ public class ApplyPatchForBaseRevisionTexts { public boolean process(final CharSequence text) { newText.setLength(0); try { - myStatus = patch.applyModifications(text, newText); + myStatus = ApplyFilePatchBase.applyModifications(patch, text, newText); } catch(ApplyPatchException ex) { return true; // continue to older versions diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/CreatePatchCommitExecutor.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/CreatePatchCommitExecutor.java index 71d2d5312857..c6b80cebd11f 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/CreatePatchCommitExecutor.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/CreatePatchCommitExecutor.java @@ -22,7 +22,7 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.ProjectComponent; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diff.impl.patch.FilePatch; -import com.intellij.openapi.diff.impl.patch.PatchBuilder; +import com.intellij.openapi.diff.impl.patch.TextPatchBuilder; import com.intellij.openapi.diff.impl.patch.UnifiedDiffWriter; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; @@ -153,7 +153,7 @@ public class CreatePatchCommitExecutor implements CommitExecutor, ProjectCompone REVERSE_PATCH = myPanel.isReversePatch(); Writer writer = new OutputStreamWriter(new FileOutputStream(fileName)); try { - List patches = PatchBuilder.buildPatch(changes, myProject.getBaseDir().getPresentableUrl(), false, REVERSE_PATCH); + List patches = TextPatchBuilder.buildPatch(changes, myProject.getBaseDir().getPresentableUrl(), REVERSE_PATCH); final String lineSeparator = CodeStyleFacade.getInstance(myProject).getLineSeparator(); UnifiedDiffWriter.write(patches, writer, lineSeparator); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/LazyPatchContentRevision.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/LazyPatchContentRevision.java index 4d027bfde718..4c1bbe049c16 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/LazyPatchContentRevision.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/LazyPatchContentRevision.java @@ -17,6 +17,7 @@ package com.intellij.openapi.vcs.changes.patch; import com.intellij.openapi.diff.impl.patch.ApplyPatchException; import com.intellij.openapi.diff.impl.patch.TextFilePatch; +import com.intellij.openapi.diff.impl.patch.apply.ApplyFilePatchBase; import com.intellij.openapi.editor.Document; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.vcs.FilePath; @@ -50,7 +51,7 @@ public class LazyPatchContentRevision implements ContentRevision { } final String baseContent = doc.getText(); final StringBuilder newText = new StringBuilder(); - myPatch.applyModifications(baseContent, newText); + ApplyFilePatchBase.applyModifications(myPatch, baseContent, newText); myContent = newText.toString(); } catch (ApplyPatchException e) { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesAction.java index 850be8768999..cfbb0c06b09d 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesAction.java @@ -19,6 +19,7 @@ import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.diff.DiffRequestFactory; import com.intellij.openapi.diff.MergeRequest; import com.intellij.openapi.diff.impl.patch.*; +import com.intellij.openapi.diff.impl.patch.apply.ApplyTextFilePatch; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.MessageType; @@ -77,7 +78,7 @@ public class DiffShelvedChangesAction extends AnAction implements DumbAware { final Change change = shelvedChange.getChange(project); final String beforePath = shelvedChange.getBeforePath(); try { - final VirtualFile f = FilePatch.findPatchTarget(context, beforePath, shelvedChange.getAfterPath(), beforePath == null); + final VirtualFile f = ApplyTextFilePatch.findPatchTarget(context, beforePath, shelvedChange.getAfterPath(), beforePath == null); if ((f == null) || (! f.exists())) { if (beforePath != null) { missing.add(beforePath); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesManager.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesManager.java index 167e4c63e80a..d7c2ac6570b3 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesManager.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesManager.java @@ -27,12 +27,13 @@ import com.intellij.openapi.application.PathManager; import com.intellij.openapi.components.ProjectComponent; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diff.impl.patch.*; +import com.intellij.openapi.diff.impl.patch.apply.ApplyFilePatchBase; import com.intellij.openapi.diff.impl.patch.formove.CustomBinaryPatchApplier; import com.intellij.openapi.diff.impl.patch.formove.PatchApplier; import com.intellij.openapi.options.StreamProvider; -import com.intellij.openapi.progress.ProgressManager; -import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.*; import com.intellij.openapi.util.io.FileUtil; @@ -170,7 +171,7 @@ public class ShelveChangesManager implements ProjectComponent, JDOMExternalizabl if (ind != null && ind.isCanceled()) { throw new ProcessCanceledException(); } - final List patches = PatchBuilder.buildPatch(textChanges, myProject.getBaseDir().getPresentableUrl(), true, false); + final List patches = TextPatchBuilder.buildPatch(textChanges, myProject.getBaseDir().getPresentableUrl(), false); if (ind != null && ind.isCanceled()) { throw new ProcessCanceledException(); } @@ -269,7 +270,7 @@ public class ShelveChangesManager implements ProjectComponent, JDOMExternalizabl } final BinaryPatchApplier binaryPatchApplier = new BinaryPatchApplier(binaryFilesToUnshelve.size()); - final PatchApplier patchApplier = new PatchApplier(myProject, myProject.getBaseDir(), patches, targetChangeList, binaryPatchApplier); + final PatchApplier patchApplier = new PatchApplier(myProject, myProject.getBaseDir(), patches, targetChangeList, binaryPatchApplier); patchApplier.execute(); remainingPatches.addAll(patchApplier.getRemainingPatches()); @@ -299,7 +300,7 @@ public class ShelveChangesManager implements ProjectComponent, JDOMExternalizabl return textFilePatches; } - private class BinaryPatchApplier implements CustomBinaryPatchApplier { + private class BinaryPatchApplier implements CustomBinaryPatchApplier { private final List myAppliedPatches; private BinaryPatchApplier(final int binaryCount) { @@ -307,9 +308,9 @@ public class ShelveChangesManager implements ProjectComponent, JDOMExternalizabl } @NotNull - public ApplyPatchStatus apply(final List> patches) throws IOException { - for (Pair patch : patches) { - final ShelvedBinaryFilePatch shelvedPatch = (ShelvedBinaryFilePatch) patch.getSecond(); + public ApplyPatchStatus apply(final List>> patches) throws IOException { + for (Pair> patch : patches) { + final ShelvedBinaryFilePatch shelvedPatch = patch.getSecond().getPatch(); unshelveBinaryFile(shelvedPatch.getShelvedBinaryFile(), patch.getFirst()); myAppliedPatches.add(shelvedPatch); } @@ -522,7 +523,7 @@ public class ShelveChangesManager implements ProjectComponent, JDOMExternalizabl return reader.readAllPatches(); } - private static class ShelvedBinaryFilePatch extends FilePatch { + public static class ShelvedBinaryFilePatch extends FilePatch { private final ShelvedBinaryFile myShelvedBinaryFile; public ShelvedBinaryFilePatch(final ShelvedBinaryFile shelvedBinaryFile) { @@ -543,11 +544,6 @@ public class ShelveChangesManager implements ProjectComponent, JDOMExternalizabl return pathNameComponents [pathNameComponents.length-1]; } - protected void applyCreate(final VirtualFile newFile) throws IOException, ApplyPatchException { - } - protected ApplyPatchStatus applyChange(final VirtualFile fileToPatch) throws IOException, ApplyPatchException { - return null; - } public boolean isNewFile() { return myShelvedBinaryFile.BEFORE_PATH == null; } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedChange.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedChange.java index 62210f3339a3..94b705d598ee 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedChange.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedChange.java @@ -26,6 +26,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diff.impl.patch.ApplyPatchException; import com.intellij.openapi.diff.impl.patch.PatchSyntaxException; import com.intellij.openapi.diff.impl.patch.TextFilePatch; +import com.intellij.openapi.diff.impl.patch.apply.ApplyFilePatchBase; import com.intellij.openapi.editor.Document; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.project.Project; @@ -203,7 +204,7 @@ public class ShelvedChange { return null; } StringBuilder newText = new StringBuilder(); - patch.applyModifications(getBaseContent(), newText); + ApplyFilePatchBase.applyModifications(patch, getBaseContent(), newText); return newText.toString(); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsGeneralConfigurationPanel.form b/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsGeneralConfigurationPanel.form index 21e3f625ca9f..6167748fc95a 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsGeneralConfigurationPanel.form +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsGeneralConfigurationPanel.form @@ -1,6 +1,6 @@
- + @@ -8,24 +8,15 @@ - - - - - - - - - - + - + @@ -92,42 +83,88 @@ - + - - - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + - + - + - + - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -135,7 +172,7 @@ - + @@ -144,7 +181,7 @@ - + @@ -155,49 +192,22 @@ - + - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + @@ -211,5 +221,10 @@ + + + + + diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsGeneralConfigurationPanel.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsGeneralConfigurationPanel.java index 0ccc62d5fad6..fcfa0d99a237 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsGeneralConfigurationPanel.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsGeneralConfigurationPanel.java @@ -15,7 +15,6 @@ */ package com.intellij.openapi.vcs.configurable; -import com.intellij.openapi.application.ApplicationNamesInfo; import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.project.Project; @@ -60,8 +59,7 @@ public class VcsGeneralConfigurationPanel implements Configurable { private JPanel myAddConfirmationPanel; private JCheckBox myCbOfferToMoveChanges; private JComboBox myFailedCommitChangelistCombo; - private JLabel myRemoveConfirmationLabel; - private JLabel myAddConfirmationLabel; + private ButtonGroup myEmptyChangelistRemovingGroup; public VcsGeneralConfigurationPanel(final Project project) { @@ -93,8 +91,6 @@ public class VcsGeneralConfigurationPanel implements Configurable { myPromptsPanel.setSize(myPromptsPanel.getPreferredSize()); - myAddConfirmationLabel.setText(VcsBundle.message("add.confirmation.label.text", ApplicationNamesInfo.getInstance().getProductName())); - myRemoveConfirmationLabel.setText(VcsBundle.message("remove.confirmation.label.text", ApplicationNamesInfo.getInstance().getProductName())); } public void apply() throws ConfigurationException { @@ -103,6 +99,7 @@ public class VcsGeneralConfigurationPanel implements Configurable { settings.FORCE_NON_EMPTY_COMMENT = myForceNonEmptyComment.isSelected(); settings.OFFER_MOVE_TO_ANOTHER_CHANGELIST_ON_PARTIAL_COMMIT = myCbOfferToMoveChanges.isSelected(); + settings.REMOVE_EMPTY_INACTIVE_CHANGELISTS = getSelected(myEmptyChangelistRemovingGroup); settings.MOVE_TO_FAILED_COMMIT_CHANGELIST = getFailedCommitConfirm(); for (VcsShowOptionsSettingImpl setting : myPromptOptions.keySet()) { @@ -141,6 +138,15 @@ public class VcsGeneralConfigurationPanel implements Configurable { return VcsShowConfirmationOption.Value.DO_NOTHING_SILENTLY; } + private static VcsShowConfirmationOption.Value getSelected(ButtonGroup group) { + switch (UIUtil.getSelectedButton(group)) { + case 0: + return VcsShowConfirmationOption.Value.SHOW_CONFIRMATION; + case 1: + return VcsShowConfirmationOption.Value.DO_ACTION_SILENTLY; + } + return VcsShowConfirmationOption.Value.DO_NOTHING_SILENTLY; + } private ReadonlyStatusHandlerImpl getReadOnlyStatusHandler() { return ((ReadonlyStatusHandlerImpl)ReadonlyStatusHandler.getInstance(myProject)); @@ -155,6 +161,9 @@ public class VcsGeneralConfigurationPanel implements Configurable { if (settings.OFFER_MOVE_TO_ANOTHER_CHANGELIST_ON_PARTIAL_COMMIT != myCbOfferToMoveChanges.isSelected()){ return true; } + if (settings.REMOVE_EMPTY_INACTIVE_CHANGELISTS != getSelected(myEmptyChangelistRemovingGroup)){ + return true; + } if (!Comparing.equal(getFailedCommitConfirm(), settings.MOVE_TO_FAILED_COMMIT_CHANGELIST)) { return true; @@ -178,6 +187,8 @@ public class VcsGeneralConfigurationPanel implements Configurable { VcsConfiguration settings = VcsConfiguration.getInstance(myProject); myForceNonEmptyComment.setSelected(settings.FORCE_NON_EMPTY_COMMENT); myCbOfferToMoveChanges.setSelected(settings.OFFER_MOVE_TO_ANOTHER_CHANGELIST_ON_PARTIAL_COMMIT); + int id = settings.REMOVE_EMPTY_INACTIVE_CHANGELISTS.getId(); + UIUtil.setSelectedButton(myEmptyChangelistRemovingGroup, id == 0 ? 0 : id == 1 ? 2 : 1); myShowReadOnlyStatusDialog.setSelected(getReadOnlyStatusHandler().getState().SHOW_DIALOG); if (settings.MOVE_TO_FAILED_COMMIT_CHANGELIST == VcsShowConfirmationOption.Value.DO_ACTION_SILENTLY) { myFailedCommitChangelistCombo.setSelectedIndex(0); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/update/RefreshVFsSynchronously.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/update/RefreshVFsSynchronously.java index 8852e9acf99f..74465ff77ed1 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/update/RefreshVFsSynchronously.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/update/RefreshVFsSynchronously.java @@ -203,9 +203,13 @@ public class RefreshVFsSynchronously { ApplicationManager.getApplication().invokeLater(new Runnable() { public void run() { - // common lock for all refreshes inside - runnable.run(); - semaphore.up(); + try { + // common lock for all refreshes inside + runnable.run(); + } + finally { + semaphore.up(); + } } }); semaphore.waitFor(); diff --git a/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties b/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties index adb7cb3c2d11..c0802ef69c12 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties +++ b/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties @@ -1757,3 +1757,5 @@ unnecessary.javadoc.link.problem.descriptor=#ref is unnecessary unnecessary.javadoc.link.quickfix=Remove unnecessary {0} thread.local.not.static.final.display.name=ThreadLocal field not declared static final thread.local.not.static.final.problem.descriptor=ThreadLocal #ref is not declared 'static final' +remove.try.finally.block.quickfix=Remove try-finally block +remove.finally.block.quickfix=Remove finally block diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/errorhandling/EmptyFinallyBlockInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/errorhandling/EmptyFinallyBlockInspection.java index a164460895d9..dca798e9d154 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/errorhandling/EmptyFinallyBlockInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/errorhandling/EmptyFinallyBlockInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2007 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2010 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,30 +15,120 @@ */ package com.siyeh.ig.errorhandling; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.project.Project; import com.intellij.psi.*; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.IncorrectOperationException; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.InspectionGadgetsFix; import org.jetbrains.annotations.NotNull; public class EmptyFinallyBlockInspection extends BaseInspection { + @Override @NotNull public String getDisplayName() { return InspectionGadgetsBundle.message( "empty.finally.block.display.name"); } + @Override public boolean isEnabledByDefault() { return true; } + @Override @NotNull protected String buildErrorString(Object... infos) { return InspectionGadgetsBundle.message( "empty.finally.block.problem.descriptor"); } + @Override + protected InspectionGadgetsFix buildFix(Object... infos) { + final Integer count = (Integer)infos[0]; + if (count.intValue() == 0) { + return new RemoveTryFinallyBlockFix(); + } else { + return new RemoveFinallyBlockFix(); + } + } + + private static class RemoveTryFinallyBlockFix extends InspectionGadgetsFix { + + @NotNull + public String getName() { + return InspectionGadgetsBundle.message( + "remove.try.finally.block.quickfix"); + } + + @Override + protected void doFix(Project project, ProblemDescriptor descriptor) + throws IncorrectOperationException { + final PsiElement element = descriptor.getPsiElement(); + final PsiTryStatement tryStatement = + PsiTreeUtil.getParentOfType(element, PsiTryStatement.class); + if (tryStatement == null) { + return; + } + final PsiCodeBlock tryBlock = tryStatement.getTryBlock(); + if (tryBlock == null) { + return; + } + final PsiStatement[] statements = tryBlock.getStatements(); + final PsiElement parent = tryStatement.getParent(); + for (PsiStatement statement : statements) { + parent.addBefore(statement, tryStatement); + } + tryStatement.delete(); + } + } + + private static class RemoveFinallyBlockFix extends InspectionGadgetsFix { + + @NotNull + public String getName() { + return InspectionGadgetsBundle.message( + "remove.finally.block.quickfix"); + } + + @Override + protected void doFix(Project project, ProblemDescriptor descriptor) + throws IncorrectOperationException { + final PsiElement element = descriptor.getPsiElement(); + final PsiTryStatement tryStatement = + PsiTreeUtil.getParentOfType(element, PsiTryStatement.class); + if (tryStatement == null) { + return; + } + final PsiCodeBlock finallyBlock = tryStatement.getFinallyBlock(); + if (finallyBlock == null) { + return; + } + deleteUntilFinally(finallyBlock); + } + + private static void deleteUntilFinally(PsiElement element) { + if (element instanceof PsiJavaToken) { + final PsiJavaToken keyword = (PsiJavaToken)element; + final IElementType tokenType = keyword.getTokenType(); + if (tokenType.equals(JavaTokenType.FINALLY_KEYWORD)) { + keyword.delete(); + return; + } + } + deleteUntilFinally(element.getPrevSibling()); + if (!(element instanceof PsiWhiteSpace)) { + element.delete(); + } + } + } + + @Override public BaseInspectionVisitor buildVisitor() { return new EmptyFinallyBlockVisitor(); } @@ -46,7 +136,8 @@ public class EmptyFinallyBlockInspection extends BaseInspection { private static class EmptyFinallyBlockVisitor extends BaseInspectionVisitor { - @Override public void visitTryStatement(@NotNull PsiTryStatement statement) { + @Override public void visitTryStatement( + @NotNull PsiTryStatement statement) { super.visitTryStatement(statement); if (JspPsiUtil.isInJspFile(statement.getContainingFile())) { return; @@ -58,11 +149,12 @@ public class EmptyFinallyBlockInspection extends BaseInspection { if (finallyBlock.getStatements().length != 0) { return; } + final PsiCodeBlock[] catchBlocks = statement.getCatchBlocks(); final PsiElement[] children = statement.getChildren(); for (final PsiElement child : children) { final String childText = child.getText(); if (PsiKeyword.FINALLY.equals(childText)) { - registerError(child); + registerError(child, Integer.valueOf(catchBlocks.length)); return; } } diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/initialization/InstanceVariableUninitializedUseInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/initialization/InstanceVariableUninitializedUseInspection.java index 5e35a7a2320a..21866a871368 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/initialization/InstanceVariableUninitializedUseInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/initialization/InstanceVariableUninitializedUseInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2007 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2010 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,28 +34,33 @@ public class InstanceVariableUninitializedUseInspection /** @noinspection PublicField*/ public boolean m_ignorePrimitives = false; + @Override @NotNull public String getID() { return "InstanceVariableUsedBeforeInitialized"; } + @Override @NotNull public String getDisplayName() { return InspectionGadgetsBundle.message( "instance.variable.used.before.initialized.display.name"); } + @Override @NotNull public String buildErrorString(Object... infos) { return InspectionGadgetsBundle.message( "instance.variable.used.before.initialized.problem.descriptor"); } + @Override public JComponent createOptionsPanel() { return new SingleCheckboxOptionsPanel(InspectionGadgetsBundle.message( "primitive.fields.ignore.option"), this, "m_ignorePrimitives"); } + @Override public BaseInspectionVisitor buildVisitor() { return new InstanceVariableInitializationVisitor(); } @@ -80,8 +85,11 @@ public class InstanceVariableUninitializedUseInspection if (aClass == null) { return; } - for(ImplicitUsageProvider provider: Extensions.getExtensions(ImplicitUsageProvider.EP_NAME)) { - if (provider.isImplicitWrite(field)) return; + for(ImplicitUsageProvider provider: + Extensions.getExtensions(ImplicitUsageProvider.EP_NAME)) { + if (provider.isImplicitWrite(field)) { + return; + } } final UninitializedReadCollector uninitializedReadsCollector = new UninitializedReadCollector(); diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/UninitializedReadCollector.java b/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/UninitializedReadCollector.java index 8ffd3b05d8ff..8b4ff4a3a08b 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/UninitializedReadCollector.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/UninitializedReadCollector.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2008 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2010 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -168,6 +168,8 @@ public class UninitializedReadCollector { (PsiSwitchStatement) statement; return switchStatementAssignsVariable(switchStatement, variable, stamp, checkedMethods); + } else if(statement instanceof PsiSwitchLabelStatement){ + return false; } else{ throw new RuntimeException("unknown statement: " + statement); // unknown statement type @@ -577,7 +579,7 @@ public class UninitializedReadCollector { // Can remark out this block to continue chase outside of of // current class - if(!calledClass.equals(containingClass)){ + if(calledClass == null || !calledClass.equals(containingClass)){ return false; } diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/java/LanguageReference.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/java/LanguageReference.java index 91c903b30694..65b760e0d65a 100644 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/java/LanguageReference.java +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/java/LanguageReference.java @@ -26,6 +26,7 @@ import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.EmptyIcon; import org.intellij.plugins.intelliLang.inject.InjectedLanguage; import org.intellij.plugins.intelliLang.util.StringLiteralReference; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** @@ -47,6 +48,7 @@ final class LanguageReference extends StringLiteralReference { return false; } + @NotNull public Object[] getVariants() { final String[] ids = InjectedLanguage.getAvailableLanguageIDs(); return ContainerUtil.map2Array(ids, LookupElement.class, new Function() { diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/java/RegExpEnumReference.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/java/RegExpEnumReference.java index 38c5550c8f51..f2c43283e887 100644 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/java/RegExpEnumReference.java +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/java/RegExpEnumReference.java @@ -42,6 +42,7 @@ final class RegExpEnumReference extends StringLiteralReference { myPattern = pattern; } + @NotNull public Object[] getVariants() { final Set values = getEnumValues(); if (values == null || values.size() == 0) { @@ -68,4 +69,4 @@ final class RegExpEnumReference extends StringLiteralReference { private Set getEnumValues() { return RegExpUtil.getEnumValues(myValue.getProject(), myPattern); } -} \ No newline at end of file +} diff --git a/plugins/ant/src/com/intellij/lang/ant/psi/impl/reference/AntAttributeReference.java b/plugins/ant/src/com/intellij/lang/ant/psi/impl/reference/AntAttributeReference.java index 578ef2bea5ef..22020a7de486 100644 --- a/plugins/ant/src/com/intellij/lang/ant/psi/impl/reference/AntAttributeReference.java +++ b/plugins/ant/src/com/intellij/lang/ant/psi/impl/reference/AntAttributeReference.java @@ -51,6 +51,7 @@ public class AntAttributeReference extends AntGenericReference { return null; } + @NotNull public Object[] getVariants() { final AntTypeDefinition def = getElement().getTypeDefinition(); return (def == null) ? EMPTY_ARRAY : def.getAttributes(); diff --git a/plugins/ant/src/com/intellij/lang/ant/psi/impl/reference/AntElementNameReference.java b/plugins/ant/src/com/intellij/lang/ant/psi/impl/reference/AntElementNameReference.java index c20fad203729..b2386f52706d 100644 --- a/plugins/ant/src/com/intellij/lang/ant/psi/impl/reference/AntElementNameReference.java +++ b/plugins/ant/src/com/intellij/lang/ant/psi/impl/reference/AntElementNameReference.java @@ -117,6 +117,7 @@ public class AntElementNameReference extends AntGenericReference { return true; } + @NotNull public Object[] getVariants() { if (getAttribute() != null) { return EMPTY_ARRAY; // scriptdef or mactodef params will be handled by XML implementation diff --git a/plugins/ant/src/com/intellij/lang/ant/psi/impl/reference/AntEntityReference.java b/plugins/ant/src/com/intellij/lang/ant/psi/impl/reference/AntEntityReference.java index 3587b5e64e51..2817c3ba8835 100644 --- a/plugins/ant/src/com/intellij/lang/ant/psi/impl/reference/AntEntityReference.java +++ b/plugins/ant/src/com/intellij/lang/ant/psi/impl/reference/AntEntityReference.java @@ -62,6 +62,7 @@ public class AntEntityReference implements PsiReference { return myXmlRef.isReferenceTo(element); } + @NotNull public Object[] getVariants() { return EMPTY_ARRAY; } diff --git a/plugins/ant/src/com/intellij/lang/ant/psi/impl/reference/AntMacroDefParameterReference.java b/plugins/ant/src/com/intellij/lang/ant/psi/impl/reference/AntMacroDefParameterReference.java index e9875c15a2ea..d20fb6294d12 100644 --- a/plugins/ant/src/com/intellij/lang/ant/psi/impl/reference/AntMacroDefParameterReference.java +++ b/plugins/ant/src/com/intellij/lang/ant/psi/impl/reference/AntMacroDefParameterReference.java @@ -97,6 +97,7 @@ public class AntMacroDefParameterReference extends AntGenericReference { return null; } + @NotNull @SuppressWarnings({"HardCodedStringLiteral"}) public Object[] getVariants() { final Set variants = StringSetSpinAllocator.alloc(); diff --git a/plugins/ant/src/com/intellij/lang/ant/psi/impl/reference/AntPropertyReference.java b/plugins/ant/src/com/intellij/lang/ant/psi/impl/reference/AntPropertyReference.java index b3427d0320ab..8368aa6954d3 100644 --- a/plugins/ant/src/com/intellij/lang/ant/psi/impl/reference/AntPropertyReference.java +++ b/plugins/ant/src/com/intellij/lang/ant/psi/impl/reference/AntPropertyReference.java @@ -119,6 +119,7 @@ public class AntPropertyReference extends AntGenericReference { return AntBundle.message("unknown.property", getCanonicalText()); } + @NotNull public Object[] getVariants() { final AntElement currentElement = getElement(); final Set variants = PsiElementSetSpinAllocator.alloc(); diff --git a/plugins/ant/src/com/intellij/lang/ant/psi/impl/reference/AntRefIdReference.java b/plugins/ant/src/com/intellij/lang/ant/psi/impl/reference/AntRefIdReference.java index 64981f1b7404..ba26c6183476 100644 --- a/plugins/ant/src/com/intellij/lang/ant/psi/impl/reference/AntRefIdReference.java +++ b/plugins/ant/src/com/intellij/lang/ant/psi/impl/reference/AntRefIdReference.java @@ -77,6 +77,7 @@ public class AntRefIdReference extends AntGenericReference { } } + @NotNull public Object[] getVariants() { final Set variants = PsiElementSetSpinAllocator.alloc(); try { diff --git a/plugins/ant/src/com/intellij/lang/ant/psi/impl/reference/AntTargetReference.java b/plugins/ant/src/com/intellij/lang/ant/psi/impl/reference/AntTargetReference.java index ffe35c35979f..4eb610c7a65f 100644 --- a/plugins/ant/src/com/intellij/lang/ant/psi/impl/reference/AntTargetReference.java +++ b/plugins/ant/src/com/intellij/lang/ant/psi/impl/reference/AntTargetReference.java @@ -153,6 +153,7 @@ public class AntTargetReference extends AntGenericReference { } } + @NotNull public Object[] getVariants() { final AntElement element = getElement(); if (element instanceof AntAntImpl) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/groovydoc/psi/impl/GrDocParameterReferenceImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/groovydoc/psi/impl/GrDocParameterReferenceImpl.java index 3c404ff89079..7d9fecffa82a 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/groovydoc/psi/impl/GrDocParameterReferenceImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/groovydoc/psi/impl/GrDocParameterReferenceImpl.java @@ -131,6 +131,7 @@ public class GrDocParameterReferenceImpl extends GroovyDocPsiElementImpl impleme return getManager().areElementsEquivalent(element, resolve()); } + @NotNull public Object[] getVariants() { final PsiElement owner = GrDocCommentUtil.findDocOwner(this); final PsiElement firstChild = getFirstChild(); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/GrListOrMapImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/GrListOrMapImpl.java index 93fb11d11c60..969465c6aab1 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/GrListOrMapImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/GrListOrMapImpl.java @@ -24,7 +24,6 @@ import com.intellij.util.Function; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; -import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mCOMMA; import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes; import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor; import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap; @@ -40,6 +39,8 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.GrExpre import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; +import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mCOMMA; + /** * @author ilyas */ @@ -89,6 +90,7 @@ public class GrListOrMapImpl extends GrExpressionImpl implements GrListOrMap { } private static class MyTypesCalculator implements Function { + @Nullable public PsiType fun(GrListOrMapImpl listOrMap) { final GlobalSearchScope scope = listOrMap.getResolveScope(); if (listOrMap.isMap()) { @@ -109,7 +111,10 @@ public class GrListOrMapImpl extends GrExpressionImpl implements GrListOrMap { @Nullable private static PsiClassType inferMapInitializerType(GrListOrMapImpl listOrMap, JavaPsiFacade facade, GlobalSearchScope scope) { - PsiClass mapClass = facade.findClass("java.util.Map", scope); + PsiClass mapClass = facade.findClass("java.util.LinkedHashMap", scope); + if (mapClass == null) { + mapClass = facade.findClass(CommonClassNames.JAVA_UTIL_MAP, scope); + } PsiElementFactory factory = facade.getElementFactory(); if (mapClass != null) { PsiTypeParameter[] typeParameters = mapClass.getTypeParameters(); @@ -146,15 +151,20 @@ public class GrListOrMapImpl extends GrExpressionImpl implements GrListOrMap { if (labels.length == 0) return null; PsiType result = null; PsiManager manager = labels[0].getManager(); + final PsiElementFactory factory = JavaPsiFacade.getElementFactory(labels[0].getProject()); for (GrArgumentLabel label : labels) { PsiElement el = label.getNameElement(); - final PsiType other; + PsiType other; if (el instanceof GrParenthesizedExpression) { other = ((GrParenthesizedExpression)el).getType(); } else { - if (el.getNode() != null) { - other = TypesUtil.getPsiType(el, el.getNode().getElementType()); + final ASTNode node = el.getNode(); + if (node != null) { + other = TypesUtil.getPsiType(el, node.getElementType()); + if (other == null) { + other = factory.createTypeByFQClassName(CommonClassNames.JAVA_LANG_STRING, el.getResolveScope()); + } } else { other = null; @@ -186,6 +196,7 @@ public class GrListOrMapImpl extends GrExpressionImpl implements GrListOrMap { return result; } + @Nullable private static PsiType getLeastUpperBound(PsiType result, PsiType other, PsiManager manager) { if (other == null) return result; if (result == null) result = other; diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/annotation/GrAnnotationNameValuePairImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/annotation/GrAnnotationNameValuePairImpl.java index 291a41b6201c..39b33c697d3c 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/annotation/GrAnnotationNameValuePairImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/annotation/GrAnnotationNameValuePairImpl.java @@ -145,6 +145,7 @@ public class GrAnnotationNameValuePairImpl extends GroovyPsiElementImpl implemen return element instanceof PsiMethod && getManager().areElementsEquivalent(element, resolve()); } + @NotNull public Object[] getVariants() { GrAnnotation anno = getAnnotation(); if (anno != null) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrConstructorInvocationImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrConstructorInvocationImpl.java index 63a2d15ef922..363e43c92a6f 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrConstructorInvocationImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrConstructorInvocationImpl.java @@ -160,6 +160,7 @@ public class GrConstructorInvocationImpl extends GroovyPsiElementImpl implements } + @NotNull public Object[] getVariants() { return ArrayUtil.EMPTY_OBJECT_ARRAY; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java index 36b6584c70ed..3010d46677e4 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java @@ -222,7 +222,7 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl implements if (getParent() instanceof GrReferenceExpression) { result = facade.getElementFactory().createType((PsiClass) resolved); } else { - PsiClass javaLangClass = facade.findClass("java.lang.Class", getResolveScope()); + PsiClass javaLangClass = facade.findClass(CommonClassNames.JAVA_LANG_CLASS, getResolveScope()); if (javaLangClass != null) { PsiSubstitutor substitutor = PsiSubstitutor.EMPTY; final PsiTypeParameter[] typeParameters = javaLangClass.getTypeParameters(); @@ -239,14 +239,14 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl implements } else if (resolved instanceof PsiMethod && !GroovyPsiManager.isTypeBeingInferred(resolved)) { if (dotType == GroovyTokenTypes.mMEMBER_POINTER) { - return facade.getElementFactory().createTypeByFQClassName("groovy.lang.Closure", getResolveScope()); + return facade.getElementFactory().createTypeByFQClassName(GrClosableBlock.GROOVY_LANG_CLOSURE, getResolveScope()); } PsiMethod method = (PsiMethod) resolved; if (PropertyUtil.isSimplePropertySetter(method) && !method.getName().equals(getReferenceName())) { result = method.getParameterList().getParameters()[0].getType(); } else { PsiClass containingClass = method.getContainingClass(); - if (containingClass != null && "java.lang.Object".equals(containingClass.getQualifiedName()) && + if (containingClass != null && CommonClassNames.JAVA_LANG_OBJECT.equals(containingClass.getQualifiedName()) && "getClass".equals(method.getName())) { result = getTypeForObjectGetClass(facade, method); } else { @@ -283,7 +283,7 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl implements PsiClassType.ClassResolveResult qResult = ((PsiClassType) qType).resolveGenerics(); PsiClass clazz = qResult.getElement(); if (clazz != null) { - PsiClass mapClass = facade.findClass("java.util.Map", getResolveScope()); + PsiClass mapClass = facade.findClass(CommonClassNames.JAVA_UTIL_MAP, getResolveScope()); if (mapClass != null && mapClass.getTypeParameters().length == 2) { PsiSubstitutor substitutor = TypeConversionUtil.getClassSubstitutor(mapClass, clazz, qResult.getSubstitutor()); if (substitutor != null) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrIndexPropertyImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrIndexPropertyImpl.java index 3e8f0cfc9272..730d93d2071f 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrIndexPropertyImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrIndexPropertyImpl.java @@ -17,16 +17,16 @@ package org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.path; import com.intellij.lang.ASTNode; -import com.intellij.psi.CommonClassNames; -import com.intellij.psi.PsiArrayType; -import com.intellij.psi.PsiType; +import com.intellij.psi.*; import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PsiUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor; +import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrIndexProperty; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrGdkMethod; import org.jetbrains.plugins.groovy.lang.psi.impl.GrTupleType; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.GrExpressionImpl; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; @@ -74,9 +74,21 @@ public class GrIndexPropertyImpl extends GrExpressionImpl implements GrIndexProp argTypes[i] = argType; } - final PsiType overloadedOperatorType = TypesUtil.getOverloadedOperatorType(thisType, "getAt", this, argTypes); - if (overloadedOperatorType!=null) { - return overloadedOperatorType; + if (thisType instanceof GrTupleType) { + PsiType[] types = ((GrTupleType)thisType).getParameters(); + return types.length == 1 ? types[0] : null; + } + + PsiType overloadedOperatorType = null; + final GroovyResolveResult[] candidates = TypesUtil.getOverloadedOperatorCandidates(thisType, "getAt", this, argTypes); + if (candidates.length == 1) { + final PsiElement element = candidates[0].getElement(); + if (element instanceof PsiMethod) { + overloadedOperatorType = candidates[0].getSubstitutor().substitute(((PsiMethod)element).getReturnType()); + if (overloadedOperatorType != null && !(element instanceof GrGdkMethod)) { //gdk 'getAt' methods don't have information about type parameters + return overloadedOperatorType; + } + } } if (thisType instanceof PsiArrayType) { @@ -84,11 +96,6 @@ public class GrIndexPropertyImpl extends GrExpressionImpl implements GrIndexProp return TypesUtil.boxPrimitiveType(componentType, getManager(), getResolveScope()); } - if (thisType instanceof GrTupleType) { - PsiType[] types = ((GrTupleType)thisType).getParameters(); - return types.length == 1 ? types[0] : null; - } - if (InheritanceUtil.isInheritor(thisType, CommonClassNames.JAVA_UTIL_LIST)) { PsiType iterType = PsiUtil.extractIterableTypeParameter(thisType, true); if (iterType != null) return iterType; @@ -97,6 +104,8 @@ public class GrIndexPropertyImpl extends GrExpressionImpl implements GrIndexProp if (InheritanceUtil.isInheritor(thisType, CommonClassNames.JAVA_UTIL_MAP)) { return PsiUtil.substituteTypeParameter(thisType, CommonClassNames.JAVA_UTIL_MAP, 1, true); } + + return overloadedOperatorType; } } return null; diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.java b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.java index f38280c2924c..2e18835f8380 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.java +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.java @@ -222,4 +222,8 @@ public class GroovyHighlightingTest extends LightCodeInsightFixtureTestCase { } public void testSuperConstructorInvocation() throws Exception {doTest();} + + public void testIndexPropertyAccess() throws Exception { + doTest(); + } } \ No newline at end of file diff --git a/plugins/groovy/testdata/highlighting/IndexPropertyAccess.groovy b/plugins/groovy/testdata/highlighting/IndexPropertyAccess.groovy new file mode 100644 index 000000000000..1c106193ff0b --- /dev/null +++ b/plugins/groovy/testdata/highlighting/IndexPropertyAccess.groovy @@ -0,0 +1,8 @@ +class X extends HashMap { + def getAt(def k) { + return "string" + } +} + +def x = new X(); +print x[2].concat("a") \ No newline at end of file diff --git a/plugins/junit_rt/src/com/intellij/junit4/JUnit4TestRunnerUtil.java b/plugins/junit_rt/src/com/intellij/junit4/JUnit4TestRunnerUtil.java index b75212516c5f..cb94f048ac6f 100644 --- a/plugins/junit_rt/src/com/intellij/junit4/JUnit4TestRunnerUtil.java +++ b/plugins/junit_rt/src/com/intellij/junit4/JUnit4TestRunnerUtil.java @@ -25,7 +25,6 @@ import org.junit.runner.manipulation.Filter; import org.junit.runner.notification.RunNotifier; import org.junit.runners.BlockJUnit4ClassRunner; import org.junit.runners.model.FrameworkMethod; -import org.junit.runners.model.InitializationError; import java.io.BufferedReader; import java.io.FileReader; @@ -114,18 +113,24 @@ public class JUnit4TestRunnerUtil { else { int index = suiteClassName.indexOf(','); if (index != -1) { - final Class clazz = loadTestClass(suiteClassName.substring(0, index)); - final Request classRequest = new ClassRequest(clazz){ - public Runner getRunner() { - try { - return new IgnoreIgnoredTestJUnit4ClassRunner(clazz); + try { + Class.forName("org.junit.runners.model.InitializationError"); + final Class clazz = loadTestClass(suiteClassName.substring(0, index)); + final Request classRequest = new ClassRequest(clazz) { + public Runner getRunner() { + try { + return new IgnoreIgnoredTestJUnit4ClassRunner(clazz); + } + catch (Exception ignored) { + } + return super.getRunner(); } - catch (InitializationError ignored) { - } - return super.getRunner(); - } - }; - return classRequest.filterWith(Description.createTestDescription(clazz, suiteClassName.substring(index + 1))); + }; + return classRequest.filterWith(Description.createTestDescription(clazz, suiteClassName.substring(index + 1))); + } + catch (ClassNotFoundException e) { + return Request.method(loadTestClass(suiteClassName.substring(0, index)), suiteClassName.substring(index + 1)); + } } appendTestClass(result, suiteClassName); } @@ -174,7 +179,7 @@ public class JUnit4TestRunnerUtil { private static class IgnoreIgnoredTestJUnit4ClassRunner extends BlockJUnit4ClassRunner { - public IgnoreIgnoredTestJUnit4ClassRunner(Class clazz) throws InitializationError { + public IgnoreIgnoredTestJUnit4ClassRunner(Class clazz) throws Exception { super(clazz); } diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/MavenDomUtil.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/MavenDomUtil.java index fa84377f670e..a9ebd1a1e336 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/MavenDomUtil.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/MavenDomUtil.java @@ -41,6 +41,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.maven.dom.model.*; import org.jetbrains.idea.maven.project.*; import org.jetbrains.idea.maven.utils.MavenConstants; +import org.jetbrains.idea.maven.utils.MavenLog; import org.jetbrains.idea.maven.vfs.MavenPropertiesVirtualFileSystem; import java.io.File; @@ -87,7 +88,11 @@ public class MavenDomUtil { } public static String calcRelativePath(VirtualFile parent, VirtualFile child) { - String result = FileUtil.getRelativePath(new File(parent.getPath()), new File(child.getPath())); + String result = FileUtil.getRelativePath(parent.getPath(), child.getPath(), '/'); + if (result == null) { + MavenLog.LOG.warn("cannot calculate relative path for\nparent: " + parent + "\nchild: " + child); + result = child.getPath(); + } return FileUtil.toSystemIndependentName(result); } diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/references/MavenModulePsiReference.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/references/MavenModulePsiReference.java index a475a6ca0885..48fde73e1ec1 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/references/MavenModulePsiReference.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/references/MavenModulePsiReference.java @@ -56,6 +56,7 @@ public class MavenModulePsiReference extends MavenPsiReference implements LocalQ return getPsiFile(file); } + @NotNull public Object[] getVariants() { List> files = MavenDomUtil.collectProjectModels(getProject()); diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/references/MavenPropertyPsiReference.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/references/MavenPropertyPsiReference.java index 37ebc09c5559..9921ceb0cc14 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/references/MavenPropertyPsiReference.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/references/MavenPropertyPsiReference.java @@ -50,10 +50,16 @@ import org.jetbrains.idea.maven.vfs.MavenPropertiesVirtualFileSystem; import javax.swing.*; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Set; public class MavenPropertyPsiReference extends MavenPsiReference { + private static final Set BASEDIR_PROPS = + new THashSet(Arrays.asList("basedir", "project.basedir", "pom.basedir", "baseUri", "project.baseUri", "pom.baseUri")); + + private static final String TIMESTAMP_PROP = "maven.build.timestamp"; + protected final MavenDomProjectModel myProjectDom; protected final MavenProject myMavenProject; private final boolean mySoft; @@ -95,10 +101,14 @@ public class MavenPropertyPsiReference extends MavenPsiReference { return resolveEnvPropety(); } - if (myText.equals("basedir") || myText.equals("project.basedir") || myText.equals("pom.basedir")) { + if (BASEDIR_PROPS.contains(myText)) { return resolveBasedir(); } + if (myText.equals(TIMESTAMP_PROP)) { + return myElement; + } + PsiElement result = resolveSystemPropety(); if (result != null) return result; @@ -204,7 +214,7 @@ public class MavenPropertyPsiReference extends MavenPsiReference { } protected void collectVariants(List result) { - collectBasedirVariants(result); + collectStandardVariants(result); collectProjectSchemaVariants(result); collectSettingsXmlSchemaVariants(result); collectPropertiesVariants(result); @@ -212,11 +222,12 @@ public class MavenPropertyPsiReference extends MavenPsiReference { collectSystemEnvProperties(MavenPropertiesVirtualFileSystem.ENV_PROPERTIES_FILE, "env.", result); } - private void collectBasedirVariants(List result) { + private void collectStandardVariants(List result) { PsiDirectory basedir = getBaseDir(); - result.add(createLookupElement(basedir, "basedir", MavenIcons.MAVEN_ICON)); - result.add(createLookupElement(basedir, "pom.basedir", MavenIcons.MAVEN_ICON)); - result.add(createLookupElement(basedir, "project.basedir", MavenIcons.MAVEN_ICON)); + for (String each : BASEDIR_PROPS) { + result.add(createLookupElement(basedir, each, MavenIcons.MAVEN_ICON)); + } + result.add(createLookupElement(myElement, TIMESTAMP_PROP, MavenIcons.MAVEN_ICON)); } private void collectProjectSchemaVariants(final List result) { @@ -245,7 +256,7 @@ public class MavenPropertyPsiReference extends MavenPsiReference { public LookupElement fun(XmlTag xmlTag) { return createLookupElement(xmlTag, xmlTag.getName()); } - })) ; + })); } private void collectSystemEnvProperties(String propertiesFileName, String prefix, List result) { @@ -276,7 +287,7 @@ public class MavenPropertyPsiReference extends MavenPsiReference { VirtualFile file = MavenSchemaProvider.getSchemaFile(schema); PsiFile psiFile = PsiManager.getInstance(myProject).findFile(file); if (!(psiFile instanceof XmlFile)) return null; - + XmlFile xmlFile = (XmlFile)psiFile; XmlDocument document = xmlFile.getDocument(); XmlNSDescriptor desc = (XmlNSDescriptor)document.getMetaData(); @@ -285,9 +296,9 @@ public class MavenPropertyPsiReference extends MavenPsiReference { } private static T doProcessSchema(XmlElementDescriptor[] descriptors, - String prefix, - SchemaProcessor processor, - Set recursionGuard) { + String prefix, + SchemaProcessor processor, + Set recursionGuard) { for (XmlElementDescriptor each : descriptors) { if (isCollection(each)) continue; diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsTree.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsTree.java index 50f9d6a4258b..6c63d266c203 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsTree.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsTree.java @@ -669,7 +669,7 @@ public class MavenProjectsTree { } private long getFileTimestamp(VirtualFile file) { - if (file == null) return -1; + if (file == null || !file.isValid()) return -1; return file.getTimeStamp(); } diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenGotoSettingsFileContibutor.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenGotoSettingsFileContibutor.java index 8f85811c785e..6ae9412af293 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenGotoSettingsFileContibutor.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenGotoSettingsFileContibutor.java @@ -32,6 +32,8 @@ import java.util.Set; public class MavenGotoSettingsFileContibutor implements ChooseByNameContributor, DumbAware { public String[] getNames(Project project, boolean includeNonProjectItems) { + if (!includeNonProjectItems) return ArrayUtil.EMPTY_STRING_ARRAY; + Set result = new THashSet(); for (VirtualFile each : getSettingsFiles(project)) { result.add(each.getName()); @@ -40,6 +42,8 @@ public class MavenGotoSettingsFileContibutor implements ChooseByNameContributor, } public NavigationItem[] getItemsByName(String name, String pattern, Project project, boolean includeNonProjectItems) { + if (!includeNonProjectItems) return NavigationItem.EMPTY_NAVIGATION_ITEM_ARRAY; + List result = new ArrayList(); for (VirtualFile each : getSettingsFiles(project)) { if (each.getName().equals(name)) { diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenUtil.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenUtil.java index 6001367c0b9e..48aa53d48e94 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenUtil.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenUtil.java @@ -19,7 +19,6 @@ import com.intellij.codeInsight.template.TemplateManager; import com.intellij.codeInsight.template.impl.TemplateImpl; import com.intellij.ide.fileTemplates.FileTemplate; import com.intellij.ide.fileTemplates.FileTemplateManager; -import com.intellij.ide.startup.StartupManagerEx; import com.intellij.notification.Notification; import com.intellij.notification.NotificationType; import com.intellij.notification.Notifications; @@ -138,7 +137,7 @@ public class MavenUtil { return; } - if (!StartupManagerEx.getInstanceEx(project).postStartupActivityPassed()) { + if (!project.isInitialized()) { StartupManager.getInstance(project).registerPostStartupActivity(r); return; } diff --git a/plugins/maven/src/main/resources/META-INF/plugin.xml b/plugins/maven/src/main/resources/META-INF/plugin.xml index 65f3f5ead48d..581a7f640910 100644 --- a/plugins/maven/src/main/resources/META-INF/plugin.xml +++ b/plugins/maven/src/main/resources/META-INF/plugin.xml @@ -196,7 +196,6 @@ text="Remove Projects" description="Remove selected Maven projects from the project tree" icon="/general/remove.png"> - ${pom.basedir}"); assertResolved(myProjectPom, baseDir); + + createProjectPom("testproject" + + "1" + + + "${baseUri}"); + + assertResolved(myProjectPom, baseDir); + } + + public void testBuiltInTimestampProperty() throws Exception { + createProjectPom("testproject" + + "1" + + + "${maven.build.timestamp}"); + + assertResolved(myProjectPom, findTag("project.name")); } public void testResolutionWithSeveralProperties() throws Exception { @@ -748,7 +766,8 @@ public class MavenPropertyCompletionAndResolutionTest extends MavenDomTestCase { "parentPomProfilesProp", "parentProfilesXmlProp"); assertContain(variants, "artifactId", "project.artifactId", "pom.artifactId"); - assertContain(variants, "basedir", "project.basedir", "pom.basedir"); + assertContain(variants, "basedir", "project.basedir", "pom.basedir", "baseUri", "project.baseUri", "pom.basedir"); + assertContain(variants, "maven.build.timestamp"); assertContain(variants, "settingsXmlProp"); assertContain(variants, "settings.localRepository"); assertContain(variants, "user.home", "env." + getEnvVar()); diff --git a/plugins/spellchecker/lib-src/jazzy-0.5.2.src.zip b/plugins/spellchecker/lib-src/jazzy-0.5.2.src.zip deleted file mode 100644 index c41b55579596..000000000000 Binary files a/plugins/spellchecker/lib-src/jazzy-0.5.2.src.zip and /dev/null differ diff --git a/plugins/spellchecker/lib/jazzy-core.jar b/plugins/spellchecker/lib/jazzy-core.jar deleted file mode 100644 index 50d313541ea9..000000000000 Binary files a/plugins/spellchecker/lib/jazzy-core.jar and /dev/null differ diff --git a/plugins/spellchecker/lib/patricia-trie-0.1.jar b/plugins/spellchecker/lib/patricia-trie-0.1.jar deleted file mode 100644 index 5d1ea6b242f0..000000000000 Binary files a/plugins/spellchecker/lib/patricia-trie-0.1.jar and /dev/null differ diff --git a/plugins/spellchecker/spellchecker.iml b/plugins/spellchecker/spellchecker.iml index d81c1984675f..edcc0cf37fa4 100644 --- a/plugins/spellchecker/spellchecker.iml +++ b/plugins/spellchecker/spellchecker.iml @@ -14,6 +14,7 @@ + diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/BaseSuggestionProvider.java b/plugins/spellchecker/src/com/intellij/spellchecker/BaseSuggestionProvider.java index 20657aa6be71..5e8a549406b8 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/BaseSuggestionProvider.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/BaseSuggestionProvider.java @@ -33,7 +33,7 @@ public class BaseSuggestionProvider implements SuggestionProvider { @NotNull public List getSuggestions(@NotNull String text) { - + String[] words = NameUtil.nameToWords(text); int index = 0; @@ -54,7 +54,7 @@ public class BaseSuggestionProvider implements SuggestionProvider { index = end; } - String[] all = null; + String[] all; int[] counter = new int[i]; int size = 1; for (int j = 0; j < i; j++) { diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/BundledDictionaryProvider.java b/plugins/spellchecker/src/com/intellij/spellchecker/BundledDictionaryProvider.java index 236ae4a39c9b..3c6ed60b35e2 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/BundledDictionaryProvider.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/BundledDictionaryProvider.java @@ -17,9 +17,6 @@ package com.intellij.spellchecker; import com.intellij.openapi.extensions.ExtensionPointName; -/** - * @author oleg - */ public interface BundledDictionaryProvider { ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.spellchecker.bundledDictionaryProvider"); diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/DefaultBundledDictionariesProvider.java b/plugins/spellchecker/src/com/intellij/spellchecker/DefaultBundledDictionariesProvider.java index 482f3db7bd1d..fc8e77f90ce6 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/DefaultBundledDictionariesProvider.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/DefaultBundledDictionariesProvider.java @@ -15,9 +15,7 @@ */ package com.intellij.spellchecker; -/** - * @author oleg - */ + public class DefaultBundledDictionariesProvider implements BundledDictionaryProvider { public String[] getBundledDictionaries() { return new String[]{"english.dic", "jetbrains.dic"}; diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/FileLoader.java b/plugins/spellchecker/src/com/intellij/spellchecker/FileLoader.java index 2659bb5843d5..cd57fb467dab 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/FileLoader.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/FileLoader.java @@ -25,14 +25,21 @@ import java.io.FileInputStream; import java.io.IOException; +@SuppressWarnings({"IOResourceOpenedButNotSafelyClosed"}) public class FileLoader implements Loader { private static final Logger LOG = Logger.getInstance("#com.intellij.spellchecker.FileLoader"); - private String url; + private final String url; + private final String name; - public FileLoader(String url) { + public FileLoader(String url, String name) { this.url = url; + this.name = name; + } + + public String getName() { + return name; } public void load(@NotNull Consumer consumer) { @@ -40,7 +47,7 @@ public class FileLoader implements Loader { FileInputStream stream = null; try { stream = new FileInputStream(file); - StreamLoader loader = new StreamLoader(stream); + StreamLoader loader = new StreamLoader(stream, file.getName()); loader.load(consumer); } catch (Exception e) { diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/SpellCheckerManager.java b/plugins/spellchecker/src/com/intellij/spellchecker/SpellCheckerManager.java index 630135483e52..762863df385d 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/SpellCheckerManager.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/SpellCheckerManager.java @@ -23,7 +23,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; -import com.intellij.spellchecker.dictionary.Dictionary; +import com.intellij.spellchecker.dictionary.EditableDictionary; import com.intellij.spellchecker.dictionary.Loader; import com.intellij.spellchecker.engine.SpellCheckerEngine; import com.intellij.spellchecker.engine.SpellCheckerFactory; @@ -33,7 +33,6 @@ import com.intellij.spellchecker.state.StateLoader; import com.intellij.spellchecker.util.SPFileUtil; import com.intellij.spellchecker.util.Strings; import com.intellij.util.Consumer; -import com.sun.net.ssl.internal.ssl.SSLEngineImpl; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -51,8 +50,7 @@ public class SpellCheckerManager { private SpellCheckerEngine spellChecker; - private Dictionary userDictionary; - + private EditableDictionary userDictionary; @NotNull @@ -67,26 +65,73 @@ public class SpellCheckerManager { public SpellCheckerManager(Project project, SpellCheckerSettings settings) { this.project = project; this.settings = settings; - reloadConfiguration(); + fullConfigurationReload(); + } + public void fullConfigurationReload() { + spellChecker = SpellCheckerFactory.create(); + fillEngineDictionary(); + } + + + public void updateBundledDictionaries() { + for (BundledDictionaryProvider provider : Extensions.getExtensions(BundledDictionaryProvider.EP_NAME)) { + for (String dictionary : provider.getBundledDictionaries()) { + boolean dictionaryShouldBeLoad = this.settings == null || !this.settings.getBundledDisabledDictionariesPaths().contains(dictionary); + boolean dictionaryIsLoad = spellChecker.isDictionaryLoad(dictionary); + if (dictionaryIsLoad && !dictionaryShouldBeLoad) { + spellChecker.removeDictionary(dictionary); + } + else if (!dictionaryIsLoad && dictionaryShouldBeLoad) { + final Class loaderClass = provider.getClass(); + final InputStream stream = loaderClass.getResourceAsStream(dictionary); + if (stream != null) { + spellChecker.loadDictionary(new StreamLoader(stream, dictionary)); + } + else { + LOG.warn("Couldn't load dictionary '" + dictionary + "' with loader '" + loaderClass + "'"); + } + } + } + } + if (this.settings != null && this.settings.getDictionaryFoldersPaths() != null) { + final Set disabledDictionaries = settings.getDisabledDictionariesPaths(); + for (String folder : this.settings.getDictionaryFoldersPaths()) { + SPFileUtil.processFilesRecursively(folder, new Consumer() { + public void consume(final String s) { + boolean dictionaryShouldBeLoad =!disabledDictionaries.contains(s); + boolean dictionaryIsLoad = spellChecker.isDictionaryLoad(s); + if (dictionaryIsLoad && !dictionaryShouldBeLoad) { + spellChecker.removeDictionary(s); + } + else if (!dictionaryIsLoad && dictionaryShouldBeLoad) { + spellChecker.loadDictionary(new FileLoader(s, s)); + } + + } + }); + + } + } + restartInspections(); } public Project getProject() { return project; } - public Dictionary getUserDictionary() { + public EditableDictionary getUserDictionary() { return userDictionary; } - public void reloadConfiguration() { - spellChecker = SpellCheckerFactory.create(); - fillEngineDictionary(); - } - private void fillEngineDictionary() { spellChecker.reset(); final StateLoader stateLoader = new StateLoader(project); + stateLoader.load(new Consumer() { + public void consume(String s) { + //do nothing - in this loader we don't worry about word list itself - the whole dictionary will be restored + } + }); final List loaders = new ArrayList(); // Load bundled dictionaries from corresponding jars for (BundledDictionaryProvider provider : Extensions.getExtensions(BundledDictionaryProvider.EP_NAME)) { @@ -94,10 +139,11 @@ public class SpellCheckerManager { if (this.settings == null || !this.settings.getBundledDisabledDictionariesPaths().contains(dictionary)) { final Class loaderClass = provider.getClass(); final InputStream stream = loaderClass.getResourceAsStream(dictionary); - if (stream != null){ - loaders.add(new StreamLoader(stream)); - } else { - LOG.warn("Couldn't load dictionary '" + dictionary + "' for loader '" + loaderClass + "'"); + if (stream != null) { + loaders.add(new StreamLoader(stream, dictionary)); + } + else { + LOG.warn("Couldn't load dictionary '" + dictionary + "' with loader '" + loaderClass + "'"); } } } @@ -108,7 +154,7 @@ public class SpellCheckerManager { SPFileUtil.processFilesRecursively(folder, new Consumer() { public void consume(final String s) { if (!disabledDictionaries.contains(s)) { - loaders.add(new FileLoader(s)); + loaders.add(new FileLoader(s, s)); } } }); @@ -120,6 +166,7 @@ public class SpellCheckerManager { spellChecker.loadDictionary(loader); } userDictionary = stateLoader.getDictionary(); + } @@ -131,17 +178,16 @@ public class SpellCheckerManager { final String transformed = spellChecker.getTransformation().transform(word); if (transformed != null) { userDictionary.addToDictionary(transformed); - spellChecker.addToDictionary(transformed); } } - public void update(@Nullable Collection words, SpellCheckerSettings allDictionaries) { + public void updateUserDictionary(@Nullable Collection words) { userDictionary.replaceAll(words); - reloadConfiguration(); restartInspections(); } + @NotNull public List getBundledDictionaries() { final ArrayList dictionaries = new ArrayList(); @@ -162,7 +208,6 @@ public class SpellCheckerManager { } - @NotNull protected List getRawSuggestions(@NotNull String word) { if (!spellChecker.isCorrect(word)) { diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/StreamLoader.java b/plugins/spellchecker/src/com/intellij/spellchecker/StreamLoader.java index 570967259cbb..72e760aa4ff9 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/StreamLoader.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/StreamLoader.java @@ -25,18 +25,26 @@ import java.io.*; public class StreamLoader implements Loader { private static final Logger LOG = Logger.getInstance("#com.intellij.spellchecker.StreamLoader"); + private static final String ENCODING = "UTF-8"; private InputStream stream; + private String name; - public StreamLoader(InputStream stream) { + public StreamLoader(InputStream stream, String name) { this.stream = stream; + this.name=name; } + public String getName() { + return name; + } public void load(@NotNull Consumer consumer) { DataInputStream in = new DataInputStream(stream); - BufferedReader br = new BufferedReader(new InputStreamReader(in)); + BufferedReader br = null; + try { + br = new BufferedReader(new InputStreamReader(in, ENCODING)); String strLine; while ((strLine = br.readLine()) != null) { consumer.consume(strLine); diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/compress/Alphabet.java b/plugins/spellchecker/src/com/intellij/spellchecker/compress/Alphabet.java new file mode 100644 index 000000000000..65c9aaf4e43a --- /dev/null +++ b/plugins/spellchecker/src/com/intellij/spellchecker/compress/Alphabet.java @@ -0,0 +1,72 @@ +package com.intellij.spellchecker.compress; + +import org.jetbrains.annotations.NotNull; + +public final class Alphabet { + + private final Character[] letters; + private int lastIndexUsed = 0; + private int maxIndex = 1023; + + public Character getLetter(int position) { + assert position < maxIndex; + return letters[position]; + } + + /* + @param forceAdd - if set to true - letter will be added to the alphabet if not present yet + @return index of the letter or -1 if letter was not found and could not be added (due to forceAdd property value) + */ + + public int getIndex(@NotNull Character letter, boolean forceAdd) { + return getNextIndex(0, letter, forceAdd); + } + + /* + @param forceAdd - if set to true - letter will be added to the alphabet if not present yet + @return index of the letter or -1 if letter was not found and could not be added (due to forceAdd property value) + */ + public int getNextIndex(int startFrom, @NotNull Character letter, boolean forceAdd) { + for (int i = startFrom; i <= lastIndexUsed; i++) { + if (letters[i] != null && letters[i].equals(letter)) { + return i; + } + } + if (!forceAdd) { + return -1; + } + return add(letter); + } + + public int getLastIndexUsed() { + return lastIndexUsed; + } + + public int getMaxIndex() { + return maxIndex; + } + + public int add(@NotNull Character c) { + lastIndexUsed++; + letters[lastIndexUsed] = c; + return lastIndexUsed; + } + + + Alphabet() { + letters = new Character[maxIndex]; + } + + Alphabet(int maxIndex) { + this.maxIndex = maxIndex; + letters = new Character[maxIndex]; + } + + public static Alphabet create(@NotNull CharSequence alphabet) { + Alphabet result = new Alphabet(alphabet.length() + 1); + for (int i = 0; i < alphabet.length(); i++) { + result.add(alphabet.charAt(i)); + } + return result; + } +} diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/compress/CompressedDictionary.java b/plugins/spellchecker/src/com/intellij/spellchecker/compress/CompressedDictionary.java new file mode 100644 index 000000000000..65fe3c1c9b22 --- /dev/null +++ b/plugins/spellchecker/src/com/intellij/spellchecker/compress/CompressedDictionary.java @@ -0,0 +1,242 @@ +/* + * Copyright 2000-2010 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.spellchecker.compress; + +import com.intellij.spellchecker.dictionary.Dictionary; +import com.intellij.spellchecker.dictionary.Loader; +import com.intellij.spellchecker.engine.Transformation; +import com.intellij.util.Consumer; +import gnu.trove.THashMap; +import org.jetbrains.annotations.NotNull; + +import java.util.*; + +public final class CompressedDictionary implements Dictionary { + + private final Alphabet alphabet; + private int wordsCount; + private byte[][] words; + private int[] lengths; + + private Encoder encoder; + private String name; + private Compressor compressor; + + private final Map> rawData = new THashMap>(); + private static final Comparator COMPARATOR = new Comparator() { + public int compare(byte[] o1, byte[] o2) { + return compareArrays(o1, o2); + } + }; + + CompressedDictionary(@NotNull Alphabet alphabet, @NotNull Compressor compressor, @NotNull Encoder encoder, @NotNull String name) { + this.alphabet = alphabet; + this.compressor = compressor; + this.encoder = encoder; + this.name = name; + } + + void addToDictionary(byte[] word) { + SortedSet set = rawData.get(word.length); + if (set == null) { + set = createSet(); + rawData.put(word.length, set); + } + set.add(word); + wordsCount++; + } + + void pack() { + if (rawData == null) { + return; + } + lengths = new int[rawData.size()]; + this.words = new byte[rawData.size()][]; + int row = 0; + for (Map.Entry> entry : rawData.entrySet()) { + final Integer l = entry.getKey(); + lengths[row] = l; + this.words[row] = new byte[entry.getValue().size() * l]; + int k = 0; + for (byte[] bytes : entry.getValue()) { + for (byte aByte : bytes) { + this.words[row][k++] = aByte; + } + } + row++; + } + rawData.clear(); + } + + private static SortedSet createSet() { + return new TreeSet(COMPARATOR); + + } + + public List getWords(Character first, int minLength, int maxLength) { + int index = alphabet.getIndex(first, false); + List result = new ArrayList(); + if (index == -1) { + return result; + } + int i = 0; + for (byte[] data : words) { + int length = lengths[i]; + for (int x = 0; x < data.length; x += length) { + byte[] toTest = new byte[length]; + System.arraycopy(data, x, toTest, 0, length); + if (toTest[1] != index || toTest[0] > maxLength || toTest[0] < minLength) { + continue; + } + UnitBitSet set = compressor.decompress(toTest); + result.add(encoder.decode(set)); + } + i++; + } + return result; + } + + public List getWords(Character first) { + return getWords(first, 0, Integer.MAX_VALUE); + } + + public String getName() { + return name; + } + + public boolean contains(String word) { + if (word == null) { + return false; + } + try { + UnitBitSet bs = encoder.encode(word, false); + byte[] compressed = compressor.compress(bs); + int index = -1; + for (int i = 0; i < lengths.length; i++) { + if (lengths[i] == compressed.length) { + index = i; + break; + } + } + if (index == -1) { + return false; + } + return contains(compressed, words[index]); + } + catch (EncodingException ignored) { + return false; + } + + } + + public boolean isEmpty() { + return wordsCount <= 0; + } + + public void traverse(Consumer action) { + throw new UnsupportedOperationException(); + } + + public Set getWords() { + throw new UnsupportedOperationException(); + } + + public int getAlphabetLength() { + return alphabet.getLastIndexUsed(); + } + + public int size() { + return wordsCount; + } + + + public String toString() { + final StringBuffer sb = new StringBuffer(); + sb.append("CompressedDictionary"); + sb.append("{wordsCount=").append(wordsCount); + sb.append(", name='").append(name).append('\''); + sb.append('}'); + return sb.toString(); + } + + public static CompressedDictionary create(@NotNull Loader loader, @NotNull final Transformation transform) { + Alphabet alphabet = new Alphabet(); + final Encoder encoder = new Encoder(alphabet); + final Compressor compressor = new Compressor(2); + final CompressedDictionary dictionary = new CompressedDictionary(alphabet, compressor, encoder, loader.getName()); + loader.load(new Consumer() { + public void consume(String s) { + String transformed = transform.transform(s); + if (transformed != null) { + UnitBitSet bs = encoder.encode(transformed, true); + byte[] compressed = compressor.compress(bs); + dictionary.addToDictionary(compressed); + } + } + }); + dictionary.pack(); + return dictionary; + } + + public static int compareArrays(byte[] array1, byte[] array2) { + if (array1.length != array2.length) { + return (array1.length < array2.length ? -1 : 1); + } + //array1.length==array2.length + if (Arrays.equals(array1, array2)) { + return 0; + } + //compare elements values + for (int i = 0; i < array1.length; i++) { + if (array1[i] < array2[i]) { + return -1; + } + else if (array1[i] > array2[i]) { + return 1; + } + } + return 0; + } + + + public static boolean contains(byte[] goal, byte[] data) { + return binarySearchNew(goal, 0, data.length / goal.length, data) >= 0; + } + + public static int binarySearchNew(byte[] goal, int fromIndex, int toIndex, byte[] data) { + int unitLength = goal.length; + int low = fromIndex; + int high = toIndex - 1; + while (low <= high) { + int mid = (low + high) >>> 1; + byte[] toTest = new byte[unitLength]; + System.arraycopy(data, mid * unitLength, toTest, 0, unitLength); + int check = compareArrays(toTest, goal); + if (check == -1) { + low = mid + 1; + } + else if (check == 1) { + high = mid - 1; + } + else { + return mid; + } + } + return -(low + 1); // key not found. + } + + +} diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/compress/Compressor.java b/plugins/spellchecker/src/com/intellij/spellchecker/compress/Compressor.java new file mode 100644 index 000000000000..e8e791121134 --- /dev/null +++ b/plugins/spellchecker/src/com/intellij/spellchecker/compress/Compressor.java @@ -0,0 +1,128 @@ +/* + * Copyright 2000-2010 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.spellchecker.compress; + +import com.intellij.util.Consumer; + +import java.util.ArrayList; +import java.util.List; + +public final class Compressor { + + public static int WORD_LENGTH = 8; + public static byte TRANSITION_WORD = new Double(-Math.pow(2, WORD_LENGTH - 1)).byteValue(); + public final int skipFirst; + + + public Compressor(int skipFirst) { + this.skipFirst = skipFirst; + } + + + public UnitBitSet decompress(byte[] value) { + UnitBitSet bs = new UnitBitSet(WORD_LENGTH - 1, true); + int index = 0; + int bitsPerUnitInOrigin = value[skipFirst]; + + for (int i = skipFirst + 1; i < value.length; i++) { + byte b = value[i]; + if (b > 0) { + //decompress verbose word + bs.setUnitValue(index++, b); + } + else { + int count = (-TRANSITION_WORD + b); + index = index + count + 1; + } + + } + UnitBitSet result = UnitBitSet.create(bs, bitsPerUnitInOrigin); + result.moveRight(skipFirst); + for (int i = 0; i < skipFirst; i++) { + result.setUnitValue(i, value[i]); + } + + return result; + } + + public byte[] compress(UnitBitSet origin) { + if (origin == null) { + return new byte[0]; + } + final List words = new ArrayList(); + final List compressed = new ArrayList(); + + final UnitBitSet copyOfOrigin = UnitBitSet.create(origin, origin.bitsPerUnit); + + for (int i = 0; i < skipFirst; i++) { + compressed.add(Integer.valueOf(copyOfOrigin.getUnitValue(i)).byteValue()); + } + copyOfOrigin.moveLeft(skipFirst); + + compressed.add(Integer.valueOf(copyOfOrigin.bitsPerUnit).byteValue()); + + UnitBitSet bs = UnitBitSet.create(copyOfOrigin, WORD_LENGTH - 1); + bs.iterateParUnits(new Consumer() { + + public void consume(Integer integer) { + if (integer != 0) { + //create verbatim word + words.add(integer.byteValue()); + } + else { + //create transition word + words.add(TRANSITION_WORD); + } + + } + }, 0, true); + + + int count = -1; + for (Byte word : words) { + if (word == TRANSITION_WORD && count == -1) { + count++; + } + else if (word == TRANSITION_WORD && count > -1) { + count++; + } + else if (word != TRANSITION_WORD && count > -1) { + //compress all transition words + while (count > 120) { + compressed.add((byte)(TRANSITION_WORD + 120)); + count -= 121; + } + if (count > -1) { + compressed.add((byte)(TRANSITION_WORD + count)); + } + compressed.add(word); + count = -1; + } + else if (word != TRANSITION_WORD && count == -1) { + compressed.add(word); + count = -1; + } + } + byte[] result = new byte[compressed.size()]; + int i = 0; + for (Byte word : compressed) { + result[i++] = word; + } + return result; + } + + +} diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/compress/Encoder.java b/plugins/spellchecker/src/com/intellij/spellchecker/compress/Encoder.java new file mode 100644 index 000000000000..c2031ff794de --- /dev/null +++ b/plugins/spellchecker/src/com/intellij/spellchecker/compress/Encoder.java @@ -0,0 +1,61 @@ +package com.intellij.spellchecker.compress; + +import com.intellij.spellchecker.compress.Alphabet; +import com.intellij.spellchecker.compress.EncodingException; +import com.intellij.spellchecker.compress.UnitBitSet; +import com.intellij.util.Consumer; +import org.jetbrains.annotations.NotNull; + +import java.text.MessageFormat; + +public final class Encoder { + + private final Alphabet alphabet; + private final int offset = 2; + + public Encoder() { + alphabet = new Alphabet(); + } + + public Encoder(Alphabet alphabet) { + this.alphabet = alphabet; + } + + public Alphabet getAlphabet() { + return alphabet; + } + + public UnitBitSet encode(@NotNull CharSequence letters, boolean force) throws EncodingException { + UnitBitSet bs = new UnitBitSet(alphabet.getMaxIndex(), false); + bs.setUnitValue(0, letters.length()); + bs.setUnitValue(1, alphabet.getIndex(letters.charAt(0), force)); + for (int i = 0; i < letters.length(); i++) { + Character letter = letters.charAt(i); + int index = alphabet.getIndex(letter, force); + bs.setUnitValue(i + offset, index); + } + return bs; + } + + public String decode(@NotNull UnitBitSet bitSet) throws EncodingException { + int wordLength = bitSet.getUnitValue(0); + Character firstLetter = alphabet.getLetter(bitSet.getUnitValue(1)); + final StringBuffer result = new StringBuffer(); + bitSet.iterateParUnits(new Consumer() { + + public void consume(Integer value) { + if (value > 0 && value <= alphabet.getLastIndexUsed()) { + result.append(alphabet.getLetter(value)); + } + } + }, offset, true); + + final String word = result.toString(); + if (word.length() != wordLength || !word.startsWith(String.valueOf(firstLetter))) { + throw new EncodingException( + new MessageFormat("Error during encoding: required length - {0}, starts with {1}, but decoded: {2}") + .format(new Object[]{wordLength, firstLetter, word})); + } + return word; + } +} \ No newline at end of file diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/compress/EncodingException.java b/plugins/spellchecker/src/com/intellij/spellchecker/compress/EncodingException.java new file mode 100644 index 000000000000..a7cd3ddaaaa4 --- /dev/null +++ b/plugins/spellchecker/src/com/intellij/spellchecker/compress/EncodingException.java @@ -0,0 +1,10 @@ +package com.intellij.spellchecker.compress; + +public class EncodingException extends RuntimeException { + + public EncodingException(String message) { + super(message); + } + + +} diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/compress/UnitBitSet.java b/plugins/spellchecker/src/com/intellij/spellchecker/compress/UnitBitSet.java new file mode 100644 index 000000000000..020a5711ca73 --- /dev/null +++ b/plugins/spellchecker/src/com/intellij/spellchecker/compress/UnitBitSet.java @@ -0,0 +1,120 @@ +package com.intellij.spellchecker.compress; + +import com.intellij.util.Consumer; +import org.jetbrains.annotations.NotNull; + +import java.util.BitSet; + +public class UnitBitSet extends BitSet { + + public int bitsPerUnit = 10; + + public UnitBitSet() { + } + + public UnitBitSet(int value, boolean exact) { + if (exact) { + this.bitsPerUnit = value; + } + else { + this.bitsPerUnit = new Double(Math.log(value) / Math.log(2)).intValue() + 1; + } + } + + public void inc() { + boolean t = true; + for (int i = 0; i <= size(); i++) { + if (!get(i) && t) { + set(i); + t = false; + } + else if (get(i) && t) { + set(i, false); + } + } + } + + public int getUnitValue(int number) { + int startIndex = number * bitsPerUnit; + int result = 0; + for (int i = 0; i < bitsPerUnit; i++) { + if (get(startIndex + i)) { + result += Math.pow(2, i); + } + } + return result; + } + + public void setUnitValue(int number, int value) { + if (value > Math.pow(2, bitsPerUnit) - 1) { + throw new IllegalArgumentException(); + } + int startIndex = number * bitsPerUnit; + for (int i = 0; i < bitsPerUnit; i++) { + set(startIndex + i, value % 2 == 1); + value /= 2; + } + } + + public void setBits(int... bits) { + if (bits == null) { + return; + } + for (int bit : bits) { + set(bit); + } + } + + public void moveLeft(int n) { + if (n <= 0) { + return; + } + for (int i = 0; i < size() - n * bitsPerUnit; i++) { + set(i, get(n * bitsPerUnit + i)); + } + for (int i = size() - n * bitsPerUnit; i < size(); i++) { + set(i, false); + } + } + + public void moveRight(int n) { + if (n <= 0) { + return; + } + for (int i = size() - 1; i >= 0; i--) { + set(i + n * bitsPerUnit, get(i)); + } + for (int i = 0; i < n * bitsPerUnit; i++) { + set(i, false); + } + } + + + public void iterateParUnits(@NotNull Consumer consumer, int offset, boolean skipLastZero) { + int units = size() / bitsPerUnit; + for (int i = offset; i <= units; i++) { + consumer.consume(getUnitValue(i)); + if (skipLastZero && nextSetBit(i) == -1) { + break; + } + } + } + + public static UnitBitSet create(int... bits) { + UnitBitSet bs = new UnitBitSet(); + bs.setBits(bits); + return bs; + } + + public static UnitBitSet create(int bitsPerUnit, boolean exact, int... bits) { + UnitBitSet bs = new UnitBitSet(bitsPerUnit, exact); + bs.setBits(bits); + return bs; + } + + public static UnitBitSet create(@NotNull BitSet bitSet, int bitsPerUnit) { + UnitBitSet result = new UnitBitSet(bitsPerUnit, true); + result.or(bitSet); + return result; + } +} diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/AggregatedDictionary.java b/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/AggregatedDictionary.java index ba886c936716..8ae6017fe808 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/AggregatedDictionary.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/AggregatedDictionary.java @@ -15,7 +15,7 @@ */ package com.intellij.spellchecker.dictionary; -import com.intellij.spellchecker.trie.Action; +import com.intellij.util.Consumer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -23,17 +23,17 @@ import java.util.Collection; import java.util.HashSet; import java.util.Set; -public class AggregatedDictionary implements Dictionary { +public class AggregatedDictionary implements EditableDictionary { private static final String DICTIONARY_NAME = "common"; - private Dictionary cachedDictionary; + private EditableDictionary cachedDictionary; private ProjectDictionary projectDictionary; public String getName() { return DICTIONARY_NAME; } - public AggregatedDictionary(@NotNull ProjectDictionary projectDictionary, @NotNull Dictionary cachedDictionary) { + public AggregatedDictionary(@NotNull ProjectDictionary projectDictionary, @NotNull EditableDictionary cachedDictionary) { this.projectDictionary = projectDictionary; this.cachedDictionary = cachedDictionary; this.cachedDictionary.addToDictionary(projectDictionary.getWords()); @@ -77,15 +77,17 @@ public class AggregatedDictionary implements Dictionary { getProjectDictionary().clear(); } - public void traverse(final Action action) { - cachedDictionary.traverse(action); + public void traverse(@NotNull final Consumer consumer) { + cachedDictionary.traverse(consumer); } public Set getWords() { - Set words = new HashSet(); - words.addAll(cachedDictionary.getWords()); - return words; + return cachedDictionary!=null?cachedDictionary.getWords():null; + } + + public int size() { + return (cachedDictionary!=null?cachedDictionary.size():0); } @Nullable @@ -93,22 +95,13 @@ public class AggregatedDictionary implements Dictionary { return getProjectDictionary().getEditableWords(); } - @Nullable - public Set getNotEditableWords() { - Set words = getWords(); - Set editable = getEditableWords(); - if (words != null && editable != null) { - words.removeAll(editable); - } - return words; - } public void addToDictionary(@Nullable Collection words) { getProjectDictionary().addToDictionary(words); getCachedDictionary().addToDictionary(words); } - public Dictionary getCachedDictionary() { + public EditableDictionary getCachedDictionary() { return cachedDictionary; } diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/Dictionary.java b/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/Dictionary.java index 48b17c285a42..5c689b1714c6 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/Dictionary.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/Dictionary.java @@ -15,10 +15,9 @@ */ package com.intellij.spellchecker.dictionary; -import com.intellij.spellchecker.trie.Action; +import com.intellij.util.Consumer; import org.jetbrains.annotations.Nullable; -import java.util.Collection; import java.util.Set; public interface Dictionary { @@ -29,26 +28,10 @@ public interface Dictionary { boolean isEmpty(); - void addToDictionary(String word); - - void removeFromDictionary(String word); - - void addToDictionary(@Nullable Collection words); - - void replaceAll(@Nullable Collection words); - - void clear(); - - void traverse(final Action action); + void traverse(final Consumer action); @Nullable Set getWords(); - @Nullable - Set getEditableWords(); - - @Nullable - Set getNotEditableWords(); - - + int size(); } diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/EditableDictionary.java b/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/EditableDictionary.java new file mode 100644 index 000000000000..d2abc06519bc --- /dev/null +++ b/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/EditableDictionary.java @@ -0,0 +1,39 @@ +/* + * Copyright 2000-2010 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.spellchecker.dictionary; + +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.Set; + +public interface EditableDictionary extends Dictionary { + + void addToDictionary(@Nullable String word); + + void removeFromDictionary(@Nullable String word); + + void addToDictionary(@Nullable Collection words); + + void replaceAll(@Nullable Collection words); + + void clear(); + + @Nullable + Set getEditableWords(); + + +} diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/EditableDictionaryLoader.java b/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/EditableDictionaryLoader.java new file mode 100644 index 000000000000..2c0fe63b2f7d --- /dev/null +++ b/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/EditableDictionaryLoader.java @@ -0,0 +1,22 @@ +/* + * Copyright 2000-2010 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.spellchecker.dictionary; + + +public interface EditableDictionaryLoader extends Loader{ + + EditableDictionary getDictionary(); +} diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/Loader.java b/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/Loader.java index e66a13933749..a15cdd5d6e8a 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/Loader.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/Loader.java @@ -22,4 +22,6 @@ import org.jetbrains.annotations.NotNull; public interface Loader { void load(@NotNull Consumer consumer); + + String getName(); } diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/ProjectDictionary.java b/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/ProjectDictionary.java index e31e10a74313..b3ac0e314d41 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/ProjectDictionary.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/ProjectDictionary.java @@ -15,24 +15,27 @@ */ package com.intellij.spellchecker.dictionary; -import com.intellij.spellchecker.trie.Action; +import com.intellij.util.Consumer; +import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; -public class ProjectDictionary implements Dictionary { +public class ProjectDictionary implements EditableDictionary { private static final String DEFAULT_CURRENT_USER_NAME = "default.user"; private static final String DEFAULT_PROJECT_DICTIONARY_NAME = "project"; private String activeName; - private Set dictionaries; + private Set dictionaries; public ProjectDictionary() { } - public ProjectDictionary(Set dictionaries) { + public ProjectDictionary(Set dictionaries) { this.dictionaries = dictionaries; } @@ -73,20 +76,20 @@ public class ProjectDictionary implements Dictionary { } @NotNull - private Dictionary getActiveDictionary() { + private EditableDictionary getActiveDictionary() { return ensureCurrentUserDictionary(); } @NotNull - private Dictionary ensureCurrentUserDictionary() { + private EditableDictionary ensureCurrentUserDictionary() { if (activeName == null) { activeName = DEFAULT_CURRENT_USER_NAME; } - Dictionary result = getDictionaryByName(activeName); + EditableDictionary result = getDictionaryByName(activeName); if (result == null) { result = new UserDictionary(this.activeName); if (dictionaries == null) { - dictionaries = new HashSet(); + dictionaries = new THashSet(); } dictionaries.add(result); } @@ -94,12 +97,12 @@ public class ProjectDictionary implements Dictionary { } @Nullable - private Dictionary getDictionaryByName(@NotNull String name) { + private EditableDictionary getDictionaryByName(@NotNull String name) { if (dictionaries == null) { return null; } - Dictionary result = null; - for (Dictionary dictionary : dictionaries) { + EditableDictionary result = null; + for (EditableDictionary dictionary : dictionaries) { if (dictionary.getName().equals(name)) { result = dictionary; break; @@ -130,13 +133,21 @@ public class ProjectDictionary implements Dictionary { return words; } - public void traverse(Action action) { + public int size(){ + int result = 0; + for (Dictionary dictionary : dictionaries) { + result+=dictionary.size(); + } + return result; + } + + public void traverse(final Consumer consumer) { if (dictionaries == null) { return; } - for (Dictionary dictionary : dictionaries) { - dictionary.traverse(action); + for (EditableDictionary dictionary : dictionaries) { + dictionary.traverse(consumer); } } @@ -146,21 +157,12 @@ public class ProjectDictionary implements Dictionary { return getActiveDictionary().getWords(); } - @Nullable - public Set getNotEditableWords() { - Set words = getWords(); - Set editable = getEditableWords(); - if (words != null && editable != null) { - words.removeAll(editable); - } - return words; - } public void addToDictionary(@Nullable Collection words) { getActiveDictionary().addToDictionary(words); } - public Set getDictionaries() { + public Set getDictionaries() { return dictionaries; } diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/UserDictionary.java b/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/UserDictionary.java index 3a8bd704072b..87975558a9a8 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/UserDictionary.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/UserDictionary.java @@ -15,17 +15,20 @@ */ package com.intellij.spellchecker.dictionary; -import com.intellij.spellchecker.trie.Action; +import com.intellij.util.Consumer; import gnu.trove.THashSet; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.Set; -public class UserDictionary implements Dictionary { +public class UserDictionary implements EditableDictionary { private String name; - private THashSet dictionary = new THashSet(); + + @NotNull + private final THashSet words = new THashSet(); public UserDictionary(String name) { this.name = name; @@ -36,26 +39,25 @@ public class UserDictionary implements Dictionary { } public boolean contains(String word) { - return word != null && dictionary.contains(word); + return words.contains(word); + } + + public int size() { + return words == null ? 0 : words.size(); } @Nullable public Set getWords() { - return dictionary; + return words; } @Nullable public Set getEditableWords() { - return dictionary; - } - - @Nullable - public Set getNotEditableWords() { - return null; + return words; } public void clear() { - dictionary.clear(); + words.clear(); } @@ -63,14 +65,14 @@ public class UserDictionary implements Dictionary { if (word == null) { return; } - dictionary.add(word); + words.add(word); } public void removeFromDictionary(String word) { if (word == null) { return; } - dictionary.remove(word); + words.remove(word); } public void replaceAll(@Nullable Collection words) { @@ -88,7 +90,7 @@ public class UserDictionary implements Dictionary { } public boolean isEmpty() { - return (dictionary == null || dictionary.size() == 0); + return words.size() == 0; } @Override @@ -102,12 +104,9 @@ public class UserDictionary implements Dictionary { } - public void traverse(final Action action){ - if (dictionary==null){ - return; - } - for (String s : dictionary) { - action.run(s); + public void traverse(final Consumer consumer) { + for (String word : words) { + consumer.consume(word); } } @@ -118,6 +117,6 @@ public class UserDictionary implements Dictionary { @Override public String toString() { - return "UserDictionary{" + "name='" + name + '\'' + ", words.count=" + dictionary.size() + '}'; + return "UserDictionary{" + "name='" + name + '\'' + ", words.count=" + words.size() + '}'; } } diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/engine/BaseSpellChecker.java b/plugins/spellchecker/src/com/intellij/spellchecker/engine/BaseSpellChecker.java index 29b9844f488c..fcb9729df678 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/engine/BaseSpellChecker.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/engine/BaseSpellChecker.java @@ -15,68 +15,157 @@ */ package com.intellij.spellchecker.engine; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.spellchecker.compress.CompressedDictionary; import com.intellij.spellchecker.dictionary.Dictionary; +import com.intellij.spellchecker.dictionary.EditableDictionary; +import com.intellij.spellchecker.dictionary.EditableDictionaryLoader; import com.intellij.spellchecker.dictionary.Loader; -import com.intellij.spellchecker.dictionary.UserDictionary; -import com.intellij.spellchecker.trie.Action; import com.intellij.util.Consumer; +import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; +import java.util.*; public class BaseSpellChecker implements SpellCheckerEngine { - private Dictionary engineDictionary; private Transformation transform = new Transformation(); + private final Set dictionaries = new THashSet(); + private final Set bundledDictionaries = new THashSet(); private Metrics metrics = new LevenshteinDistance(); - private Consumer consumer = new Consumer() { - public void consume(@Nullable String word) { - final String transformed = transform.transform(word); - if (transformed != null) { - engineDictionary.addToDictionary(transformed); - } - } - }; public BaseSpellChecker() { - ensureEngineDictionary(); } + public void loadDictionary(@NotNull Loader loader) { - loader.load(consumer); + if (loader instanceof EditableDictionaryLoader) { + final EditableDictionary dictionary = ((EditableDictionaryLoader)loader).getDictionary(); + if (dictionary != null) { + addModifiableDictionary(dictionary); + } + } + else { + loadFixedDictionary(loader); + } + + } + + private void loadFixedDictionary(final @NotNull Loader loader) { + /*if (ApplicationManager.getApplication().isUnitTestMode()) { + loadCompressedDictionary(loader); + } + else { + ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { + public void run() { + loadCompressedDictionary(loader); + } + }); + }*/ + loadCompressedDictionary(loader); + } + + private void loadCompressedDictionary(@NotNull Loader loader) { + final CompressedDictionary dictionary = CompressedDictionary.create(loader, transform); + if (dictionary != null) { + addCompressedFixedDictionary(dictionary); + } + } + + private void addModifiableDictionary(@NotNull EditableDictionary dictionary) { + dictionaries.add(dictionary); + } + + private void addCompressedFixedDictionary(@NotNull Dictionary dictionary) { + bundledDictionaries.add(dictionary); } public Transformation getTransformation() { return transform; } - private void ensureEngineDictionary() { - if (engineDictionary == null) { - engineDictionary = new UserDictionary("engine"); + @NotNull + private static List restore(@NotNull Character startFrom, int i, int j, @Nullable Collection dictionaries) { + if (dictionaries == null) { + return Collections.emptyList(); } + List results = new ArrayList(); + + for (Object o : dictionaries) { + if (o instanceof Dictionary) { + results.addAll(restore(startFrom, i, j, (Dictionary)o)); + } + + } + return results; } - - public void addToDictionary(String word) { - final String transformed = transform.transform(word); - if (transformed != null) { - engineDictionary.addToDictionary(transformed); + @NotNull + private static List restore(@NotNull final Character first, final int i, final int j, @Nullable Dictionary dictionary) { + if (dictionary == null) { + return Collections.emptyList(); } + final List result = new ArrayList(); + if (dictionary instanceof CompressedDictionary) { + result.addAll(((CompressedDictionary)dictionary).getWords(first)); + } + else { + dictionary.traverse(new Consumer() { + public void consume(String s) { + if (StringUtil.isEmpty(s)) { + return; + } + if (s.charAt(0) == first && s.length() >= i && s.length() <= j) { + result.add(s); + } + } + }); + } + + return result; + } + + private static boolean isCorrect(@NotNull String transformed, @Nullable Collection dictionaries) { + if (dictionaries == null) { + return true; + } + + for (Object o : dictionaries) { + if (o instanceof Dictionary) { + boolean result = isCorrect(transformed, (Dictionary)o); + if (result) { + return true; + } + } + + } + return false; + } + + private static boolean isCorrect(@NotNull String transformed, @Nullable Dictionary dictionary) { + if (dictionary == null) { + return true; + } + + return dictionary.contains(transformed); } public boolean isCorrect(@NotNull String word) { final String transformed = transform.transform(word); - return transformed != null && engineDictionary.contains(transformed); + if (transformed == null) { + return true; + } + return isCorrect(transformed, bundledDictionaries) || isCorrect(transformed, dictionaries); + + } + @NotNull public List getSuggestions(final @NotNull String word, int threshold, int quality) { final String transformed = transform.transform(word); @@ -84,14 +173,12 @@ public class BaseSpellChecker implements SpellCheckerEngine { return Collections.emptyList(); } final List suggestions = new ArrayList(); - engineDictionary.traverse(new Action() { - public void run(String entry) { - if (transformed.charAt(0) == entry.charAt(0)/* && Math.abs(mpw.length() - entry.getKey().length()) <= 1*/) { - final int distance = metrics.calculateMetrics(transformed, entry); - suggestions.add(new Suggestion(entry, distance)); - } - } - }); + List rawSuggestions = restore(transformed.charAt(0), 0, Integer.MAX_VALUE, bundledDictionaries); + rawSuggestions.addAll(restore(word.charAt(0), 0, Integer.MAX_VALUE, dictionaries)); + for (String rawSuggestion : rawSuggestions) { + final int distance = metrics.calculateMetrics(transformed, rawSuggestion); + suggestions.add(new Suggestion(rawSuggestion, distance)); + } List result = new ArrayList(); if (suggestions.isEmpty()) { return result; @@ -108,13 +195,44 @@ public class BaseSpellChecker implements SpellCheckerEngine { return result; } + @NotNull public List getVariants(@NotNull String prefix) { - return null; + //if (StringUtil.isEmpty(prefix)) { + return Collections.emptyList(); + //} + } public void reset() { - engineDictionary = null; - ensureEngineDictionary(); + bundledDictionaries.clear(); + dictionaries.clear(); + } + + public boolean isDictionaryLoad(@NotNull String name) { + return getBundledDictionaryByName(name) != null; + } + + public void removeDictionary(@NotNull String name) { + for (Iterator iterator = bundledDictionaries.iterator(); iterator.hasNext();) { + Dictionary dictionary = iterator.next(); + if (name.equals(dictionary.getName())) { + iterator.remove(); + break; + } + } + } + + @Nullable + public Dictionary getBundledDictionaryByName(@NotNull String name) { + if (bundledDictionaries == null) { + return null; + } + for (Dictionary dictionary : bundledDictionaries) { + if (name.equals(dictionary.getName())) { + return dictionary; + } + } + return null; } } diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/engine/SpellCheckerEngine.java b/plugins/spellchecker/src/com/intellij/spellchecker/engine/SpellCheckerEngine.java index 05b6e0249788..80bb8dd87232 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/engine/SpellCheckerEngine.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/engine/SpellCheckerEngine.java @@ -30,7 +30,6 @@ public interface SpellCheckerEngine { boolean isCorrect(@NotNull String word); - void addToDictionary(String word); @NotNull List getSuggestions(@NotNull String word, int threshold, int quality); @@ -38,9 +37,10 @@ public interface SpellCheckerEngine { @NotNull List getVariants(@NotNull String prefix); - /** - * This method must clean up user dictionary words and ignored words. - */ + void reset(); + boolean isDictionaryLoad(@NotNull String name); + + void removeDictionary(@NotNull String name); } diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/engine/Transformation.java b/plugins/spellchecker/src/com/intellij/spellchecker/engine/Transformation.java index 6381a025b351..79a5fb88fde1 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/engine/Transformation.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/engine/Transformation.java @@ -21,10 +21,11 @@ import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.Set; +@SuppressWarnings({"ALL"}) public class Transformation { @Nullable - public String transform(@Nullable String word) { + public String transform(@Nullable String word) { if (word == null || word.trim().length() < 3) { return null; } @@ -32,7 +33,7 @@ public class Transformation { } @Nullable - public Set transform(@Nullable Collection words) { + public Set transform(@Nullable Collection words) { if (words == null || words.isEmpty()) { return null; } diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/inspections/SpellCheckingInspection.java b/plugins/spellchecker/src/com/intellij/spellchecker/inspections/SpellCheckingInspection.java index 359f1f9783a8..2cc1e161567a 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/inspections/SpellCheckingInspection.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/inspections/SpellCheckingInspection.java @@ -36,6 +36,7 @@ import com.intellij.spellchecker.tokenizer.Token; import com.intellij.spellchecker.tokenizer.Tokenizer; import com.intellij.spellchecker.util.SpellCheckerBundle; import com.intellij.util.containers.hash.HashMap; +import gnu.trove.THashSet; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -43,14 +44,12 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; -import java.util.ArrayList; -import java.util.Collection; +import java.util.*; import java.util.List; -import java.util.Map; public class SpellCheckingInspection extends LocalInspectionTool { - + public static final String SPELL_CHECKING_INSPECTION_TOOL_NAME = "SpellCheckingInspection"; private static final AcceptWordAsCorrect BATCH_ACCEPT_FIX = new AcceptWordAsCorrect(); @@ -146,56 +145,68 @@ public class SpellCheckingInspection extends LocalInspectionTool { if (tokens == null) { return; } + SpellCheckerManager manager = SpellCheckerManager.getInstance(element.getProject()); + Set alreadyChecked = new THashSet(); for (Token token : tokens) { - inspect(token, holder, isOnTheFly, NAMES_VALIDATORS); + inspect(token, holder, isOnTheFly, alreadyChecked, manager,NAMES_VALIDATORS); } } }; } - private static void inspect(Token token, ProblemsHolder holder, boolean isOnTheFly, NamesValidator... validators) { + + private static void inspect(Token token, ProblemsHolder holder, boolean isOnTheFly,@NotNull Set alreadyChecked, @NotNull SpellCheckerManager manager, NamesValidator... validators) { List areaList = TextSplitter.splitText(token.getText()); if (areaList == null) { return; } for (CheckArea area : areaList) { boolean ignored = area.isIgnored(); - boolean keyword = isKeyword(validators, token.getElement(), area.getWord()); - if (!ignored && !keyword) { - inspect(area, token, holder, isOnTheFly); + final TextRange textRange = area.getTextRange(); + + if (ignored || textRange ==null){ + continue; } + + final String word = area.getWord(); + if (word==null || (!isOnTheFly && alreadyChecked.contains(word))){ + continue; + } + + boolean keyword = isKeyword(validators, token.getElement(), word); + if (keyword){ + continue; + } + + boolean hasProblems = manager.hasProblem(word); + if (hasProblems){ + if (!isOnTheFly){ + alreadyChecked.add(word); + addBatchDescriptor(textRange,token,holder); + } else{ + addRegularDescriptor(textRange,token,holder); + } + } + } } - private static void inspect(@NotNull CheckArea area, @NotNull Token token, @NotNull ProblemsHolder holder, boolean isOnTheFly) { - SpellCheckerManager manager = SpellCheckerManager.getInstance(token.getElement().getProject()); - - final TextRange textRange = area.getTextRange(); - final String word = area.getWord(); - - if (textRange == null || word == null) { - return; - } - - if (manager.hasProblem(word)) { + private static void addBatchDescriptor(@NotNull TextRange textRange, @NotNull Token token, @NotNull ProblemsHolder holder) { List fixes = new ArrayList(); - if (isOnTheFly) { - if (!token.isUseRename()) { - fixes.add(new ChangeTo()); - } - else { - fixes.add(new RenameTo()); - } - } + fixes.add(BATCH_ACCEPT_FIX); - final AcceptWordAsCorrect acceptWordAsCorrect = isOnTheFly ? new AcceptWordAsCorrect() : BATCH_ACCEPT_FIX; - fixes.add(acceptWordAsCorrect); - - final ProblemDescriptor problemDescriptor = createProblemDescriptor(token, holder, textRange, fixes, isOnTheFly); + final ProblemDescriptor problemDescriptor = createProblemDescriptor(token, holder, textRange, fixes, false); holder.registerProblem(problemDescriptor); - } + } + private static void addRegularDescriptor(@NotNull TextRange textRange, @NotNull Token token, @NotNull ProblemsHolder holder) { + List fixes = new ArrayList(); + fixes.add((token.isUseRename()?new RenameTo():new ChangeTo())); + fixes.add(new AcceptWordAsCorrect()); + + final ProblemDescriptor problemDescriptor = createProblemDescriptor(token, holder, textRange, fixes, true); + holder.registerProblem(problemDescriptor); } private static ProblemDescriptor createProblemDescriptor(Token token, diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/inspections/TextSplitter.java b/plugins/spellchecker/src/com/intellij/spellchecker/inspections/TextSplitter.java index 39286a1407b8..2599714b642d 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/inspections/TextSplitter.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/inspections/TextSplitter.java @@ -15,7 +15,6 @@ */ package com.intellij.spellchecker.inspections; -import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.codeStyle.NameUtil; @@ -29,19 +28,10 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; -/** - * @author shkate@jetbrains.com - */ public class TextSplitter { - @NonNls - private static final Pattern NON_SPACE = Pattern.compile("\\S+"); @NonNls - private static final Pattern HTML = Pattern.compile("<(0)>"); - - @NonNls - private static final Pattern WORD = Pattern.compile("\\b\\p{L}*'?\\p{L}*"); private static final Pattern EXTENDED_WORD = Pattern.compile("\\b\\p{L}*'?\\p{L}(_*\\p{L})*"); @@ -51,12 +41,15 @@ public class TextSplitter { @NonNls private static final Pattern URL = Pattern.compile("(https?|ftp|mailto)\\:\\/\\/"); + @NonNls private static final Pattern COMPLEX = Pattern.compile("(\\.[^\\.]+)|([@]+)"); @NonNls private static final Pattern SPECIAL = Pattern.compile("^&\\p{Alnum}{4};"); + private static final String delimiters = ".,;:!?*/&\""; + private TextSplitter() { } @@ -73,8 +66,8 @@ public class TextSplitter { List results = new ArrayList(); String[] pieces = text.substring(i).split(WORD_SPLITTER); for (String s : pieces) { - ProgressManager.checkCanceled(); - if (s.length() > 0) { + //ProgressManager.checkCanceled(); + if (s.length() > 0 && startWithLetterOrDelimiter(s)) { int p1 = text.indexOf(s, i); TextRange range = TextRange.from(p1, s.length()); List areaList = splitNonSpace(text, range); @@ -87,6 +80,13 @@ public class TextSplitter { return (results.size() == 0) ? null : results; } + private static boolean startWithLetterOrDelimiter(@NotNull String s) { + // Matcher matcher = PRINTABLE.matcher(s); + // return matcher.find(); + return Character.isLetter(s.charAt(0)) || delimiters.contains(s.substring(0,1)); + // return true; + } + @Nullable private static List splitNonSpace(String text, TextRange range) { String nonSpaceArea = text.substring(range.getStartOffset(), range.getEndOffset()); @@ -100,12 +100,12 @@ public class TextSplitter { @NotNull private static List splitSimpleWord(String text, TextRange range) { List results = new ArrayList(); - if (text==null || range==null || range.getLength()<1){ + if (text == null || range == null || range.getLength() < 1) { return results; } String word = text.substring(range.getStartOffset(), range.getEndOffset()); String[] words = NameUtil.splitNameIntoWords(word); - if (words == null || words.length==0) { + if (words == null || words.length == 0) { return results; } @@ -132,7 +132,7 @@ public class TextSplitter { int start = word.indexOf(s, index); int end = start + s.length(); boolean isUpperCase = Strings.isUpperCase(s); - boolean flag = (isUpperCase && !isAllWordsAreUpperCased) || isKeyword(s); + boolean flag = (isUpperCase && !isAllWordsAreUpperCased); Matcher matcher = WORD.matcher(s); if (matcher.find()) { TextRange found = matcherRange(subRange(range, start, end), matcher); @@ -172,12 +172,15 @@ public class TextSplitter { private static void addWord(String text, List results, boolean flag, TextRange found) { boolean tooShort = (found.getEndOffset() - found.getStartOffset()) <= 3; - results.add(new CheckArea(text, found, flag || tooShort)); + for (int i = found.getStartOffset(); i{ +public class CachedDictionaryState extends DictionaryState implements PersistentStateComponent { public static final String DEFAULT_NAME = "cached"; public CachedDictionaryState() { name = DEFAULT_NAME; } - public CachedDictionaryState(Dictionary dictionary) { + public CachedDictionaryState(EditableDictionary dictionary) { super(dictionary); name = DEFAULT_NAME; } @Override public void loadState(DictionaryState state) { - if (state.name==null){ - state.name= DEFAULT_NAME; + if (state.name == null) { + state.name = DEFAULT_NAME; } super.loadState(state); } diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/state/DictionaryState.java b/plugins/spellchecker/src/com/intellij/spellchecker/state/DictionaryState.java index 1339cd523c3f..d392b69d152d 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/state/DictionaryState.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/state/DictionaryState.java @@ -16,7 +16,7 @@ package com.intellij.spellchecker.state; import com.intellij.openapi.components.PersistentStateComponent; -import com.intellij.spellchecker.dictionary.Dictionary; +import com.intellij.spellchecker.dictionary.EditableDictionary; import com.intellij.spellchecker.dictionary.UserDictionary; import com.intellij.util.xmlb.annotations.AbstractCollection; import com.intellij.util.xmlb.annotations.Attribute; @@ -39,24 +39,24 @@ public class DictionaryState implements PersistentStateComponent projectDictionaries = projectDictionary.getDictionaries(); + Set projectDictionaries = projectDictionary.getDictionaries(); if (projectDictionaries != null) { - for (Dictionary dic : projectDictionary.getDictionaries()) { + for (EditableDictionary dic : projectDictionary.getDictionaries()) { dictionaryStates.add(new DictionaryState(dic)); } } @@ -101,7 +103,7 @@ public class ProjectDictionaryState implements PersistentStateComponent dictionaries = new HashSet(); + Set dictionaries = new THashSet(); if (dictionaryStates != null) { for (DictionaryState dictionaryState : dictionaryStates) { dictionaryState.loadState(dictionaryState); diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/state/StateLoader.java b/plugins/spellchecker/src/com/intellij/spellchecker/state/StateLoader.java index 86d163adc407..b31aaf0c5d44 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/state/StateLoader.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/state/StateLoader.java @@ -17,40 +17,48 @@ package com.intellij.spellchecker.state; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.project.Project; -import com.intellij.spellchecker.dictionary.Dictionary; -import com.intellij.spellchecker.dictionary.Loader; +import com.intellij.spellchecker.dictionary.EditableDictionary; +import com.intellij.spellchecker.dictionary.EditableDictionaryLoader; import com.intellij.util.Consumer; import org.jetbrains.annotations.NotNull; import java.util.Set; -public class StateLoader implements Loader { +public class StateLoader implements EditableDictionaryLoader { - private Project project; - private Dictionary dictionary; + private Project project; + private EditableDictionary dictionary; - public StateLoader(Project project) { - this.project = project; + public StateLoader(Project project) { + this.project = project; + } + + + public void load(@NotNull Consumer consumer) { + AggregatedDictionaryState state = ServiceManager.getService(project, AggregatedDictionaryState.class); + state.setProject(project); + state.loadState(); + dictionary = state.getDictionary(); + if (dictionary == null) { + return; + } + final Set storedWords = dictionary.getWords(); + if (storedWords != null) { + for (String word : storedWords) { + consumer.consume(word); + } } + } - public void load(@NotNull Consumer consumer) { - AggregatedDictionaryState state = ServiceManager.getService(project, AggregatedDictionaryState.class); - state.setProject(project); - state.loadState(); - dictionary = state.getDictionary(); - final Set storedWords = dictionary.getWords(); - if (storedWords!=null){ - for (String word : storedWords) { - consumer.consume(word); - } - } - } + public EditableDictionary getDictionary() { + return dictionary; + } - public Dictionary getDictionary() { - return dictionary; - } + public String getName() { + return (dictionary != null ? dictionary.getName() : ""); + } } diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/CommentTokenizer.java b/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/CommentTokenizer.java index a9b47f0964bf..7b1b4360b065 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/CommentTokenizer.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/CommentTokenizer.java @@ -19,11 +19,6 @@ import com.intellij.psi.PsiComment; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -/** - * Created by IntelliJ IDEA. - * - * @author shkate@jetbrains.com - */ public class CommentTokenizer extends Tokenizer { @Nullable diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/HtmlSpellcheckingStrategy.java b/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/HtmlSpellcheckingStrategy.java index d57820623fce..150e46c7d103 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/HtmlSpellcheckingStrategy.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/HtmlSpellcheckingStrategy.java @@ -22,11 +22,6 @@ import com.intellij.psi.xml.XmlAttributeValue; import com.intellij.psi.xml.XmlText; import org.jetbrains.annotations.NotNull; -/** - * Created by IntelliJ IDEA. - * - * @author shkate@jetbrains.com - */ public class HtmlSpellcheckingStrategy extends SpellcheckingStrategy { @NotNull @Override diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/TextTokenizer.java b/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/TextTokenizer.java index dd47ae9e4932..bec720598155 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/TextTokenizer.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/TextTokenizer.java @@ -19,14 +19,8 @@ import com.intellij.psi.PsiPlainText; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -/** - * Created by IntelliJ IDEA. - * - * @author shkate@jetbrains.com - */ public class TextTokenizer extends Tokenizer { - @Nullable @Override public Token[] tokenize(@NotNull PsiPlainText element) { diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/Token.java b/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/Token.java index 903cabaecd07..774c67514043 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/Token.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/Token.java @@ -17,11 +17,7 @@ package com.intellij.spellchecker.tokenizer; import com.intellij.psi.PsiElement; -/** - * Created by IntelliJ IDEA. - * - * @author shkate@jetbrains.com - */ + public class Token { private String text; diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/Tokenizer.java b/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/Tokenizer.java index bb3583f27a78..483c2ff7d39f 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/Tokenizer.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/Tokenizer.java @@ -19,11 +19,6 @@ import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -/** - * Created by IntelliJ IDEA. - * - * @author shkate@jetbrains.com - */ public class Tokenizer { @Nullable diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/XmlAttributeTokenizer.java b/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/XmlAttributeTokenizer.java index 4e538139dfc4..5925f4555c50 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/XmlAttributeTokenizer.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/XmlAttributeTokenizer.java @@ -19,11 +19,6 @@ import com.intellij.psi.xml.XmlAttributeValue; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -/** - * Created by IntelliJ IDEA. - * - * @author shkate@jetbrains.com - */ public class XmlAttributeTokenizer extends Tokenizer{ diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/XmlTextTokenizer.java b/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/XmlTextTokenizer.java index 8a89637d9b39..6b2de76f2d00 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/XmlTextTokenizer.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/XmlTextTokenizer.java @@ -19,11 +19,6 @@ import com.intellij.psi.xml.XmlText; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -/** - * Created by IntelliJ IDEA. - * - * @author shkate@jetbrains.com - */ public class XmlTextTokenizer extends Tokenizer { diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/util/Strings.java b/plugins/spellchecker/src/com/intellij/spellchecker/util/Strings.java index 306ff3d17b5f..008d5ba10755 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/util/Strings.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/util/Strings.java @@ -17,9 +17,6 @@ package com.intellij.spellchecker.util; import java.util.List; -/** - * Text utility. - */ public final class Strings { private Strings() { } diff --git a/plugins/spellchecker/testData/inspection/groovy/Test.groovy b/plugins/spellchecker/testData/inspection/groovy/Test.groovy index c35a6c90fc75..201b418f1772 100644 --- a/plugins/spellchecker/testData/inspection/groovy/Test.groovy +++ b/plugins/spellchecker/testData/inspection/groovy/Test.groovy @@ -1,2 +1,2 @@ -def abc(String dddd) { +def test(int dddd) { } diff --git a/plugins/spellchecker/testSrc/com/intellij/spellchecker/compress/CompressorTest.java b/plugins/spellchecker/testSrc/com/intellij/spellchecker/compress/CompressorTest.java new file mode 100644 index 000000000000..59da085f0f5e --- /dev/null +++ b/plugins/spellchecker/testSrc/com/intellij/spellchecker/compress/CompressorTest.java @@ -0,0 +1,68 @@ +/* + * Copyright 2000-2010 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.spellchecker.compress; + +import com.intellij.spellchecker.compress.Compressor; +import com.intellij.spellchecker.compress.UnitBitSet; +import junit.framework.TestCase; + +import java.util.BitSet; +import java.util.Random; + +public class CompressorTest extends TestCase { + + public void testCompressorWithDefinedData() { + Compressor compressor = new Compressor(0); + UnitBitSet bs = UnitBitSet.create(1, 3, 5, 10); + final byte[] compressed = compressor.compress(bs); + assertEquals(3, compressed.length); + assertEquals(42, compressed[1]); + assertEquals(8, compressed[2]); + final UnitBitSet restored = compressor.decompress(compressed); + assertEquals(bs, restored); + } + + public void testCompressorWithRandomData() { + Compressor compressor = new Compressor(0); + int max = 10; + for (int i = 0; i < 1000; i++) { + int[] values = getRandoms(1000, max); + final UnitBitSet bitSet = new UnitBitSet(); + for (int value : values) { + bitSet.set(value); + } + final byte[] compressed = compressor.compress(bitSet); + final BitSet decompressed = compressor.decompress(compressed); + boolean check = (bitSet.equals(decompressed)); + if (!check) { + System.out.println("bitSet: " + bitSet); + System.out.println("decompressed: " + decompressed); + compressor.compress(bitSet); + compressor.decompress(compressed); + } + assertEquals(bitSet, decompressed); + } + } + + private static int[] getRandoms(int max, int count) { + int[] result = new int[count]; + for (int i = 0; i < count; i++) { + result[i] = new Random().nextInt(max); + } + return result; + } + +} diff --git a/plugins/spellchecker/testSrc/com/intellij/spellchecker/compress/DictionaryTest.java b/plugins/spellchecker/testSrc/com/intellij/spellchecker/compress/DictionaryTest.java new file mode 100644 index 000000000000..906030852491 --- /dev/null +++ b/plugins/spellchecker/testSrc/com/intellij/spellchecker/compress/DictionaryTest.java @@ -0,0 +1,140 @@ +/* + * Copyright 2000-2010 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.spellchecker.compress; + +import com.intellij.openapi.util.Pair; +import com.intellij.spellchecker.DefaultBundledDictionariesProvider; +import com.intellij.spellchecker.StreamLoader; +import com.intellij.spellchecker.dictionary.Dictionary; +import com.intellij.spellchecker.dictionary.Loader; +import com.intellij.spellchecker.engine.Transformation; +import com.intellij.testFramework.IdeaTestUtil; +import com.intellij.util.Consumer; +import gnu.trove.THashSet; +import junit.framework.TestCase; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +@SuppressWarnings({"JUnitTestCaseWithNonTrivialConstructors"}) +public class DictionaryTest extends TestCase { + + private Dictionary dictionary; + + private Map sizes = new HashMap(); + private Map times = new HashMap(); + private static final String JETBRAINS_DIC = "jetbrains.dic"; + private static final String ENGLISH_DIC = "english.dic"; + + { + sizes.put(JETBRAINS_DIC, 1000); + sizes.put(ENGLISH_DIC, 140000); + } + { + times.put(JETBRAINS_DIC, 100); + times.put(ENGLISH_DIC, 3500); + } + + public void testDictionary() throws IOException { + final String[] names = new String[]{JETBRAINS_DIC,ENGLISH_DIC}; + for (String name : names) { + loadDictionaryTest(name, sizes.get(name)); + loadHalfDictionaryTest(name, 50000); + } + } + + public void loadDictionaryTest(@NotNull final String name, int wordCount) throws IOException { + final Transformation transform = new Transformation(); + final Loader loader = new StreamLoader(DefaultBundledDictionariesProvider.class.getResourceAsStream(name), name); + IdeaTestUtil + .assertTiming("Dictionary load depends on words count. Approximate word count: " + wordCount + ".", times.get(name), new Runnable() { + public void run() { + dictionary = CompressedDictionary.create(loader, transform); + } + }); + + final Set wordsToStoreAndCheck = createWordSets(name, 50000, 1).getFirst(); + IdeaTestUtil.assertTiming("Invoke 'contains' " + wordsToStoreAndCheck.size() + " times", 2000, new Runnable() { + public void run() { + for (String s : wordsToStoreAndCheck) { + assertTrue(dictionary.contains(s)); + } + } + }); + + } + + private static Loader createLoader(final Set words) { + return new Loader() { + public void load(@NotNull Consumer consumer) { + for (String word : words) { + consumer.consume(word); + } + } + + public String getName() { + return "test"; + } + }; + } + + @SuppressWarnings({"unchecked"}) + private static Pair, Set> createWordSets(String name, final int maxCount, final int mod) { + Loader loader = new StreamLoader(DefaultBundledDictionariesProvider.class.getResourceAsStream(name), name); + final Set wordsToStore = new THashSet(); + final Set wordsToCheck = new THashSet(); + final Transformation transform = new Transformation(); + loader.load(new Consumer() { + private int counter = 0; + + public void consume(String s) { + if (counter > maxCount) { + return; + } + String transformed = transform.transform(s); + if (transformed != null) { + + if (counter % mod == 0) { + wordsToStore.add(transformed); + } + else { + wordsToCheck.add(transformed); + } + counter++; + } + } + }); + + return new Pair(wordsToStore, wordsToCheck); + } + + + public static void loadHalfDictionaryTest(final String name, final int maxCount) throws IOException { + final Pair, Set> sets = createWordSets(name, maxCount, 2); + final Loader loader = createLoader(sets.getFirst()); + CompressedDictionary dictionary = CompressedDictionary.create(loader, new Transformation()); + for (String s : sets.getSecond()) { + if (!sets.getFirst().contains(s)) { + assertFalse(dictionary.contains(s)); + } + } + } + + +} diff --git a/plugins/spellchecker/testSrc/com/intellij/spellchecker/compress/EncodeAndCompressTest.java b/plugins/spellchecker/testSrc/com/intellij/spellchecker/compress/EncodeAndCompressTest.java new file mode 100644 index 000000000000..37082acc133b --- /dev/null +++ b/plugins/spellchecker/testSrc/com/intellij/spellchecker/compress/EncodeAndCompressTest.java @@ -0,0 +1,23 @@ +package com.intellij.spellchecker.compress; + +import com.intellij.spellchecker.compress.Compressor; +import com.intellij.spellchecker.compress.Encoder; +import com.intellij.spellchecker.compress.UnitBitSet; +import junit.framework.TestCase; + + +public class EncodeAndCompressTest extends TestCase { + + public void testEncodeAndCompress() { + Encoder encoder = new Encoder(); + Compressor compressor = new Compressor(2); + String word = "example"; + UnitBitSet bs = encoder.encode(word, true); + byte[] compressed = compressor.compress(bs); + final UnitBitSet decompressed = compressor.decompress(compressed); + assertEquals(bs,decompressed); + String restored = encoder.decode(decompressed); + assertEquals(word,restored); + } + +} diff --git a/plugins/spellchecker/testSrc/com/intellij/spellchecker/compress/EncoderTest.java b/plugins/spellchecker/testSrc/com/intellij/spellchecker/compress/EncoderTest.java new file mode 100644 index 000000000000..23bff92b5494 --- /dev/null +++ b/plugins/spellchecker/testSrc/com/intellij/spellchecker/compress/EncoderTest.java @@ -0,0 +1,114 @@ +/* + * Copyright 2000-2010 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.spellchecker.compress; + +import com.intellij.spellchecker.compress.Alphabet; +import com.intellij.spellchecker.compress.Encoder; +import com.intellij.spellchecker.compress.UnitBitSet; +import junit.framework.TestCase; + +public class EncoderTest extends TestCase { + + + public void testSimple() { + Encoder encoder = new Encoder(); + final String wordToTest = "abc"; + final UnitBitSet bitSet = encoder.encode(wordToTest, true); + assertEquals(3, encoder.getAlphabet().getLastIndexUsed()); + assertEquals(3, bitSet.getUnitValue(0)); + assertEquals(1, bitSet.getUnitValue(1)); + assertEquals(1, bitSet.getUnitValue(2)); + assertEquals(2, bitSet.getUnitValue(3)); + assertEquals(3, bitSet.getUnitValue(4)); + + assertEquals(wordToTest, encoder.decode(bitSet)); + } + + + public void testDouble() { + Encoder encoder = new Encoder(); + final String wordToTest = "aaa"; + final UnitBitSet bitSet = encoder.encode(wordToTest, true); + assertEquals(1, encoder.getAlphabet().getLastIndexUsed()); + assertEquals(3, bitSet.getUnitValue(0)); + assertEquals(1, bitSet.getUnitValue(1)); + assertEquals(1, bitSet.getUnitValue(2)); + assertEquals(1, bitSet.getUnitValue(3)); + assertEquals(1, bitSet.getUnitValue(4)); + + assertEquals(wordToTest, encoder.decode(bitSet)); + } + + public void testLetterRepetition() { + Encoder encoder = new Encoder(); + final String wordToTest = "aba"; + final UnitBitSet bitSet = encoder.encode(wordToTest, true); + assertEquals(2, encoder.getAlphabet().getLastIndexUsed()); + assertEquals(3, bitSet.getUnitValue(0)); + assertEquals(1, bitSet.getUnitValue(1)); + assertEquals(1, bitSet.getUnitValue(2)); + assertEquals(2, bitSet.getUnitValue(3)); + assertEquals(1, bitSet.getUnitValue(4)); + + assertEquals(wordToTest, encoder.decode(bitSet)); + } + + public void testReverse() { + Encoder encoder = new Encoder(); + final String wordToTest1 = "abc"; + final UnitBitSet bitSet = encoder.encode(wordToTest1, true); + assertEquals(3, encoder.getAlphabet().getLastIndexUsed()); + assertEquals(3, bitSet.getUnitValue(0)); + assertEquals(1, bitSet.getUnitValue(1)); + assertEquals(1, bitSet.getUnitValue(2)); + assertEquals(2, bitSet.getUnitValue(3)); + assertEquals(3, bitSet.getUnitValue(4)); + + assertEquals(wordToTest1, encoder.decode(bitSet)); + + final String wordToTest2 = "cba"; + final UnitBitSet bitSet2 = encoder.encode(wordToTest2, true); + assertEquals(3, encoder.getAlphabet().getLastIndexUsed()); + assertEquals(3, bitSet2.getUnitValue(0)); + assertEquals(3, bitSet2.getUnitValue(1)); + assertEquals(3, bitSet2.getUnitValue(2)); + assertEquals(2, bitSet2.getUnitValue(3)); + assertEquals(1, bitSet2.getUnitValue(4)); + + assertEquals(wordToTest2, encoder.decode(bitSet2)); + } + + + public void testWithPredefinedAlphabet() { + Encoder encoder = new Encoder(Alphabet.create("abcdefghijklmnopqrst")); + final String wordToTest1 = "asia"; + //letter 'a' will be added at the end + final UnitBitSet bitSet = encoder.encode(wordToTest1, true); + assertEquals(20, encoder.getAlphabet().getLastIndexUsed()); + assertEquals(4, bitSet.getUnitValue(0)); + assertEquals(1, bitSet.getUnitValue(1)); + assertEquals(1, bitSet.getUnitValue(2)); + assertEquals(19, bitSet.getUnitValue(3)); + assertEquals(9, bitSet.getUnitValue(4)); + assertEquals(1, bitSet.getUnitValue(5)); + + assertEquals(wordToTest1, encoder.decode(bitSet)); + + + } + + +} diff --git a/plugins/spellchecker/testSrc/com/intellij/spellchecker/compress/UnitBitSetTests.java b/plugins/spellchecker/testSrc/com/intellij/spellchecker/compress/UnitBitSetTests.java new file mode 100644 index 000000000000..90234b66d753 --- /dev/null +++ b/plugins/spellchecker/testSrc/com/intellij/spellchecker/compress/UnitBitSetTests.java @@ -0,0 +1,62 @@ +package com.intellij.spellchecker.compress; + +import com.intellij.spellchecker.compress.UnitBitSet; +import junit.framework.TestCase; + +public class UnitBitSetTests extends TestCase { + + public void testInc0() { + UnitBitSet bs = UnitBitSet.create(); + bs.inc(); + assertEquals(UnitBitSet.create(0), bs); + } + + public void testInc1() { + UnitBitSet bs = UnitBitSet.create(0); + bs.inc(); + assertEquals(UnitBitSet.create(1), bs); + } + + public void testInc2() { + UnitBitSet bs = UnitBitSet.create(1); + bs.inc(); + assertEquals(UnitBitSet.create(0, 1), bs); + } + + public void testUnitValue() { + int bitsPerUnit = 256; + for (int i = 0; i < bitsPerUnit-1; i++) { + UnitBitSet bs = UnitBitSet.create(bitsPerUnit); + bs.setUnitValue(0, i); + assertEquals(i, bs.getUnitValue(0)); + assertEquals(0, bs.getUnitValue(1)); + } + } + + public void testCreateFromBitSet() { + UnitBitSet bs1 = UnitBitSet.create(1,5,10); + UnitBitSet bs2 = UnitBitSet.create(bs1,7); + assertEquals(bs2,bs1); + } + + public void testMoveLeft() { + UnitBitSet bs1 = UnitBitSet.create(3,true, 1,3,5,10); + bs1.moveLeft(2); + assertEquals(UnitBitSet.create(3,true,4),bs1); + } + + public void testMoveLeft2() { + UnitBitSet bs1 = UnitBitSet.create(10,true, 1,3,5,10); + bs1.moveLeft(2); + assertEquals(UnitBitSet.create(10,true),bs1); + } + + public void testMoveRight() { + UnitBitSet bs1 = UnitBitSet.create(3,true, 1,3,5,10); + bs1.moveRight(2); + assertEquals(UnitBitSet.create(3,true,7,9,11,16),bs1); + } + + + +} diff --git a/plugins/spellchecker/testSrc/com/intellij/spellchecker/inspection/SpellcheckerInspectionTestCase.java b/plugins/spellchecker/testSrc/com/intellij/spellchecker/inspection/SpellcheckerInspectionTestCase.java index a1086228b155..4851a41d26de 100644 --- a/plugins/spellchecker/testSrc/com/intellij/spellchecker/inspection/SpellcheckerInspectionTestCase.java +++ b/plugins/spellchecker/testSrc/com/intellij/spellchecker/inspection/SpellcheckerInspectionTestCase.java @@ -19,6 +19,7 @@ import com.intellij.codeInspection.LocalInspectionTool; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.application.PluginPathManager; import com.intellij.openapi.util.io.FileUtil; + import com.intellij.testFramework.fixtures.CodeInsightFixtureTestCase; import org.jetbrains.annotations.NonNls; diff --git a/plugins/spellchecker/testSrc/com/intellij/spellchecker/inspector/SplitterTest.java b/plugins/spellchecker/testSrc/com/intellij/spellchecker/inspector/SplitterTest.java index b76cb2fcedbc..a0313507b136 100644 --- a/plugins/spellchecker/testSrc/com/intellij/spellchecker/inspector/SplitterTest.java +++ b/plugins/spellchecker/testSrc/com/intellij/spellchecker/inspector/SplitterTest.java @@ -22,6 +22,10 @@ import junit.framework.TestCase; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -81,7 +85,7 @@ public class SplitterTest extends TestCase { } - public void testWordWithApostrophe4() { + public void testWordWithApostrophe4() { String text = "we'll"; List checkAreas = TextSplitter.splitText(text); correctListToCheck(checkAreas, text, new String[]{"we'll"}); @@ -91,64 +95,64 @@ public class SplitterTest extends TestCase { public void testWordWithApostrophe5() { String text = "I'm you're we'll"; List checkAreas = TextSplitter.splitText(text); - correctListToCheck(checkAreas, text, new String[]{"you're","we'll"}); + correctListToCheck(checkAreas, text, new String[]{"you're", "we'll"}); correctIgnored(checkAreas, text, new String[]{"I'm"}); } public void testConstantName() { String text = "TEST_CONSTANT"; List checkAreas = TextSplitter.splitText(text); - correctListToCheck(checkAreas, text, new String[]{"TEST","CONSTANT"}); + correctListToCheck(checkAreas, text, new String[]{"TEST", "CONSTANT"}); correctIgnored(checkAreas, text, new String[]{}); } public void testLongConstantName() { String text = "TEST_VERY_VERY_LONG_AND_COMPLEX_CONSTANT"; List checkAreas = TextSplitter.splitText(text); - correctListToCheck(checkAreas, text, new String[]{"TEST","VERY","VERY","LONG","COMPLEX","CONSTANT"}); + correctListToCheck(checkAreas, text, new String[]{"TEST", "VERY", "VERY", "LONG", "COMPLEX", "CONSTANT"}); correctIgnored(checkAreas, text, new String[]{"AND"}); } - public void testJavaComments() { + public void testJavaComments() { String text = "/*special symbols*/"; List checkAreas = TextSplitter.splitText(text); - correctListToCheck(checkAreas, text, new String[]{"special","symbols"}); + correctListToCheck(checkAreas, text, new String[]{"special", "symbols"}); correctIgnored(checkAreas, text, new String[]{}); } public void testXmlComments() { - String text = ""; - List checkAreas = TextSplitter.splitText(text); - correctListToCheck(checkAreas, text, new String[]{"special","symbols"}); - correctIgnored(checkAreas, text, new String[]{}); - } + String text = ""; + List checkAreas = TextSplitter.splitText(text); + correctListToCheck(checkAreas, text, new String[]{"special", "symbols"}); + correctIgnored(checkAreas, text, new String[]{}); + } public void testCamelCaseInXmlComments() { - String text = ""; - List checkAreas = TextSplitter.splitText(text); - correctListToCheck(checkAreas, text, new String[]{"special","Case","symbols"}); - correctIgnored(checkAreas, text, new String[]{}); - } + String text = ""; + List checkAreas = TextSplitter.splitText(text); + correctListToCheck(checkAreas, text, new String[]{"special", "Case", "symbols"}); + correctIgnored(checkAreas, text, new String[]{}); + } public void testWordsWithNumbers() { - String text = "testCamelCase123"; - List checkAreas = TextSplitter.splitText(text); - correctListToCheck(checkAreas, text, new String[]{"test","Camel","Case"}); - correctIgnored(checkAreas, text, new String[]{}); - } + String text = "testCamelCase123"; + List checkAreas = TextSplitter.splitText(text); + correctListToCheck(checkAreas, text, new String[]{"test", "Camel", "Case"}); + correctIgnored(checkAreas, text, new String[]{}); + } public void testCommentsWithWordsWithNumbers() { String text = ""; List checkAreas = TextSplitter.splitText(text); - correctListToCheck(checkAreas, text, new String[]{"special","Case","symbols"}); + correctListToCheck(checkAreas, text, new String[]{"special", "Case", "symbols"}); correctIgnored(checkAreas, text, new String[]{}); } public void testCommentsWithAbr() { String text = ""; List checkAreas = TextSplitter.splitText(text); - correctListToCheck(checkAreas, text, new String[]{"Test","Class"}); + correctListToCheck(checkAreas, text, new String[]{"Test", "Class"}); correctIgnored(checkAreas, text, new String[]{"JSP"}); } @@ -162,7 +166,7 @@ public class SplitterTest extends TestCase { public void testCommentWithHtml() { String text = ""; List checkAreas = TextSplitter.splitText(text); - correctListToCheck(checkAreas, text, new String[]{"something","here","foooo","barrrr","text", "text"}); + correctListToCheck(checkAreas, text, new String[]{"something", "here", "foooo", "barrrr", "text", "text"}); correctIgnored(checkAreas, text, new String[]{"go"}); } @@ -170,75 +174,123 @@ public class SplitterTest extends TestCase { String text = "test   test"; List checkAreas = TextSplitter.splitText(text); correctListToCheck(checkAreas, text, new String[]{"test", "test"}); - correctIgnored(checkAreas, text, new String[]{" "}); + correctIgnored(checkAreas, text, new String[]{}); + } + + public void testColorUC() { + String text = "#AABBFF"; + List checkAreas = TextSplitter.splitText(text); + correctListToCheck(checkAreas, text, new String[]{}); + correctIgnored(checkAreas, text, new String[]{}); + } + + public void testColorLC() { + String text = "#fff"; + List checkAreas = TextSplitter.splitText(text); + correctListToCheck(checkAreas, text, new String[]{}); + correctIgnored(checkAreas, text, new String[]{}); } public void testTooShort() { String text = "bgColor carLight"; List checkAreas = TextSplitter.splitText(text); correctListToCheck(checkAreas, text, new String[]{"Color", "Light"}); - correctIgnored(checkAreas, text, new String[]{"bg","car"}); + correctIgnored(checkAreas, text, new String[]{"bg", "car"}); } public void testComplex() { - String text = "shkate@gmail.com"; - List checkAreas = TextSplitter.splitText(text); - correctListToCheck(checkAreas, text, new String[]{}); - correctIgnored(checkAreas, text, new String[]{}); - } + String text = "shkate@gmail.com"; + List checkAreas = TextSplitter.splitText(text); + correctListToCheck(checkAreas, text, new String[]{}); + correctIgnored(checkAreas, text, new String[]{}); + } - public void testWordBeforeDelimeter() { + public void testWordBeforeDelimiter() { String text = "badd,"; List checkAreas = TextSplitter.splitText(text); correctListToCheck(checkAreas, text, new String[]{"badd"}); correctIgnored(checkAreas, text, new String[]{}); } - public void testWordAfterDelimeter() { + + public void testWordAfterDelimiter() { String text = ",badd"; List checkAreas = TextSplitter.splitText(text); correctListToCheck(checkAreas, text, new String[]{"badd"}); correctIgnored(checkAreas, text, new String[]{}); } - public void testWordInCapsBeforeDelimeter() { - String text = "BADD,"; - List checkAreas = TextSplitter.splitText(text); - correctListToCheck(checkAreas, text, new String[]{"BADD"}); - correctIgnored(checkAreas, text, new String[]{}); - } - public void testWordInCapsAfterDelimeter() { - String text = ",BADD"; - List checkAreas = TextSplitter.splitText(text); - correctListToCheck(checkAreas, text, new String[]{"BADD"}); - correctIgnored(checkAreas, text, new String[]{}); - } - public void testWordInCapsAfterDelimeter2() { - String text = "BADD;"; - List checkAreas = TextSplitter.splitText(text); - correctListToCheck(checkAreas, text, new String[]{"BADD"}); - correctIgnored(checkAreas, text, new String[]{}); - } - public void testWordInCapsAfterDelimeter3() { - String text = ";BADD;"; - List checkAreas = TextSplitter.splitText(text); - correctListToCheck(checkAreas, text, new String[]{"BADD"}); - correctIgnored(checkAreas, text, new String[]{}); - } + public void testWordInCapsBeforeDelimiter() { + String text = "BADD,"; + List checkAreas = TextSplitter.splitText(text); + correctListToCheck(checkAreas, text, new String[]{"BADD"}); + correctIgnored(checkAreas, text, new String[]{}); + } + + public void testWordInCapsAfterDelimiter() { + String text = ",BADD"; + List checkAreas = TextSplitter.splitText(text); + correctListToCheck(checkAreas, text, new String[]{"BADD"}); + correctIgnored(checkAreas, text, new String[]{}); + } + + public void testWordInCapsAfterDelimiter2() { + String text = "BADD;"; + List checkAreas = TextSplitter.splitText(text); + correctListToCheck(checkAreas, text, new String[]{"BADD"}); + correctIgnored(checkAreas, text, new String[]{}); + } + + public void testWordInCapsAfterDelimiter3() { + String text = ";BADD;"; + List checkAreas = TextSplitter.splitText(text); + correctListToCheck(checkAreas, text, new String[]{"BADD"}); + correctIgnored(checkAreas, text, new String[]{}); + } + + public void testWordWithUmlauts() { + String text = "rechtsb\u00FCndig"; + List checkAreas = TextSplitter.splitText(text); + correctListToCheck(checkAreas, text, new String[]{text}); + correctIgnored(checkAreas, text, new String[]{}); + } - public void testWordWithUmlauts() { - String text = "rechtsb\u00FCndig"; - List checkAreas = TextSplitter.splitText(text); - correctListToCheck(checkAreas, text, new String[]{text}); - correctIgnored(checkAreas, text, new String[]{}); - } - public void testWordUpperCasedWithUmlauts() { - String text = "RECHTSB\u00DCNDIG"; - List checkAreas = TextSplitter.splitText(text); - correctListToCheck(checkAreas, text, new String[]{text}); - correctIgnored(checkAreas, text, new String[]{}); - } + String text = "RECHTSB\u00DCNDIG"; + List checkAreas = TextSplitter.splitText(text); + correctListToCheck(checkAreas, text, new String[]{text}); + correctIgnored(checkAreas, text, new String[]{}); + } + + /* public void testWordsWithDots() { + String text = "properties.test.properties"; + List checkAreas = TextSplitter.splitText(text); + correctListToCheck(checkAreas, text, new String[]{"properties", "test", "properties"}); + correctIgnored(checkAreas, text, new String[]{}); + }*/ + + public void testWordUpperCasedWithUmlautsInTheBeginning() { + String text = "\u00DCNDIG"; + List checkAreas = TextSplitter.splitText(text); + correctListToCheck(checkAreas, text, new String[]{text}); + correctIgnored(checkAreas, text, new String[]{}); + } + + public void testNoWordsInHugeBinaryFile() { + final InputStream stream = SplitterTest.class.getResourceAsStream("crt.txt"); + + String text = convertStreamToString(stream); + List checkAreas = TextSplitter.splitText(text); + correctListToCheck(checkAreas, text, new String[]{}); + } + + public void testTCData() { + final InputStream stream = SplitterTest.class.getResourceAsStream("contents.txt"); + String text = convertStreamToString(stream); + List checkAreas = TextSplitter.splitText(text); + List words = wordsToCheck(checkAreas, text); + assertNull(words); + } @Nullable @@ -254,7 +306,7 @@ public class SplitterTest extends TestCase { } - @Nullable + @Nullable private static List wordsToIgnore(List toCheck, String text) { if (text == null || toCheck == null) return null; List words = new ArrayList(); @@ -275,7 +327,7 @@ public class SplitterTest extends TestCase { Assert.assertNotNull(words); Assert.assertEquals(expected.length, words.size()); List expectedWords = Arrays.asList(expected); - Assert.assertEquals( expectedWords,words); + Assert.assertEquals(expectedWords, words); } } @@ -288,7 +340,36 @@ public class SplitterTest extends TestCase { Assert.assertNotNull(words); Assert.assertEquals(expected.length, words.size()); List expectedWords = Arrays.asList(expected); - Assert.assertEquals( expectedWords,words); + Assert.assertEquals(expectedWords, words); + } + } + + private String convertStreamToString(InputStream is) { + if (is != null) { + StringBuilder sb = new StringBuilder(); + String line; + + try { + BufferedReader reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); + while ((line = reader.readLine()) != null) { + sb.append(line).append("\n"); + } + } + catch (Exception e) { + throw new RuntimeException(e); + } + finally { + try { + is.close(); + } + catch (IOException ignore) { + + } + } + return sb.toString(); + } + else { + return ""; } } } diff --git a/plugins/spellchecker/testSrc/com/intellij/spellchecker/inspector/contents.txt b/plugins/spellchecker/testSrc/com/intellij/spellchecker/inspector/contents.txt new file mode 100644 index 000000000000..bde7c70d3d06 Binary files /dev/null and b/plugins/spellchecker/testSrc/com/intellij/spellchecker/inspector/contents.txt differ diff --git a/plugins/spellchecker/testSrc/com/intellij/spellchecker/inspector/crt.txt b/plugins/spellchecker/testSrc/com/intellij/spellchecker/inspector/crt.txt new file mode 100644 index 000000000000..03c3883d613f Binary files /dev/null and b/plugins/spellchecker/testSrc/com/intellij/spellchecker/inspector/crt.txt differ diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnCopiesRefreshManager.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnCopiesRefreshManager.java index 7d05232726f5..8610983764e6 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnCopiesRefreshManager.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnCopiesRefreshManager.java @@ -80,12 +80,12 @@ public class SvnCopiesRefreshManager { semaphore.up(); } }; + semaphore.down(); if (isOnlyInit) { myRequestMerger.ensureInitialization(waiter); } else { myRequestMerger.waitRefresh(waiter); } - semaphore.down(); while (true) { if (semaphore.waitFor(ourQueryInterval)) break; if (pi != null) { diff --git a/plugins/testng/src/com/theoryinpractice/testng/TestNGReferenceContributor.java b/plugins/testng/src/com/theoryinpractice/testng/TestNGReferenceContributor.java index 4a63ce22c848..930feff3bbbe 100644 --- a/plugins/testng/src/com/theoryinpractice/testng/TestNGReferenceContributor.java +++ b/plugins/testng/src/com/theoryinpractice/testng/TestNGReferenceContributor.java @@ -109,6 +109,7 @@ public class TestNGReferenceContributor extends PsiReferenceContributor { return null; } + @NotNull public Object[] getVariants() { final List list = new ArrayList(); final PsiClass cls = PsiUtil.getTopLevelClass(getElement()); @@ -159,6 +160,7 @@ public class TestNGReferenceContributor extends PsiReferenceContributor { return null; } + @NotNull public Object[] getVariants() { List list = new ArrayList(); PsiClass cls = PsiUtil.getTopLevelClass(getElement()); @@ -189,6 +191,7 @@ public class TestNGReferenceContributor extends PsiReferenceContributor { return null; } + @NotNull public Object[] getVariants() { List list = new ArrayList(); diff --git a/plugins/ui-designer/src/com/intellij/uiDesigner/binding/ReferenceInForm.java b/plugins/ui-designer/src/com/intellij/uiDesigner/binding/ReferenceInForm.java index 0a62834a3ded..3e94d99bad1c 100644 --- a/plugins/ui-designer/src/com/intellij/uiDesigner/binding/ReferenceInForm.java +++ b/plugins/ui-designer/src/com/intellij/uiDesigner/binding/ReferenceInForm.java @@ -25,6 +25,7 @@ import com.intellij.psi.PsiPlainTextFile; import com.intellij.psi.PsiReference; import com.intellij.util.ArrayUtil; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; /** * @author Eugene Zhuravlev @@ -75,6 +76,7 @@ public abstract class ReferenceInForm implements PsiReference { return resolve() == element; } + @NotNull public Object[] getVariants() { return ArrayUtil.EMPTY_OBJECT_ARRAY; } diff --git a/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/psi/impl/PrefixReferenceImpl.java b/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/psi/impl/PrefixReferenceImpl.java index 6d4405a33751..a4f86fddd633 100644 --- a/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/psi/impl/PrefixReferenceImpl.java +++ b/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/psi/impl/PrefixReferenceImpl.java @@ -22,6 +22,7 @@ import org.intellij.lang.xpath.psi.QNameElement; import com.intellij.lang.ASTNode; import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; class PrefixReferenceImpl extends ReferenceBase implements PrefixReference { @@ -45,6 +46,7 @@ class PrefixReferenceImpl extends ReferenceBase implements PrefixReference { return element.equals(resolve()); } + @NotNull public Object[] getVariants() { // handled in XPathCompletionData return EMPTY_ARRAY; @@ -69,4 +71,4 @@ class PrefixReferenceImpl extends ReferenceBase implements PrefixReference { final String prefix = getCanonicalText(); return !namespaceContext.getKnownPrefixes(myProvider.getContextElement()).contains(prefix); } -} \ No newline at end of file +} diff --git a/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/psi/impl/ReferenceBase.java b/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/psi/impl/ReferenceBase.java index c2887c2f3ddf..877e17e1010d 100644 --- a/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/psi/impl/ReferenceBase.java +++ b/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/psi/impl/ReferenceBase.java @@ -65,6 +65,7 @@ public abstract class ReferenceBase implements PsiReference { return Comparing.equal(resolve(), element); } + @NotNull public abstract Object[] getVariants(); public boolean isSoft() { diff --git a/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/psi/impl/XPathFunctionCallImpl.java b/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/psi/impl/XPathFunctionCallImpl.java index 43e3035dfa52..5be6c8dc1c40 100644 --- a/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/psi/impl/XPathFunctionCallImpl.java +++ b/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/psi/impl/XPathFunctionCallImpl.java @@ -156,6 +156,7 @@ public class XPathFunctionCallImpl extends XPathElementImpl implements XPathFunc } } + @NotNull public Object[] getVariants() { return EMPTY_ARRAY; } diff --git a/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/psi/impl/XPathNodeTestImpl.java b/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/psi/impl/XPathNodeTestImpl.java index 952a4510ff74..17916be11749 100644 --- a/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/psi/impl/XPathNodeTestImpl.java +++ b/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/psi/impl/XPathNodeTestImpl.java @@ -110,6 +110,7 @@ public class XPathNodeTestImpl extends XPathElementImpl implements XPathNodeTest super(element, nameNode); } + @NotNull public Object[] getVariants() { // handled in XPathCompletionData return EMPTY_ARRAY; diff --git a/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/psi/impl/XPathVariableReferenceImpl.java b/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/psi/impl/XPathVariableReferenceImpl.java index 261c7479b3bd..580f738e5047 100644 --- a/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/psi/impl/XPathVariableReferenceImpl.java +++ b/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/psi/impl/XPathVariableReferenceImpl.java @@ -133,6 +133,7 @@ public class XPathVariableReferenceImpl extends XPathElementImpl implements XPat return false; } + @NotNull public Object[] getVariants() { return ArrayUtil.EMPTY_OBJECT_ARRAY; } diff --git a/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/impl/references/AttributeReference.java b/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/impl/references/AttributeReference.java index df35fc89239a..2f60e3559541 100644 --- a/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/impl/references/AttributeReference.java +++ b/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/impl/references/AttributeReference.java @@ -33,6 +33,7 @@ class AttributeReference extends SimpleAttributeReference { mySoft = soft; } + @NotNull public Object[] getVariants() { return ResolveUtil.collect(myMatcher.variantMatcher()); } diff --git a/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/impl/references/ExternalResourceReference.java b/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/impl/references/ExternalResourceReference.java index c633ad6fd1e9..3548f3f84302 100644 --- a/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/impl/references/ExternalResourceReference.java +++ b/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/impl/references/ExternalResourceReference.java @@ -102,6 +102,7 @@ class ExternalResourceReference implements PsiReference, QuickFixProvider implements DomFileElement< getRootElement().accept(visitor); } - public T getUserData(Key key) { + public T getUserData(@NotNull Key key) { return (T)myUserData.get(key); } - public void putUserData(Key key, T value) { + public void putUserData(@NotNull Key key, T value) { myUserData.put(key, value); } diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/GenericDomValueReference.java b/xml/dom-impl/src/com/intellij/util/xml/impl/GenericDomValueReference.java index 4877c1bbdef5..d01679d80738 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/impl/GenericDomValueReference.java +++ b/xml/dom-impl/src/com/intellij/util/xml/impl/GenericDomValueReference.java @@ -177,6 +177,7 @@ public class GenericDomValueReference extends PsiReferenceBase im return null; } + @NotNull public Object[] getVariants() { final Converter converter = getConverter(); if (converter instanceof EnumConverter || converter == ResolvingConverter.BOOLEAN_CONVERTER) { diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/GenericValueReferenceProvider.java b/xml/dom-impl/src/com/intellij/util/xml/impl/GenericValueReferenceProvider.java index 4a9a36133de6..9fea4de86318 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/impl/GenericValueReferenceProvider.java +++ b/xml/dom-impl/src/com/intellij/util/xml/impl/GenericValueReferenceProvider.java @@ -172,6 +172,7 @@ public class GenericValueReferenceProvider extends PsiReferenceProvider { if (ReflectionCache.isAssignable(Integer.class, clazz)) { return new PsiReference[]{new GenericDomValueReference((GenericDomValue)domValue) { + @NotNull public Object[] getVariants() { return new Object[]{"0"}; } diff --git a/xml/dom-openapi/src/com/intellij/util/xml/converters/DelimitedListConverter.java b/xml/dom-openapi/src/com/intellij/util/xml/converters/DelimitedListConverter.java index 8f490a2d9054..e8b12e646afb 100644 --- a/xml/dom-openapi/src/com/intellij/util/xml/converters/DelimitedListConverter.java +++ b/xml/dom-openapi/src/com/intellij/util/xml/converters/DelimitedListConverter.java @@ -185,6 +185,7 @@ public abstract class DelimitedListConverter extends ResolvingConverter extends ResolvingConverter extends ResolvingConverter impl return multiResolveReference(convertString(value, myContext), myContext); } + @NotNull public Object[] getVariants() { return getReferenceVariants(myContext, myGenericDomValue, getRangeInElement()); } diff --git a/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/XmlUnboundNsPrefixInspection.java b/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/XmlUnboundNsPrefixInspection.java index 6f7aa860e471..06c943ab7106 100644 --- a/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/XmlUnboundNsPrefixInspection.java +++ b/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/XmlUnboundNsPrefixInspection.java @@ -68,7 +68,7 @@ public class XmlUnboundNsPrefixInspection extends XmlSuppressableInspectionTool if (parent instanceof XmlTag && !(token.getNextSibling() instanceof OuterLanguageElement)) { XmlTag tag = (XmlTag)parent; - checkUnboundNamespacePrefix(tag, tag, tag.getNamespacePrefix(), token, holder); + checkUnboundNamespacePrefix(tag, tag, tag.getNamespacePrefix(), token, holder, isOnTheFly); } } } @@ -94,13 +94,13 @@ public class XmlUnboundNsPrefixInspection extends XmlSuppressableInspectionTool final String name = attribute.getName(); - checkUnboundNamespacePrefix(attribute, tag, XmlUtil.findPrefixByQualifiedName(name), null, holder); + checkUnboundNamespacePrefix(attribute, tag, XmlUtil.findPrefixByQualifiedName(name), null, holder, isOnTheFly); } }; } private static void checkUnboundNamespacePrefix(final XmlElement element, final XmlTag context, String namespacePrefix, final XmlToken token, - final ProblemsHolder holder) { + final ProblemsHolder holder, boolean withFixes) { if (namespacePrefix.length() == 0 && (!(element instanceof XmlTag) || !(element.getParent() instanceof XmlDocument)) || XML.equals(namespacePrefix)) { @@ -125,7 +125,8 @@ public class XmlUnboundNsPrefixInspection extends XmlSuppressableInspectionTool if (namespacePrefix.length() == 0) { final XmlTag tag = (XmlTag)element; if (!XmlUtil.JSP_URI.equals(tag.getNamespace())) { - reportTagProblem(tag, localizedMessage, null, ProblemHighlightType.INFORMATION, new CreateNSDeclarationIntentionFix(context, namespacePrefix, token), + reportTagProblem(tag, localizedMessage, null, ProblemHighlightType.INFORMATION, + withFixes ? new CreateNSDeclarationIntentionFix(context, namespacePrefix, token):null, holder); } return; @@ -136,7 +137,7 @@ public class XmlUnboundNsPrefixInspection extends XmlSuppressableInspectionTool final HighlightInfoType infoType = extension.getHighlightInfoType(containingFile); final ProblemHighlightType highlightType = infoType == HighlightInfoType.ERROR ? ProblemHighlightType.ERROR : ProblemHighlightType.LIKE_UNKNOWN_SYMBOL; if (element instanceof XmlTag) { - final CreateNSDeclarationIntentionFix fix = new CreateNSDeclarationIntentionFix(context, namespacePrefix, token); + final CreateNSDeclarationIntentionFix fix = withFixes ? new CreateNSDeclarationIntentionFix(context, namespacePrefix, token):null; reportTagProblem(element, localizedMessage, range, highlightType, fix, holder); } else { holder.registerProblem(element, localizedMessage, highlightType, range); diff --git a/xml/impl/src/com/intellij/codeInsight/template/XmlZenCodingTemplate.java b/xml/impl/src/com/intellij/codeInsight/template/XmlZenCodingTemplate.java deleted file mode 100644 index 8da184ae3d46..000000000000 --- a/xml/impl/src/com/intellij/codeInsight/template/XmlZenCodingTemplate.java +++ /dev/null @@ -1,707 +0,0 @@ -/* - * Copyright 2000-2010 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.codeInsight.template; - -import com.intellij.application.options.editor.WebEditorOptions; -import com.intellij.codeInsight.template.impl.TemplateImpl; -import com.intellij.lang.xml.XMLLanguage; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.util.Pair; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.psi.xml.XmlTag; -import com.intellij.psi.xml.XmlToken; -import com.intellij.psi.xml.XmlTokenType; -import com.intellij.util.containers.HashMap; -import com.intellij.util.containers.HashSet; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.*; - -/** - * @author Eugene.Kudelevsky - */ -public class XmlZenCodingTemplate implements CustomLiveTemplate { - private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.template.XmlZenCodingTemplate"); - - private static final String ATTRS = "ATTRS"; - - private static final String OPERATIONS = ">+*"; - private static final String SELECTORS = ".#["; - private static final char MARKER = '$'; - private static final String ID = "id"; - private static final String CLASS = "class"; - private static final String NUMBER_IN_ITERATION_PLACE_HOLDER = "$"; - - private static enum MyState { - OPERATION, WORD, AFTER_NUMBER, NUMBER - } - - private static class MyToken { - } - - private static class MyMarkerToken extends MyToken { - } - - private static class MyTemplateToken extends MyToken { - final String myKey; - final List> myAttribute2Value; - - MyTemplateToken(String key, List> attribute2value) { - myKey = key; - myAttribute2Value = attribute2value; - } - } - - private static class MyNumberToken extends MyToken { - final int myNumber; - - MyNumberToken(int number) { - myNumber = number; - } - } - - private static class MyOperationToken extends MyToken { - final char mySign; - - MyOperationToken(char sign) { - mySign = sign; - } - } - - private static int parseNonNegativeInt(@NotNull String s) { - try { - return Integer.parseInt(s); - } - catch (Throwable ignored) { - } - return -1; - } - - private static String getPrefix(@NotNull String templateKey) { - for (int i = 0, n = templateKey.length(); i < n; i++) { - char c = templateKey.charAt(i); - if (SELECTORS.indexOf(c) >= 0) { - return templateKey.substring(0, i); - } - } - return templateKey; - } - - @Nullable - private static Pair parseAttrNameAndValue(@NotNull String text) { - int eqIndex = text.indexOf('='); - if (eqIndex > 0) { - return new Pair(text.substring(0, eqIndex), text.substring(eqIndex + 1)); - } - return null; - } - - @Nullable - private static MyTemplateToken parseSelectors(@NotNull String text) { - String templateKey = null; - List> attributes = new ArrayList>(); - Set definedAttrs = new HashSet(); - final List classes = new ArrayList(); - StringBuilder builder = new StringBuilder(); - char lastDelim = 0; - text += MARKER; - int classAttrPosition = -1; - for (int i = 0, n = text.length(); i < n; i++) { - char c = text.charAt(i); - if (c == '#' || c == '.' || c == '[' || c == ']' || i == n - 1) { - if (c != ']') { - switch (lastDelim) { - case 0: - templateKey = builder.toString(); - break; - case '#': - if (!definedAttrs.add(ID)) { - return null; - } - attributes.add(new Pair(ID, builder.toString())); - break; - case '.': - if (builder.length() <= 0) { - return null; - } - if (classAttrPosition < 0) { - classAttrPosition = attributes.size(); - } - classes.add(builder.toString()); - break; - case ']': - if (builder.length() > 0) { - return null; - } - break; - default: - return null; - } - } - else if (lastDelim != '[') { - return null; - } - else { - Pair pair = parseAttrNameAndValue(builder.toString()); - if (pair == null || !definedAttrs.add(pair.first)) { - return null; - } - attributes.add(pair); - } - lastDelim = c; - builder = new StringBuilder(); - } - else { - builder.append(c); - } - } - if (classes.size() > 0) { - if (definedAttrs.contains(CLASS)) { - return null; - } - StringBuilder classesAttrValue = new StringBuilder(); - for (int i = 0; i < classes.size(); i++) { - classesAttrValue.append(classes.get(i)); - if (i < classes.size() - 1) { - classesAttrValue.append(' '); - } - } - assert classAttrPosition >= 0; - attributes.add(classAttrPosition, new Pair(CLASS, classesAttrValue.toString())); - } - return new MyTemplateToken(templateKey, attributes); - } - - @Nullable - private static List parse(@NotNull String text, @NotNull CustomTemplateCallback callback) { - text += MARKER; - StringBuilder templateKeyBuilder = new StringBuilder(); - List result = new ArrayList(); - for (int i = 0, n = text.length(); i < n; i++) { - char c = text.charAt(i); - if (i == n - 1 || (i < n - 2 && OPERATIONS.indexOf(c) >= 0)) { - String key = templateKeyBuilder.toString(); - templateKeyBuilder = new StringBuilder(); - int num = parseNonNegativeInt(key); - if (num > 0) { - result.add(new MyNumberToken(num)); - } - else { - if (key.length() == 0) { - return null; - } - String prefix = getPrefix(key); - if (!callback.isLiveTemplateApplicable(prefix) && prefix.indexOf('<') >= 0 /*&& !XML11Char.isXML11ValidQName(prefix)*/) { - return null; - } - MyTemplateToken token = parseSelectors(key); - if (token == null) { - return null; - } - result.add(token); - } - result.add(i < n - 1 ? new MyOperationToken(c) : new MyMarkerToken()); - } - else if (!Character.isWhitespace(c)) { - templateKeyBuilder.append(c); - } - else { - return null; - } - } - return result; - } - - private static boolean check(@NotNull Collection tokens) { - MyState state = MyState.WORD; - for (MyToken token : tokens) { - if (token instanceof MyMarkerToken) { - break; - } - switch (state) { - case OPERATION: - if (token instanceof MyOperationToken) { - state = ((MyOperationToken)token).mySign == '*' ? MyState.NUMBER : MyState.WORD; - } - else { - return false; - } - break; - case WORD: - if (token instanceof MyTemplateToken) { - state = MyState.OPERATION; - } - else { - return false; - } - break; - case NUMBER: - if (token instanceof MyNumberToken) { - state = MyState.AFTER_NUMBER; - } - else { - return false; - } - break; - case AFTER_NUMBER: - if (token instanceof MyOperationToken && ((MyOperationToken)token).mySign != '*') { - state = MyState.WORD; - } - else { - return false; - } - break; - } - } - return state == MyState.OPERATION || state == MyState.AFTER_NUMBER; - } - - private static String computeKey(Editor editor, int startOffset) { - int offset = editor.getCaretModel().getOffset(); - String s = editor.getDocument().getCharsSequence().subSequence(startOffset, offset).toString(); - int index = 0; - while (index < s.length() && Character.isWhitespace(s.charAt(index))) { - index++; - } - String key = s.substring(index); - int lastWhitespaceIndex = -1; - for (int i = 0; i < key.length(); i++) { - if (Character.isWhitespace(key.charAt(i))) { - lastWhitespaceIndex = i; - } - } - if (lastWhitespaceIndex >= 0 && lastWhitespaceIndex < key.length() - 1) { - return key.substring(lastWhitespaceIndex + 1); - } - return key; - } - - public String computeTemplateKey(@NotNull CustomTemplateCallback callback) { - Editor editor = callback.getEditor(); - int offset = callback.getOffset(); - PsiElement element = callback.getFile().findElementAt(offset > 0 ? offset - 1 : offset); - int line = editor.getCaretModel().getLogicalPosition().line; - int lineStart = editor.getDocument().getLineStartOffset(line); - int parentStart; - do { - parentStart = element != null ? element.getTextRange().getStartOffset() : 0; - int startOffset = parentStart > lineStart ? parentStart : lineStart; - String key = computeKey(editor, startOffset); - List tokens = parse(key, callback); - if (tokens != null && check(tokens)) { - if (tokens.size() == 2) { - MyToken token = tokens.get(0); - if (token instanceof MyTemplateToken) { - if (key.equals(((MyTemplateToken)token).myKey) && callback.isLiveTemplateApplicable(key)) { - // do not activate only live template - return null; - } - } - } - return key; - } - if (element != null) { - element = element.getParent(); - } - } - while (element != null && parentStart > lineStart); - return null; - } - - public boolean isApplicable(PsiFile file, int offset, boolean selection) { - WebEditorOptions webEditorOptions = WebEditorOptions.getInstance(); - if (!webEditorOptions.isZenCodingEnabled()) { - return false; - } - if (file.getLanguage() instanceof XMLLanguage) { - PsiElement element = file.findElementAt(offset > 0 ? offset - 1 : offset); - if (element == null || element.getLanguage() instanceof XMLLanguage) { - return true; - } - } - return false; - } - - public void expand(String key, @NotNull CustomTemplateCallback callback, @Nullable TemplateInvokationListener listener) { - List tokens = parse(key, callback); - assert tokens != null; - MyInterpreter interpreter = new MyInterpreter(tokens, callback, MyState.WORD, listener); - interpreter.invoke(0); - } - - public void wrap(String selection, @NotNull CustomTemplateCallback callback, @Nullable TemplateInvokationListener listener) { - } - - private static void fail() { - LOG.error("Input string was checked incorrectly during isApplicable() invokation"); - } - - @NotNull - private static String buildAttributesString(List> attribute2value, int numberInIteration) { - StringBuilder result = new StringBuilder(); - for (Iterator> it = attribute2value.iterator(); it.hasNext();) { - Pair pair = it.next(); - String name = pair.first; - String value = pair.second.replace(NUMBER_IN_ITERATION_PLACE_HOLDER, Integer.toString(numberInIteration + 1)); - result.append(name).append("=\"").append(value).append('"'); - if (it.hasNext()) { - result.append(' '); - } - } - return result.toString(); - } - - private static boolean invokeTemplate(MyTemplateToken token, - final CustomTemplateCallback callback, - final TemplateInvokationListener listener, - int numberInIteration) { - String attributes = buildAttributesString(token.myAttribute2Value, numberInIteration); - attributes = attributes.length() > 0 ? ' ' + attributes : null; - Map predefinedValues = null; - if (attributes != null) { - predefinedValues = new HashMap(); - predefinedValues.put(ATTRS, attributes); - } - if (callback.isLiveTemplateApplicable(token.myKey)) { - if (attributes != null && !callback.templateContainsVars(token.myKey, ATTRS)) { - TemplateImpl newTemplate = generateTemplateWithAttributes(token.myKey, attributes, callback); - if (newTemplate != null) { - return callback.startTemplate(newTemplate, predefinedValues, listener); - } - } - return callback.startTemplate(token.myKey, predefinedValues, listener); - } - else { - TemplateImpl template = new TemplateImpl("", ""); - template.addTextSegment('<' + token.myKey); - if (attributes != null) { - template.addVariable(ATTRS, "", "", false); - template.addVariableSegment(ATTRS); - } - template.addTextSegment(">"); - template.addVariableSegment(TemplateImpl.END); - template.addTextSegment(""); - template.setToReformat(true); - return callback.startTemplate(template, predefinedValues, listener); - } - } - - private static int findPlaceToInsertAttrs(@NotNull TemplateImpl template) { - String s = template.getString(); - if (s.length() > 0) { - if (s.charAt(0) != '<') { - return -1; - } - int i = 1; - while (i < s.length() && !Character.isWhitespace(s.charAt(i)) && s.charAt(i) != '>') { - i++; - } - if (i == 1) { - return -1; - } - if (s.indexOf('>', i) >= i) { - return i; - } - } - return -1; - } - - @Nullable - private static TemplateImpl generateTemplateWithAttributes(String key, String attributes, CustomTemplateCallback callback) { - TemplateImpl template = callback.findApplicableTemplate(key); - assert template != null; - String templateString = template.getString(); - int offset = findPlaceToInsertAttrs(template); - if (offset >= 0) { - String newTemplateString = templateString.substring(0, offset) + attributes + templateString.substring(offset); - TemplateImpl newTemplate = template.copy(); - newTemplate.setString(newTemplateString); - return newTemplate; - } - return null; - } - - /*private static boolean hasClosingTag(CharSequence text, CharSequence tagName, int offset, int rightBound) { - if (offset + 1 < text.length() && text.charAt(offset) == '<' && text.charAt(offset + 1) == '/') { - CharSequence closingTagName = parseTagName(text, offset + 2, rightBound); - if (tagName.equals(closingTagName)) { - return true; - } - } - return false; - }*/ - - private class MyInterpreter { - private final List myTokens; - private final CustomTemplateCallback myCallback; - private final TemplateInvokationListener myListener; - private MyState myState; - - private MyInterpreter(List tokens, - CustomTemplateCallback callback, - MyState initialState, - TemplateInvokationListener listener) { - myTokens = tokens; - myCallback = callback; - myListener = listener; - myState = initialState; - } - - private void finish(boolean inSeparateEvent) { - myCallback.gotoEndOffset(); - if (myListener != null) { - myListener.finished(inSeparateEvent); - } - } - - private void gotoChild(Object templateBoundsKey) { - int startOfTemplate = myCallback.getStartOfTemplate(templateBoundsKey); - int endOfTemplate = myCallback.getEndOfTemplate(templateBoundsKey); - Editor editor = myCallback.getEditor(); - int offset = myCallback.getOffset(); - - PsiFile file = myCallback.getFile(); - - PsiElement element = file.findElementAt(offset); - if (element instanceof XmlToken && ((XmlToken)element).getTokenType() == XmlTokenType.XML_END_TAG_START) { - return; - } - - int newOffset = -1; - XmlTag tag = PsiTreeUtil.findElementOfClassAtRange(file, startOfTemplate, endOfTemplate, XmlTag.class); - if (tag != null) { - for (PsiElement child : tag.getChildren()) { - if (child instanceof XmlToken && ((XmlToken)child).getTokenType() == XmlTokenType.XML_END_TAG_START) { - newOffset = child.getTextOffset(); - } - } - } - - if (newOffset >= 0) { - myCallback.fixEndOffset(); - editor.getCaretModel().moveToOffset(newOffset); - } - - /*CharSequence tagName = getPrecedingTagName(text, offset, startOfTemplate); - if (tagName != null) { - *//*if (!hasClosingTag(text, tagName, offset, endOfTemplate)) { - document.insertString(offset, "'); - }*//* - } - else if (offset != endOfTemplate) { - tagName = getPrecedingTagName(text, endOfTemplate, startOfTemplate); - if (tagName != null) { - *//*fixEndOffset(); - document.insertString(endOfTemplate, "');*//* - editor.getCaretModel().moveToOffset(endOfTemplate); - } - }*/ - } - - public boolean invoke(int startIndex) { - final int n = myTokens.size(); - MyTemplateToken templateToken = null; - int number = -1; - for (int i = startIndex; i < n; i++) { - final int finalI = i; - MyToken token = myTokens.get(i); - switch (myState) { - case OPERATION: - if (templateToken != null) { - if (token instanceof MyMarkerToken || token instanceof MyOperationToken) { - final char sign = token instanceof MyOperationToken ? ((MyOperationToken)token).mySign : MARKER; - if (sign == MARKER || sign == '+') { - final Object key = new Object(); - myCallback.fixStartOfTemplate(key); - TemplateInvokationListener listener = new TemplateInvokationListener() { - public void finished(boolean inSeparateEvent) { - myState = MyState.WORD; - if (myCallback.getOffset() != myCallback.getEndOfTemplate(key)) { - myCallback.fixEndOffset(); - } - if (sign == '+') { - myCallback.gotoEndOfTemplate(key); - } - if (inSeparateEvent) { - invoke(finalI + 1); - } - } - }; - if (!invokeTemplate(templateToken, myCallback, listener, -1)) { - return false; - } - templateToken = null; - } - else if (sign == '>') { - if (!startTemplateAndGotoChild(templateToken, finalI)) { - return false; - } - templateToken = null; - } - else if (sign == '*') { - myState = MyState.NUMBER; - } - } - else { - fail(); - } - } - break; - case WORD: - if (token instanceof MyTemplateToken) { - templateToken = ((MyTemplateToken)token); - myState = MyState.OPERATION; - } - else { - fail(); - } - break; - case NUMBER: - if (token instanceof MyNumberToken) { - number = ((MyNumberToken)token).myNumber; - myState = MyState.AFTER_NUMBER; - } - else { - fail(); - } - break; - case AFTER_NUMBER: - if (token instanceof MyMarkerToken || token instanceof MyOperationToken) { - char sign = token instanceof MyOperationToken ? ((MyOperationToken)token).mySign : MARKER; - if (sign == MARKER || sign == '+') { - if (!invokeTemplateSeveralTimes(templateToken, 0, number, finalI)) { - return false; - } - templateToken = null; - } - else if (number > 1) { - return invokeTemplateAndProcessTail(templateToken, 0, number, i + 1); - } - else { - assert number == 1; - if (!startTemplateAndGotoChild(templateToken, finalI)) { - return false; - } - templateToken = null; - } - myState = MyState.WORD; - } - else { - fail(); - } - break; - } - } - finish(startIndex == n); - return true; - } - - private boolean startTemplateAndGotoChild(MyTemplateToken templateToken, final int index) { - final Object key = new Object(); - myCallback.fixStartOfTemplate(key); - TemplateInvokationListener listener = new TemplateInvokationListener() { - public void finished(boolean inSeparateEvent) { - myState = MyState.WORD; - gotoChild(key); - if (inSeparateEvent) { - invoke(index + 1); - } - } - }; - if (!invokeTemplate(templateToken, myCallback, listener, -1)) { - return false; - } - return true; - } - - private boolean invokeTemplateSeveralTimes(final MyTemplateToken templateToken, - final int startIndex, - final int count, - final int globalIndex) { - final Object key = new Object(); - myCallback.fixStartOfTemplate(key); - for (int i = startIndex; i < count; i++) { - final int finalI = i; - TemplateInvokationListener listener = new TemplateInvokationListener() { - public void finished(boolean inSeparateEvent) { - myState = MyState.WORD; - if (myCallback.getOffset() != myCallback.getEndOfTemplate(key)) { - myCallback.fixEndOffset(); - } - myCallback.gotoEndOfTemplate(key); - if (inSeparateEvent) { - if (finalI + 1 < count) { - invokeTemplateSeveralTimes(templateToken, finalI + 1, count, globalIndex); - } - else { - invoke(globalIndex + 1); - } - } - } - }; - if (!invokeTemplate(templateToken, myCallback, listener, i)) { - return false; - } - } - return true; - } - - private boolean invokeTemplateAndProcessTail(final MyTemplateToken templateToken, - final int startIndex, - final int count, - final int tailStart) { - final Object key = new Object(); - myCallback.fixStartOfTemplate(key); - for (int i = startIndex; i < count; i++) { - final int finalI = i; - final boolean[] flag = new boolean[]{false}; - TemplateInvokationListener listener = new TemplateInvokationListener() { - public void finished(boolean inSeparateEvent) { - gotoChild(key); - MyInterpreter interpreter = new MyInterpreter(myTokens, myCallback, MyState.WORD, new TemplateInvokationListener() { - public void finished(boolean inSeparateEvent) { - if (myCallback.getOffset() != myCallback.getEndOfTemplate(key)) { - myCallback.fixEndOffset(); - } - myCallback.gotoEndOfTemplate(key); - if (inSeparateEvent) { - invokeTemplateAndProcessTail(templateToken, finalI + 1, count, tailStart); - } - } - }); - if (interpreter.invoke(tailStart)) { - if (inSeparateEvent) { - invokeTemplateAndProcessTail(templateToken, finalI + 1, count, tailStart); - } - } - else { - flag[0] = true; - } - } - }; - if (!invokeTemplate(templateToken, myCallback, listener, i) || flag[0]) { - return false; - } - } - finish(count == 0); - return true; - } - } -} \ No newline at end of file diff --git a/platform/platform-api/src/com/intellij/openapi/ui/TestableUi.java b/xml/impl/src/com/intellij/codeInsight/template/zencoding/MarkerToken.java similarity index 76% rename from platform/platform-api/src/com/intellij/openapi/ui/TestableUi.java rename to xml/impl/src/com/intellij/codeInsight/template/zencoding/MarkerToken.java index 85aeb7f8c2fc..ba9c9af1b9c8 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/TestableUi.java +++ b/xml/impl/src/com/intellij/codeInsight/template/zencoding/MarkerToken.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2010 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. @@ -13,12 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.intellij.openapi.ui; +package com.intellij.codeInsight.template.zencoding; -import java.util.Map; - -public interface TestableUi { - - void putInfo(Map info); - -} \ No newline at end of file +/** + * @author Eugene.Kudelevsky + */ +class MarkerToken extends Token { +} diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/NumberToken.java b/xml/impl/src/com/intellij/codeInsight/template/zencoding/NumberToken.java new file mode 100644 index 000000000000..4adf4f763c59 --- /dev/null +++ b/xml/impl/src/com/intellij/codeInsight/template/zencoding/NumberToken.java @@ -0,0 +1,27 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.template.zencoding; + +/** + * @author Eugene.Kudelevsky + */ +class NumberToken extends Token { + final int myNumber; + + NumberToken(int number) { + myNumber = number; + } +} diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/OperationToken.java b/xml/impl/src/com/intellij/codeInsight/template/zencoding/OperationToken.java new file mode 100644 index 000000000000..63bb2d343e6c --- /dev/null +++ b/xml/impl/src/com/intellij/codeInsight/template/zencoding/OperationToken.java @@ -0,0 +1,27 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.template.zencoding; + +/** + * @author Eugene.Kudelevsky + */ +class OperationToken extends Token { + final char mySign; + + OperationToken(char sign) { + mySign = sign; + } +} diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/State.java b/xml/impl/src/com/intellij/codeInsight/template/zencoding/State.java new file mode 100644 index 000000000000..23ff80b9cc43 --- /dev/null +++ b/xml/impl/src/com/intellij/codeInsight/template/zencoding/State.java @@ -0,0 +1,23 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.template.zencoding; + +/** + * @author Eugene.Kudelevsky + */ +enum State { + OPERATION, WORD, AFTER_NUMBER, NUMBER +} diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/TemplateToken.java b/xml/impl/src/com/intellij/codeInsight/template/zencoding/TemplateToken.java new file mode 100644 index 000000000000..bb9b2f96b0f6 --- /dev/null +++ b/xml/impl/src/com/intellij/codeInsight/template/zencoding/TemplateToken.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.template.zencoding; + +import com.intellij.codeInsight.template.impl.TemplateImpl; +import com.intellij.openapi.util.Pair; + +import java.util.List; + +/** + * @author Eugene.Kudelevsky + */ +class TemplateToken extends Token { + final String myKey; + final List> myAttribute2Value; + TemplateImpl myTemplate; + + TemplateToken(String key, List> attribute2value) { + myKey = key; + myAttribute2Value = attribute2value; + } +} diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/trie/Action.java b/xml/impl/src/com/intellij/codeInsight/template/zencoding/Token.java similarity index 79% rename from plugins/spellchecker/src/com/intellij/spellchecker/trie/Action.java rename to xml/impl/src/com/intellij/codeInsight/template/zencoding/Token.java index a5b401680802..082b83bf2f16 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/trie/Action.java +++ b/xml/impl/src/com/intellij/codeInsight/template/zencoding/Token.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2010 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. @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.intellij.spellchecker.trie; +package com.intellij.codeInsight.template.zencoding; -public interface Action { - - void run(String entry); +/** + * @author Eugene.Kudelevsky + */ +class Token { } diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/XmlZenCodingInterpreter.java b/xml/impl/src/com/intellij/codeInsight/template/zencoding/XmlZenCodingInterpreter.java new file mode 100644 index 000000000000..eab488d1498d --- /dev/null +++ b/xml/impl/src/com/intellij/codeInsight/template/zencoding/XmlZenCodingInterpreter.java @@ -0,0 +1,439 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.template.zencoding; + +import com.intellij.codeInsight.template.CustomTemplateCallback; +import com.intellij.codeInsight.template.TemplateInvokationListener; +import com.intellij.codeInsight.template.impl.TemplateImpl; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.xml.*; +import com.intellij.util.containers.HashMap; +import com.intellij.util.containers.HashSet; +import com.intellij.util.containers.IntArrayList; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +/** + * @author Eugene.Kudelevsky + */ +class XmlZenCodingInterpreter { + private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.template.zencoding.XmlZenCodingInterpreter"); + private static final String ATTRS = "ATTRS"; + + private final List myTokens; + private final CustomTemplateCallback myCallback; + private final TemplateInvokationListener myListener; + private static final String NUMBER_IN_ITERATION_PLACE_HOLDER = "$"; + private State myState; + + XmlZenCodingInterpreter(List tokens, + CustomTemplateCallback callback, + State initialState, + TemplateInvokationListener listener) { + myTokens = tokens; + myCallback = callback; + myListener = listener; + myState = initialState; + } + + private void finish(boolean inSeparateEvent) { + myCallback.gotoEndOffset(); + if (myListener != null) { + myListener.finished(inSeparateEvent); + } + } + + private void gotoChild(Object templateBoundsKey) { + int startOfTemplate = myCallback.getStartOfTemplate(templateBoundsKey); + int endOfTemplate = myCallback.getEndOfTemplate(templateBoundsKey); + Editor editor = myCallback.getEditor(); + int offset = myCallback.getOffset(); + + PsiFile file = myCallback.getFile(); + + PsiElement element = file.findElementAt(offset); + if (element instanceof XmlToken && ((XmlToken)element).getTokenType() == XmlTokenType.XML_END_TAG_START) { + return; + } + + int newOffset = -1; + XmlTag tag = PsiTreeUtil.findElementOfClassAtRange(file, startOfTemplate, endOfTemplate, XmlTag.class); + if (tag != null) { + for (PsiElement child : tag.getChildren()) { + if (child instanceof XmlToken && ((XmlToken)child).getTokenType() == XmlTokenType.XML_END_TAG_START) { + newOffset = child.getTextOffset(); + } + } + } + + if (newOffset >= 0) { + myCallback.fixEndOffset(); + editor.getCaretModel().moveToOffset(newOffset); + } + + /*CharSequence tagName = getPrecedingTagName(text, offset, startOfTemplate); + if (tagName != null) { + *//*if (!hasClosingTag(text, tagName, offset, endOfTemplate)) { + document.insertString(offset, "'); + }*//* + } + else if (offset != endOfTemplate) { + tagName = getPrecedingTagName(text, endOfTemplate, startOfTemplate); + if (tagName != null) { + *//*fixEndOffset(); + document.insertString(endOfTemplate, "');*//* + editor.getCaretModel().moveToOffset(endOfTemplate); + } + }*/ + } + + public boolean invoke(int startIndex) { + final int n = myTokens.size(); + TemplateToken templateToken = null; + int number = -1; + for (int i = startIndex; i < n; i++) { + final int finalI = i; + Token token = myTokens.get(i); + switch (myState) { + case OPERATION: + if (templateToken != null) { + if (token instanceof MarkerToken || token instanceof OperationToken) { + final char sign = token instanceof OperationToken ? ((OperationToken)token).mySign : XmlZenCodingTemplate.MARKER; + if (sign == XmlZenCodingTemplate.MARKER || sign == '+') { + final Object key = new Object(); + myCallback.fixStartOfTemplate(key); + TemplateInvokationListener listener = new TemplateInvokationListener() { + public void finished(boolean inSeparateEvent) { + myState = State.WORD; + if (myCallback.getOffset() != myCallback.getEndOfTemplate(key)) { + myCallback.fixEndOffset(); + } + if (sign == '+') { + myCallback.gotoEndOfTemplate(key); + } + if (inSeparateEvent) { + invoke(finalI + 1); + } + } + }; + if (!invokeTemplate(templateToken, myCallback, listener, 0)) { + return false; + } + templateToken = null; + } + else if (sign == '>') { + if (!startTemplateAndGotoChild(templateToken, finalI)) { + return false; + } + templateToken = null; + } + else if (sign == '*') { + myState = State.NUMBER; + } + } + else { + fail(); + } + } + break; + case WORD: + if (token instanceof TemplateToken) { + templateToken = ((TemplateToken)token); + myState = State.OPERATION; + } + else { + fail(); + } + break; + case NUMBER: + if (token instanceof NumberToken) { + number = ((NumberToken)token).myNumber; + myState = State.AFTER_NUMBER; + } + else { + fail(); + } + break; + case AFTER_NUMBER: + if (token instanceof MarkerToken || token instanceof OperationToken) { + char sign = token instanceof OperationToken ? ((OperationToken)token).mySign : XmlZenCodingTemplate.MARKER; + if (sign == XmlZenCodingTemplate.MARKER || sign == '+') { + if (!invokeTemplateSeveralTimes(templateToken, 0, number, finalI)) { + return false; + } + templateToken = null; + } + else if (number > 1) { + return invokeTemplateAndProcessTail(templateToken, 0, number, i + 1); + } + else { + assert number == 1; + if (!startTemplateAndGotoChild(templateToken, finalI)) { + return false; + } + templateToken = null; + } + myState = State.WORD; + } + else { + fail(); + } + break; + } + } + finish(startIndex == n); + return true; + } + + private boolean startTemplateAndGotoChild(TemplateToken templateToken, final int index) { + final Object key = new Object(); + myCallback.fixStartOfTemplate(key); + TemplateInvokationListener listener = new TemplateInvokationListener() { + public void finished(boolean inSeparateEvent) { + myState = State.WORD; + gotoChild(key); + if (inSeparateEvent) { + invoke(index + 1); + } + } + }; + if (!invokeTemplate(templateToken, myCallback, listener, 0)) { + return false; + } + return true; + } + + private boolean invokeTemplateSeveralTimes(final TemplateToken templateToken, + final int startIndex, + final int count, + final int globalIndex) { + final Object key = new Object(); + myCallback.fixStartOfTemplate(key); + for (int i = startIndex; i < count; i++) { + final int finalI = i; + TemplateInvokationListener listener = new TemplateInvokationListener() { + public void finished(boolean inSeparateEvent) { + myState = State.WORD; + if (myCallback.getOffset() != myCallback.getEndOfTemplate(key)) { + myCallback.fixEndOffset(); + } + myCallback.gotoEndOfTemplate(key); + if (inSeparateEvent) { + if (finalI + 1 < count) { + invokeTemplateSeveralTimes(templateToken, finalI + 1, count, globalIndex); + } + else { + invoke(globalIndex + 1); + } + } + } + }; + if (!invokeTemplate(templateToken, myCallback, listener, i)) { + return false; + } + } + return true; + } + + private boolean invokeTemplateAndProcessTail(final TemplateToken templateToken, + final int startIndex, + final int count, + final int tailStart) { + final Object key = new Object(); + myCallback.fixStartOfTemplate(key); + for (int i = startIndex; i < count; i++) { + final int finalI = i; + final boolean[] flag = new boolean[]{false}; + TemplateInvokationListener listener = new TemplateInvokationListener() { + public void finished(boolean inSeparateEvent) { + gotoChild(key); + XmlZenCodingInterpreter interpreter = + new XmlZenCodingInterpreter(myTokens, myCallback, State.WORD, new TemplateInvokationListener() { + public void finished(boolean inSeparateEvent) { + if (myCallback.getOffset() != myCallback.getEndOfTemplate(key)) { + myCallback.fixEndOffset(); + } + myCallback.gotoEndOfTemplate(key); + if (inSeparateEvent) { + invokeTemplateAndProcessTail(templateToken, finalI + 1, count, tailStart); + } + } + }); + if (interpreter.invoke(tailStart)) { + if (inSeparateEvent) { + invokeTemplateAndProcessTail(templateToken, finalI + 1, count, tailStart); + } + } + else { + flag[0] = true; + } + } + }; + if (!invokeTemplate(templateToken, myCallback, listener, i) || flag[0]) { + return false; + } + } + finish(count == 0); + return true; + } + + private static boolean containsAttrsVar(TemplateImpl template) { + for (int i = 0; i < template.getVariableCount(); i++) { + String varName = template.getVariableNameAt(i); + if (ATTRS.equals(varName)) { + return true; + } + } + return false; + } + + private static void removeVariablesWhichHasNoSegment(TemplateImpl template) { + Set segments = new HashSet(); + for (int i = 0; i < template.getSegmentsCount(); i++) { + segments.add(template.getSegmentName(i)); + } + IntArrayList varsToRemove = new IntArrayList(); + for (int i = 0; i < template.getVariableCount(); i++) { + String varName = template.getVariableNameAt(i); + if (!segments.contains(varName)) { + varsToRemove.add(i); + } + } + for (int i = 0; i < varsToRemove.size(); i++) { + template.removeVariable(varsToRemove.get(i)); + } + } + + @Nullable + private static Map buildPredefinedValues(List> attribute2value, int numberInIteration) { + StringBuilder result = new StringBuilder(); + for (Iterator> it = attribute2value.iterator(); it.hasNext();) { + Pair pair = it.next(); + String name = pair.first; + String value = getValue(pair, numberInIteration); + result.append(name).append("=\"").append(value).append('"'); + if (it.hasNext()) { + result.append(' '); + } + } + String attributes = result.toString(); + attributes = attributes.length() > 0 ? ' ' + attributes : null; + Map predefinedValues = null; + if (attributes != null) { + predefinedValues = new HashMap(); + predefinedValues.put(ATTRS, attributes); + } + return predefinedValues; + } + + private static String getValue(Pair pair, int numberInIteration) { + return pair.second.replace(NUMBER_IN_ITERATION_PLACE_HOLDER, Integer.toString(numberInIteration + 1)); + } + + @Nullable + private static String addAttrsVar(TemplateImpl modifiedTemplate, XmlTag tag) { + String text = tag.getContainingFile().getText(); + PsiElement[] children = tag.getChildren(); + if (children.length >= 1 && + children[0] instanceof XmlToken && + ((XmlToken)children[0]).getTokenType() == XmlTokenType.XML_START_TAG_START) { + PsiElement beforeAttrs = children[0]; + if (children.length >= 2 && children[1] instanceof XmlToken && ((XmlToken)children[1]).getTokenType() == XmlTokenType.XML_NAME) { + beforeAttrs = children[1]; + } + TextRange range = beforeAttrs.getTextRange(); + if (range == null) { + return null; + } + int offset = range.getEndOffset(); + text = text.substring(0, offset) + " $ATTRS$" + text.substring(offset); + modifiedTemplate.addVariable(ATTRS, "", "", false); + return text; + } + return null; + } + + private static boolean invokeTemplate(TemplateToken token, + final CustomTemplateCallback callback, + final TemplateInvokationListener listener, + int numberInIteration) { + List> attr2value = new ArrayList>(token.myAttribute2Value); + if (callback.isLiveTemplateApplicable(token.myKey)) { + if (token.myTemplate != null) { + TemplateImpl modifiedTemplate = token.myTemplate.copy(); + XmlTag tag = XmlZenCodingTemplate.parseXmlTagInTemplate(token.myTemplate.getString(), callback.getProject()); + assert tag != null; + for (Iterator> iterator = attr2value.iterator(); iterator.hasNext();) { + Pair pair = iterator.next(); + if (tag.getAttribute(pair.first) != null) { + tag.setAttribute(pair.first, getValue(pair, numberInIteration)); + iterator.remove(); + } + } + String text = null; + if (!containsAttrsVar(modifiedTemplate) && attr2value.size() > 0) { + String textWithAttrs = addAttrsVar(modifiedTemplate, tag); + if (textWithAttrs != null) { + text = textWithAttrs; + } + else { + for (Iterator> iterator = attr2value.iterator(); iterator.hasNext();) { + Pair pair = iterator.next(); + tag.setAttribute(pair.first, getValue(pair, numberInIteration)); + iterator.remove(); + } + } + } + if (text == null) { + text = tag.getContainingFile().getText(); + } + modifiedTemplate.setString(text); + removeVariablesWhichHasNoSegment(modifiedTemplate); + Map predefinedValues = buildPredefinedValues(attr2value, numberInIteration); + return callback.startTemplate(modifiedTemplate, predefinedValues, listener); + } + else { + Map predefinedValues = buildPredefinedValues(attr2value, numberInIteration); + return callback.startTemplate(token.myKey, predefinedValues, listener); + } + } + else { + TemplateImpl template = new TemplateImpl("", ""); + template.addTextSegment('<' + token.myKey); + if (attr2value.size() > 0) { + template.addVariable(ATTRS, "", "", false); + template.addVariableSegment(ATTRS); + } + template.addTextSegment(">"); + template.addVariableSegment(TemplateImpl.END); + template.addTextSegment(""); + template.setToReformat(true); + Map predefinedValues = buildPredefinedValues(attr2value, numberInIteration); + return callback.startTemplate(template, predefinedValues, listener); + } + } + + private static void fail() { + LOG.error("Input string was checked incorrectly during isApplicable() invokation"); + } +} diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/XmlZenCodingTemplate.java b/xml/impl/src/com/intellij/codeInsight/template/zencoding/XmlZenCodingTemplate.java new file mode 100644 index 000000000000..29fdbb908743 --- /dev/null +++ b/xml/impl/src/com/intellij/codeInsight/template/zencoding/XmlZenCodingTemplate.java @@ -0,0 +1,349 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.template.zencoding; + +import com.intellij.application.options.editor.WebEditorOptions; +import com.intellij.codeInsight.template.CustomLiveTemplate; +import com.intellij.codeInsight.template.CustomTemplateCallback; +import com.intellij.codeInsight.template.TemplateInvokationListener; +import com.intellij.codeInsight.template.impl.TemplateImpl; +import com.intellij.lang.xml.XMLLanguage; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiFileFactory; +import com.intellij.psi.xml.*; +import com.intellij.util.containers.HashSet; +import org.apache.xerces.util.XML11Char; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +/** + * @author Eugene.Kudelevsky + */ +public class XmlZenCodingTemplate implements CustomLiveTemplate { + static final char MARKER = '$'; + private static final String OPERATIONS = ">+*"; + private static final String SELECTORS = ".#["; + private static final String ID = "id"; + private static final String CLASS = "class"; + + private static int parseNonNegativeInt(@NotNull String s) { + try { + return Integer.parseInt(s); + } + catch (Throwable ignored) { + } + return -1; + } + + private static String getPrefix(@NotNull String templateKey) { + for (int i = 0, n = templateKey.length(); i < n; i++) { + char c = templateKey.charAt(i); + if (SELECTORS.indexOf(c) >= 0) { + return templateKey.substring(0, i); + } + } + return templateKey; + } + + @Nullable + private static Pair parseAttrNameAndValue(@NotNull String text) { + int eqIndex = text.indexOf('='); + if (eqIndex > 0) { + return new Pair(text.substring(0, eqIndex), text.substring(eqIndex + 1)); + } + return null; + } + + @Nullable + private static TemplateToken parseSelectors(@NotNull String text) { + String templateKey = null; + List> attributes = new ArrayList>(); + Set definedAttrs = new HashSet(); + final List classes = new ArrayList(); + StringBuilder builder = new StringBuilder(); + char lastDelim = 0; + text += MARKER; + int classAttrPosition = -1; + for (int i = 0, n = text.length(); i < n; i++) { + char c = text.charAt(i); + if (c == '#' || c == '.' || c == '[' || c == ']' || i == n - 1) { + if (c != ']') { + switch (lastDelim) { + case 0: + templateKey = builder.toString(); + break; + case '#': + if (!definedAttrs.add(ID)) { + return null; + } + attributes.add(new Pair(ID, builder.toString())); + break; + case '.': + if (builder.length() <= 0) { + return null; + } + if (classAttrPosition < 0) { + classAttrPosition = attributes.size(); + } + classes.add(builder.toString()); + break; + case ']': + if (builder.length() > 0) { + return null; + } + break; + default: + return null; + } + } + else if (lastDelim != '[') { + return null; + } + else { + Pair pair = parseAttrNameAndValue(builder.toString()); + if (pair == null || !definedAttrs.add(pair.first)) { + return null; + } + attributes.add(pair); + } + lastDelim = c; + builder = new StringBuilder(); + } + else { + builder.append(c); + } + } + if (classes.size() > 0) { + if (definedAttrs.contains(CLASS)) { + return null; + } + StringBuilder classesAttrValue = new StringBuilder(); + for (int i = 0; i < classes.size(); i++) { + classesAttrValue.append(classes.get(i)); + if (i < classes.size() - 1) { + classesAttrValue.append(' '); + } + } + assert classAttrPosition >= 0; + attributes.add(classAttrPosition, new Pair(CLASS, classesAttrValue.toString())); + } + return new TemplateToken(templateKey, attributes); + } + + private static boolean isXML11ValidQName(String str) { + final int colon = str.indexOf(':'); + if (colon == 0 || colon == str.length() - 1) { + return false; + } + if (colon > 0) { + final String prefix = str.substring(0, colon); + final String localPart = str.substring(colon + 1); + return XML11Char.isXML11ValidNCName(prefix) && XML11Char.isXML11ValidNCName(localPart); + } + return XML11Char.isXML11ValidNCName(str); + } + + private static boolean generateTemplateAndAddToToken(TemplateToken token, CustomTemplateCallback callback) { + TemplateImpl template = callback.findApplicableTemplate(token.myKey); + assert template != null; + XmlTag tag = parseXmlTagInTemplate(template.getString(), callback.getProject()); + if (tag == null) { + return false; + } + token.myTemplate = template; + return true; + } + + @Nullable + private static List parse(@NotNull String text, @NotNull CustomTemplateCallback callback) { + text += MARKER; + StringBuilder templateKeyBuilder = new StringBuilder(); + List result = new ArrayList(); + for (int i = 0, n = text.length(); i < n; i++) { + char c = text.charAt(i); + if (i == n - 1 || (i < n - 2 && OPERATIONS.indexOf(c) >= 0)) { + String key = templateKeyBuilder.toString(); + templateKeyBuilder = new StringBuilder(); + int num = parseNonNegativeInt(key); + if (num > 0) { + result.add(new NumberToken(num)); + } + else { + if (key.length() == 0) { + return null; + } + String prefix = getPrefix(key); + boolean applicable = callback.isLiveTemplateApplicable(prefix); + if (!applicable && !isXML11ValidQName(prefix)) { + return null; + } + TemplateToken token = parseSelectors(key); + if (token == null) { + return null; + } + if (applicable && token.myAttribute2Value.size() > 0) { + assert prefix.equals(token.myKey); + if (!generateTemplateAndAddToToken(token, callback)) { + return null; + } + } + result.add(token); + } + result.add(i < n - 1 ? new OperationToken(c) : new MarkerToken()); + } + else if (!Character.isWhitespace(c)) { + templateKeyBuilder.append(c); + } + else { + return null; + } + } + return result; + } + + private static boolean check(@NotNull Collection tokens) { + State state = State.WORD; + for (Token token : tokens) { + if (token instanceof MarkerToken) { + break; + } + switch (state) { + case OPERATION: + if (token instanceof OperationToken) { + state = ((OperationToken)token).mySign == '*' ? State.NUMBER : State.WORD; + } + else { + return false; + } + break; + case WORD: + if (token instanceof TemplateToken) { + state = State.OPERATION; + } + else { + return false; + } + break; + case NUMBER: + if (token instanceof NumberToken) { + state = State.AFTER_NUMBER; + } + else { + return false; + } + break; + case AFTER_NUMBER: + if (token instanceof OperationToken && ((OperationToken)token).mySign != '*') { + state = State.WORD; + } + else { + return false; + } + break; + } + } + return state == State.OPERATION || state == State.AFTER_NUMBER; + } + + private static String computeKey(Editor editor, int startOffset) { + int offset = editor.getCaretModel().getOffset(); + String s = editor.getDocument().getCharsSequence().subSequence(startOffset, offset).toString(); + int index = 0; + while (index < s.length() && Character.isWhitespace(s.charAt(index))) { + index++; + } + String key = s.substring(index); + int lastWhitespaceIndex = -1; + for (int i = 0; i < key.length(); i++) { + if (Character.isWhitespace(key.charAt(i))) { + lastWhitespaceIndex = i; + } + } + if (lastWhitespaceIndex >= 0 && lastWhitespaceIndex < key.length() - 1) { + return key.substring(lastWhitespaceIndex + 1); + } + return key; + } + + public String computeTemplateKey(@NotNull CustomTemplateCallback callback) { + Editor editor = callback.getEditor(); + int offset = callback.getOffset(); + PsiElement element = callback.getFile().findElementAt(offset > 0 ? offset - 1 : offset); + int line = editor.getCaretModel().getLogicalPosition().line; + int lineStart = editor.getDocument().getLineStartOffset(line); + int parentStart; + do { + parentStart = element != null ? element.getTextRange().getStartOffset() : 0; + int startOffset = parentStart > lineStart ? parentStart : lineStart; + String key = computeKey(editor, startOffset); + List tokens = parse(key, callback); + if (tokens != null && check(tokens)) { + if (tokens.size() == 2) { + Token token = tokens.get(0); + if (token instanceof TemplateToken) { + if (key.equals(((TemplateToken)token).myKey) && callback.isLiveTemplateApplicable(key)) { + // do not activate only live template + return null; + } + } + } + return key; + } + if (element != null) { + element = element.getParent(); + } + } + while (element != null && parentStart > lineStart); + return null; + } + + public boolean isApplicable(PsiFile file, int offset, boolean selection) { + WebEditorOptions webEditorOptions = WebEditorOptions.getInstance(); + if (!webEditorOptions.isZenCodingEnabled()) { + return false; + } + if (file.getLanguage() instanceof XMLLanguage) { + PsiElement element = file.findElementAt(offset > 0 ? offset - 1 : offset); + if (element == null || element.getLanguage() instanceof XMLLanguage) { + return true; + } + } + return false; + } + + public void expand(String key, @NotNull CustomTemplateCallback callback, @Nullable TemplateInvokationListener listener) { + List tokens = parse(key, callback); + assert tokens != null; + XmlZenCodingInterpreter interpreter = new XmlZenCodingInterpreter(tokens, callback, State.WORD, listener); + interpreter.invoke(0); + } + + public void wrap(String selection, @NotNull CustomTemplateCallback callback, @Nullable TemplateInvokationListener listener) { + } + + @Nullable + static XmlTag parseXmlTagInTemplate(String templateString, Project project) { + XmlFile xmlFile = (XmlFile)PsiFileFactory.getInstance(project).createFileFromText("dummy.xml", templateString); + XmlDocument document = xmlFile.getDocument(); + return document == null ? null : document.getRootTag(); + } +} \ No newline at end of file diff --git a/xml/impl/src/com/intellij/codeInspection/htmlInspections/HtmlUnknownTagInspection.java b/xml/impl/src/com/intellij/codeInspection/htmlInspections/HtmlUnknownTagInspection.java index ecbf5ed7e91a..77402ef8cfe2 100644 --- a/xml/impl/src/com/intellij/codeInspection/htmlInspections/HtmlUnknownTagInspection.java +++ b/xml/impl/src/com/intellij/codeInspection/htmlInspections/HtmlUnknownTagInspection.java @@ -202,7 +202,8 @@ public class HtmlUnknownTagInspection extends HtmlLocalInspectionTool { final PsiElement startTagName = XmlTagUtil.getStartTagNameElement(tag); final PsiElement endTagName = XmlTagUtil.getEndTagNameElement(tag); - final CreateNSDeclarationIntentionFix declarationIntentionFix = new CreateNSDeclarationIntentionFix(startTagName, ""); + final CreateNSDeclarationIntentionFix declarationIntentionFix = + isOnTheFly ? new CreateNSDeclarationIntentionFix(startTagName, ""):null; ProblemHighlightType highlightType = tag.getContainingFile().getContext() == null ? ProblemHighlightType.GENERIC_ERROR_OR_WARNING : ProblemHighlightType.INFORMATION; diff --git a/xml/impl/src/com/intellij/psi/impl/source/parsing/xml/OldXmlParsing.java b/xml/impl/src/com/intellij/psi/impl/source/parsing/xml/OldXmlParsing.java index 4e24e7acdca3..620984108486 100644 --- a/xml/impl/src/com/intellij/psi/impl/source/parsing/xml/OldXmlParsing.java +++ b/xml/impl/src/com/intellij/psi/impl/source/parsing/xml/OldXmlParsing.java @@ -1072,7 +1072,7 @@ public class OldXmlParsing implements XmlElementType { } current = treeNext; } - if (insertAfter != null) insertAfter.rawInsertAfterMe(firstMissing); + if (insertAfter != null && firstMissing != null) insertAfter.rawInsertAfterMe(firstMissing); } } passTokenOrChameleon(next, lexer); diff --git a/xml/impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/AttributeValueSelfReference.java b/xml/impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/AttributeValueSelfReference.java index 7a687801d24f..9ecba6f551a2 100644 --- a/xml/impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/AttributeValueSelfReference.java +++ b/xml/impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/AttributeValueSelfReference.java @@ -18,6 +18,7 @@ package com.intellij.psi.impl.source.resolve.reference.impl.providers; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; import com.intellij.util.ArrayUtil; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** @@ -41,6 +42,7 @@ public class AttributeValueSelfReference extends BasicAttributeValueReference { return myElement; } + @NotNull public Object[] getVariants() { return ArrayUtil.EMPTY_OBJECT_ARRAY; } diff --git a/xml/impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/DtdReferencesProvider.java b/xml/impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/DtdReferencesProvider.java index b3a385933adb..66a3b512b747 100644 --- a/xml/impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/DtdReferencesProvider.java +++ b/xml/impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/DtdReferencesProvider.java @@ -136,6 +136,7 @@ public class DtdReferencesProvider extends PsiReferenceProvider { return myElement.getManager().areElementsEquivalent(element, resolve()); } + @NotNull public Object[] getVariants() { final XmlNSDescriptor rootTagNSDescriptor = getNsDescriptor(); return rootTagNSDescriptor != null ? @@ -226,6 +227,7 @@ public class DtdReferencesProvider extends PsiReferenceProvider { return myElement.getManager().areElementsEquivalent(resolve(), element); } + @NotNull public Object[] getVariants() { return ArrayUtil.EMPTY_OBJECT_ARRAY; } diff --git a/xml/impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/IdRefReference.java b/xml/impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/IdRefReference.java index 1c884cde28e0..c8ec76e80668 100644 --- a/xml/impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/IdRefReference.java +++ b/xml/impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/IdRefReference.java @@ -157,6 +157,7 @@ public class IdRefReference extends BasicAttributeValueReference { return result[0]; } + @NotNull public Object[] getVariants() { final List result = new LinkedList(); diff --git a/xml/impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/SchemaReferencesProvider.java b/xml/impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/SchemaReferencesProvider.java index 35d215cd7583..068b66c16d6a 100644 --- a/xml/impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/SchemaReferencesProvider.java +++ b/xml/impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/SchemaReferencesProvider.java @@ -116,6 +116,7 @@ public class SchemaReferencesProvider extends PsiReferenceProvider { } } + @NotNull public Object[] getVariants() { return ArrayUtil.EMPTY_OBJECT_ARRAY; } @@ -170,6 +171,7 @@ public class SchemaReferencesProvider extends PsiReferenceProvider { return myElement.getManager().areElementsEquivalent(resolve(), element); } + @NotNull public Object[] getVariants() { return ArrayUtil.EMPTY_OBJECT_ARRAY; } @@ -454,6 +456,7 @@ public class SchemaReferencesProvider extends PsiReferenceProvider { } } + @NotNull public Object[] getVariants() { final XmlTag tag = PsiTreeUtil.getParentOfType(myElement, XmlTag.class); if (tag == null) return null; diff --git a/xml/impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/URIReferenceProvider.java b/xml/impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/URIReferenceProvider.java index 6dbcab0dbda9..860cad0634d9 100644 --- a/xml/impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/URIReferenceProvider.java +++ b/xml/impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/URIReferenceProvider.java @@ -88,6 +88,7 @@ public class URIReferenceProvider extends PsiReferenceProvider { return myReference.resolve(); } + @NotNull public Object[] getVariants() { return ArrayUtil.EMPTY_OBJECT_ARRAY; } diff --git a/xml/impl/src/com/intellij/psi/impl/source/xml/TagNameReference.java b/xml/impl/src/com/intellij/psi/impl/source/xml/TagNameReference.java index b1a0683c35ab..80270442197b 100644 --- a/xml/impl/src/com/intellij/psi/impl/source/xml/TagNameReference.java +++ b/xml/impl/src/com/intellij/psi/impl/source/xml/TagNameReference.java @@ -169,6 +169,7 @@ public class TagNameReference implements PsiReference { return getElement().getManager().areElementsEquivalent(element, resolve()); } + @NotNull public Object[] getVariants(){ final PsiElement element = getElement(); if(!myStartTagFlag){ diff --git a/xml/impl/src/com/intellij/psi/impl/source/xml/XmlAttributeImpl.java b/xml/impl/src/com/intellij/psi/impl/source/xml/XmlAttributeImpl.java index f0c74823fe05..a4a329875749 100644 --- a/xml/impl/src/com/intellij/psi/impl/source/xml/XmlAttributeImpl.java +++ b/xml/impl/src/com/intellij/psi/impl/source/xml/XmlAttributeImpl.java @@ -420,6 +420,7 @@ public class XmlAttributeImpl extends XmlElementImpl implements XmlAttribute { return getManager().areElementsEquivalent(element, resolve()); } + @NotNull public Object[] getVariants() { final List> variants = new ArrayList>(); diff --git a/xml/impl/src/com/intellij/xml/util/AnchorReference.java b/xml/impl/src/com/intellij/xml/util/AnchorReference.java index 1b8bff853027..e829e1df2ff5 100644 --- a/xml/impl/src/com/intellij/xml/util/AnchorReference.java +++ b/xml/impl/src/com/intellij/xml/util/AnchorReference.java @@ -196,6 +196,7 @@ class AnchorReference implements PsiReference, EmptyResolveMessageProvider { return myElement.getManager().areElementsEquivalent(element,resolve()); } + @NotNull public Object[] getVariants() { final Map idMap = getIdMap(); if (idMap == null) return ArrayUtil.EMPTY_OBJECT_ARRAY; diff --git a/xml/impl/src/com/intellij/xml/util/CheckEmptyTagInspection.java b/xml/impl/src/com/intellij/xml/util/CheckEmptyTagInspection.java index 76e1624c89cd..728e91eab011 100644 --- a/xml/impl/src/com/intellij/xml/util/CheckEmptyTagInspection.java +++ b/xml/impl/src/com/intellij/xml/util/CheckEmptyTagInspection.java @@ -49,7 +49,7 @@ import java.util.Set; public class CheckEmptyTagInspection extends XmlSuppressableInspectionTool { private static final Logger LOG = Logger.getInstance("#com.intellij.xml.util.CheckEmptyTagInspection"); @NonNls private static final String SCRIPT_TAG_NAME = "script"; - private static Set ourTagsWithEmptyEndsNotAllowed = new THashSet(Arrays.asList(SCRIPT_TAG_NAME, "div", "iframe")); + private static final Set ourTagsWithEmptyEndsNotAllowed = new THashSet(Arrays.asList(SCRIPT_TAG_NAME, "div", "iframe")); public boolean isEnabledByDefault() { return true; @@ -59,55 +59,57 @@ public class CheckEmptyTagInspection extends XmlSuppressableInspectionTool { public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) { return new XmlElementVisitor() { @Override public void visitXmlTag(final XmlTag tag) { - if (isTagWithEmptyEndNotAllowed(tag)) { - final ASTNode child = XmlChildRole.EMPTY_TAG_END_FINDER.findChild(tag.getNode()); - - if (child != null) { - - final LocalQuickFix fix = new LocalQuickFix() { - @NotNull - public String getName() { - return XmlBundle.message("html.inspections.check.empty.script.tag.fix.message"); - } - - public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { - final XmlTag tag = (XmlTag)descriptor.getPsiElement(); - if (tag == null) return; - final PsiFile psiFile = tag.getContainingFile(); - - if (psiFile == null) return; - ReadonlyStatusHandler.getInstance(project).ensureFilesWritable(psiFile.getVirtualFile()); - - final StringBuilder builder = new StringBuilder(tag.getText()); - builder.replace(builder.length() - 2, builder.length(), ">"); - - try { - final FileType fileType = psiFile.getFileType(); - PsiFile file = PsiFileFactory.getInstance(tag.getProject()).createFileFromText( - "dummy." + (fileType == StdFileTypes.JSP || tag.getContainingFile().getLanguage() == HTMLLanguage.INSTANCE ? "html" : "xml"), builder.toString()); - - tag.replace(((XmlFile)file).getDocument().getRootTag()); - } - catch (IncorrectOperationException e) { - LOG.error(e); - } - } - - //to appear in "Apply Fix" statement when multiple Quick Fixes exist - @NotNull - public String getFamilyName() { - return getName(); - } - }; - - holder.registerProblem(tag, - XmlBundle.message("html.inspections.check.empty.script.message"), - tag.getContainingFile().getContext() != null ? - ProblemHighlightType.INFORMATION: - ProblemHighlightType.GENERIC_ERROR_OR_WARNING, - fix); - } + if (!isTagWithEmptyEndNotAllowed(tag)) { + return; } + final ASTNode child = XmlChildRole.EMPTY_TAG_END_FINDER.findChild(tag.getNode()); + + if (child == null) { + return; + } + + final LocalQuickFix fix = new LocalQuickFix() { + @NotNull + public String getName() { + return XmlBundle.message("html.inspections.check.empty.script.tag.fix.message"); + } + + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + final XmlTag tag = (XmlTag)descriptor.getPsiElement(); + if (tag == null) return; + final PsiFile psiFile = tag.getContainingFile(); + + if (psiFile == null) return; + ReadonlyStatusHandler.getInstance(project).ensureFilesWritable(psiFile.getVirtualFile()); + + final StringBuilder builder = new StringBuilder(tag.getText()); + builder.replace(builder.length() - 2, builder.length(), ">"); + + try { + final FileType fileType = psiFile.getFileType(); + PsiFile file = PsiFileFactory.getInstance(tag.getProject()).createFileFromText( + "dummy." + (fileType == StdFileTypes.JSP || tag.getContainingFile().getLanguage() == HTMLLanguage.INSTANCE ? "html" : "xml"), builder.toString()); + + tag.replace(((XmlFile)file).getDocument().getRootTag()); + } + catch (IncorrectOperationException e) { + LOG.error(e); + } + } + + //to appear in "Apply Fix" statement when multiple Quick Fixes exist + @NotNull + public String getFamilyName() { + return getName(); + } + }; + + holder.registerProblem(tag, + XmlBundle.message("html.inspections.check.empty.script.message"), + tag.getContainingFile().getContext() != null ? + ProblemHighlightType.INFORMATION: + ProblemHighlightType.GENERIC_ERROR_OR_WARNING, + fix); } }; } @@ -117,11 +119,8 @@ public class CheckEmptyTagInspection extends XmlSuppressableInspectionTool { if (tag instanceof HtmlTag) tagName = tagName.toLowerCase(); Language language = tag.getLanguage(); - return (ourTagsWithEmptyEndsNotAllowed.contains(tagName) && - language != XMLLanguage.INSTANCE) || - (language == HTMLLanguage.INSTANCE && - ( !HtmlUtil.isSingleHtmlTagL(tagName) && tagName.indexOf(':') == -1)) - ; + return ourTagsWithEmptyEndsNotAllowed.contains(tagName) && language != XMLLanguage.INSTANCE || + language == HTMLLanguage.INSTANCE && !HtmlUtil.isSingleHtmlTagL(tagName) && tagName.indexOf(':') == -1; } @NotNull