diff --git a/bin/scripts/unix/idea.sh b/bin/scripts/unix/idea.sh index 126450d9bbff..6245a0620128 100755 --- a/bin/scripts/unix/idea.sh +++ b/bin/scripts/unix/idea.sh @@ -37,10 +37,10 @@ TR=`which tr` #Disbale Jayatana on linux -#if [ -n "$JAVA_TOOL_OPTIONS" ]; then -# JAVA_TOOL_OPTIONS=${JAVA_TOOL_OPTIONS%-javaagent*jayatanaag.jar*}${JAVA_TOOL_OPTIONS#*jayatanaag.jar} -# message "Jayatana global menu integration is disabled." -#fi +if [ -n "$JAVA_TOOL_OPTIONS" ]; then + JAVA_TOOL_OPTIONS=${JAVA_TOOL_OPTIONS%-javaagent*jayatanaag.jar*}${JAVA_TOOL_OPTIONS#*jayatanaag.jar} + message "Jayatana global menu integration is disabled." +fi if [ -z "$UNAME" -o -z "$GREP" -o -z "$CUT" -o -z "$MKTEMP" -o -z "$RM" -o -z "$CAT" -o -z "$TR" ]; then message "Required tools are missing - check beginning of \"$0\" file for details." diff --git a/bin/win/fsnotifier64.exe b/bin/win/fsnotifier64.exe new file mode 100644 index 000000000000..5324b9a3715c Binary files /dev/null and b/bin/win/fsnotifier64.exe differ diff --git a/java/debugger/impl/src/com/intellij/debugger/jdi/LocalVariablesUtil.java b/java/debugger/impl/src/com/intellij/debugger/jdi/LocalVariablesUtil.java index 134408c669c9..b204903ca751 100644 --- a/java/debugger/impl/src/com/intellij/debugger/jdi/LocalVariablesUtil.java +++ b/java/debugger/impl/src/com/intellij/debugger/jdi/LocalVariablesUtil.java @@ -309,22 +309,37 @@ public class LocalVariablesUtil { private int myCurrentSlotIndex; private final PsiElement myElement; private final Stack myIndexStack; + private boolean myReached = false; public LocalVariableNameFinder(int startSlot, MultiMap names, PsiElement element) { myNames = names; myCurrentSlotIndex = startSlot; myElement = element; myIndexStack = new Stack(); + } private boolean shouldVisit(PsiElement scope) { - return PsiTreeUtil.isContextAncestor(scope, myElement, false); + return !myReached && PsiTreeUtil.isContextAncestor(scope, myElement, false); + } + + @Override + public void visitElement(PsiElement element) { + if (element == myElement) { + myReached = true; + } + else { + super.visitElement(element); + } } @Override public void visitLocalVariable(PsiLocalVariable variable) { - appendName(variable.getName()); - myCurrentSlotIndex += getTypeSlotSize(variable.getType()); + super.visitLocalVariable(variable); + if (!myReached) { + appendName(variable.getName()); + myCurrentSlotIndex += getTypeSlotSize(variable.getType()); + } } public void visitSynchronizedStatement(PsiSynchronizedStatement statement) { diff --git a/java/execution/impl/src/com/intellij/execution/ui/JrePathEditor.java b/java/execution/impl/src/com/intellij/execution/ui/JrePathEditor.java index f4ad86acda6e..72ed2d2854f9 100644 --- a/java/execution/impl/src/com/intellij/execution/ui/JrePathEditor.java +++ b/java/execution/impl/src/com/intellij/execution/ui/JrePathEditor.java @@ -258,7 +258,9 @@ public class JrePathEditor extends JPanel implements PanelWithAnchor { @Override public void render(SimpleColoredComponent component, boolean selected) { component.append(DEFAULT_JRE_TEXT); - component.append(myDefaultJreSelector.getDescriptionString(), SimpleTextAttributes.GRAY_ATTRIBUTES); + if (myDefaultJreSelector != null) { + component.append(myDefaultJreSelector.getDescriptionString(), SimpleTextAttributes.GRAY_ATTRIBUTES); + } } @Override diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java index a196cf93eec4..ba51df0c5f10 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java @@ -216,6 +216,7 @@ public class JavaCompletionContributor extends CompletionContributor { if (ANNOTATION_ATTRIBUTE_NAME.accepts(position) && !JavaKeywordCompletion.isAfterPrimitiveOrArrayType(position)) { JavaKeywordCompletion.addExpectedTypeMembers(parameters, result); + JavaKeywordCompletion.addPrimitiveTypes(result, position); completeAnnotationAttributeName(result, position, parameters); result.stopHere(); return; diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaKeywordCompletion.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaKeywordCompletion.java index a5710722a064..1f8d88ae665f 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaKeywordCompletion.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaKeywordCompletion.java @@ -22,6 +22,7 @@ import com.intellij.codeInsight.completion.util.ParenthesesInsertHandler; import com.intellij.codeInsight.daemon.impl.analysis.LambdaHighlightingUtil; import com.intellij.codeInsight.lookup.*; import com.intellij.openapi.util.AtomicNotNullLazyValue; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.NotNullLazyValue; import com.intellij.patterns.ElementPattern; import com.intellij.pom.java.LanguageLevel; @@ -32,10 +33,12 @@ import com.intellij.psi.filters.position.*; import com.intellij.psi.impl.source.jsp.jspJava.JspClassLevelDeclarationStatement; import com.intellij.psi.jsp.JspElementType; import com.intellij.psi.templateLanguages.OuterLanguageElement; +import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.util.Consumer; import com.intellij.util.ProcessingContext; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -578,8 +581,9 @@ public class JavaKeywordCompletion { isAfterPrimitiveOrArrayType(position); } - private static void addPrimitiveTypes(final Consumer result, PsiElement position) { - if (AFTER_DOT.accepts(position)) { + static void addPrimitiveTypes(final Consumer result, PsiElement position) { + if (AFTER_DOT.accepts(position) || + psiElement().inside(psiAnnotation()).accepts(position) && !expectsClassLiteral(position)) { return; } @@ -623,6 +627,15 @@ public class JavaKeywordCompletion { } } + private static boolean expectsClassLiteral(PsiElement position) { + return ContainerUtil.find(JavaSmartCompletionContributor.getExpectedTypes(position, false), new Condition() { + @Override + public boolean value(ExpectedTypeInfo info) { + return InheritanceUtil.isInheritor(info.getType(), CommonClassNames.JAVA_LANG_CLASS); + } + }) != null; + } + private static boolean isAtResourceVariableStart(PsiElement position) { return psiElement().insideStarting(psiElement(PsiTypeElement.class).withParent(PsiResourceList.class)).accepts(position); } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaSmartCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaSmartCompletionContributor.java index dd27cbe3dc66..d181a34e6358 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaSmartCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaSmartCompletionContributor.java @@ -246,12 +246,11 @@ public class JavaSmartCompletionContributor extends CompletionContributor { @NotNull public static ExpectedTypeInfo[] getExpectedTypes(final CompletionParameters parameters) { - return getExpectedTypes(parameters, parameters.getCompletionType() == CompletionType.SMART); + return getExpectedTypes(parameters.getPosition(), parameters.getCompletionType() == CompletionType.SMART); } @NotNull - public static ExpectedTypeInfo[] getExpectedTypes(final CompletionParameters parameters, boolean voidable) { - final PsiElement position = parameters.getPosition(); + public static ExpectedTypeInfo[] getExpectedTypes(PsiElement position, boolean voidable) { if (psiElement().withParent(psiElement(PsiReferenceExpression.class).withParent(PsiThrowStatement.class)).accepts(position)) { final PsiElementFactory factory = JavaPsiFacade.getInstance(position.getProject()).getElementFactory(); final PsiClassType classType = factory diff --git a/java/java-impl/src/com/intellij/codeInspection/magicConstant/MagicCompletionContributor.java b/java/java-impl/src/com/intellij/codeInspection/magicConstant/MagicCompletionContributor.java index d3902809a44c..2a4a758cdf1b 100644 --- a/java/java-impl/src/com/intellij/codeInspection/magicConstant/MagicCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInspection/magicConstant/MagicCompletionContributor.java @@ -45,7 +45,7 @@ public class MagicCompletionContributor extends CompletionContributor { private static final ElementPattern IN_METHOD_CALL_ARGUMENT = psiElement().withParent(psiElement(PsiReferenceExpression.class).inside(psiElement(PsiExpressionList.class).withParent(PsiCall.class))); private static final ElementPattern IN_BINARY_COMPARISON = - psiElement().withParent(psiElement(PsiReferenceExpression.class).inside(psiElement(PsiBinaryExpression.class))); + psiElement().withParent(psiElement(PsiReferenceExpression.class).inside(psiElement(PsiPolyadicExpression.class))); private static final ElementPattern IN_ASSIGNMENT = psiElement().withParent(psiElement(PsiReferenceExpression.class).inside(psiElement(PsiAssignmentExpression.class))); private static final ElementPattern IN_RETURN = @@ -129,17 +129,13 @@ public class MagicCompletionContributor extends CompletionContributor { } } else if (IN_BINARY_COMPARISON.accepts(pos)) { - PsiBinaryExpression exp = PsiTreeUtil.getParentOfType(pos, PsiBinaryExpression.class); + PsiPolyadicExpression exp = PsiTreeUtil.getParentOfType(pos, PsiPolyadicExpression.class); if (exp != null && (exp.getOperationTokenType() == JavaTokenType.EQEQ || exp.getOperationTokenType() == JavaTokenType.NE)) { - PsiExpression l = exp.getLOperand(); - PsiModifierListOwner resolved = resolveExpression(l); - if (resolved != null) { - result.add(Pair.create(resolved, l.getType())); - } - PsiExpression r = exp.getROperand(); - resolved = resolveExpression(r); - if (r != null && resolved != null) { - result.add(Pair.create(resolved, r.getType())); + for (PsiExpression operand : exp.getOperands()) { + PsiModifierListOwner resolved = resolveExpression(operand); + if (resolved != null) { + result.add(Pair.create(resolved, operand.getType())); + } } } } diff --git a/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/javadoc/CommentFormatter.java b/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/javadoc/CommentFormatter.java index 8e3b75b6fab7..3845b2bb99c0 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/javadoc/CommentFormatter.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/javadoc/CommentFormatter.java @@ -49,7 +49,7 @@ public class CommentFormatter { public CommentFormatter(@NotNull Project project) { mySettings = CodeStyleSettingsManager.getSettings(project); - myParser = new JDParser(mySettings, LanguageLevelProjectExtension.getInstance(project).getLanguageLevel()); + myParser = new JDParser(mySettings); myProject = project; } diff --git a/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/javadoc/JDParser.java b/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/javadoc/JDParser.java index 997c155e5aa3..cf4a353ce049 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/javadoc/JDParser.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/javadoc/JDParser.java @@ -45,11 +45,9 @@ public class JDParser { private static final char lineSeparator = '\n'; private final CodeStyleSettings mySettings; - private final LanguageLevel myLanguageLevel; - public JDParser(@NotNull CodeStyleSettings settings, @NotNull LanguageLevel languageLevel) { + public JDParser(@NotNull CodeStyleSettings settings) { mySettings = settings; - myLanguageLevel = languageLevel; } public void formatCommentText(@NotNull PsiElement element, @NotNull CommentFormatter formatter) { @@ -253,7 +251,7 @@ public class JDParser { first = true; if (p2nl) { if (isParaTag(token) && s.indexOf(P_END_TAG, curPos) < 0) { - list.add(""); + list.add(isSelfClosedPTag(token) ? SELF_CLOSED_P_TAG : ""); markers.add(Boolean.valueOf(preCount > 0)); continue; } @@ -277,6 +275,11 @@ public class JDParser { String withoutWS = removeWhiteSpacesFrom(token).toLowerCase(); return withoutWS.equals(SELF_CLOSED_P_TAG) || withoutWS.equals(P_START_TAG); } + + private static boolean isSelfClosedPTag(@NotNull final String token) { + return removeWhiteSpacesFrom(token).toLowerCase().equals(SELF_CLOSED_P_TAG); + } + @NotNull private static String removeWhiteSpacesFrom(@NotNull final String token) { @@ -379,12 +382,12 @@ public class JDParser { result.add(Pair.create(s1, marks[i])); } else { - if (s1.isEmpty()) { + if (s1.isEmpty() || s1.equals(SELF_CLOSED_P_TAG)) { if (sb.length() != 0) { result.add(new Pair(sb.toString(), false)); sb.setLength(0); } - result.add(Pair.create("", marks[i])); + result.add(Pair.create(s1, marks[i])); } else if (mySettings.JD_PRESERVE_LINE_FEEDS) { result.add(Pair.create(s1, marks[i])); @@ -596,13 +599,7 @@ public class JDParser { if (line.isEmpty() && !mySettings.JD_KEEP_EMPTY_LINES) continue; if (i != 0) sb.append(prefix); if (line.isEmpty() && mySettings.JD_P_AT_EMPTY_LINES && !insidePreTag) { - if (myLanguageLevel.isAtLeast(LanguageLevel.JDK_1_8)) { - //Self-closing elements are not allowed for javadoc tool from JDK8 - sb.append(P_START_TAG); - } - else { - sb.append(SELF_CLOSED_P_TAG); - } + sb.append(P_START_TAG); } else { sb.append(line); diff --git a/java/java-impl/src/com/intellij/refactoring/typeMigration/TypeEvaluator.java b/java/java-impl/src/com/intellij/refactoring/typeMigration/TypeEvaluator.java index 1435dc1f2271..4e4f078c8944 100644 --- a/java/java-impl/src/com/intellij/refactoring/typeMigration/TypeEvaluator.java +++ b/java/java-impl/src/com/intellij/refactoring/typeMigration/TypeEvaluator.java @@ -88,6 +88,7 @@ public class TypeEvaluator { e.addFirst(type); + usageInfo.setOwnerRoot(myLabeler.getCurrentRoot()); myTypeMap.put(usageInfo, e); return true; } diff --git a/java/java-impl/src/com/intellij/refactoring/typeMigration/TypeMigrationLabeler.java b/java/java-impl/src/com/intellij/refactoring/typeMigration/TypeMigrationLabeler.java index d381a958191d..68fad5e282f2 100644 --- a/java/java-impl/src/com/intellij/refactoring/typeMigration/TypeMigrationLabeler.java +++ b/java/java-impl/src/com/intellij/refactoring/typeMigration/TypeMigrationLabeler.java @@ -23,6 +23,7 @@ import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; +import com.intellij.psi.impl.PsiDiamondTypeUtil; import com.intellij.psi.impl.PsiImplUtil; import com.intellij.psi.javadoc.PsiDocTagValue; import com.intellij.psi.search.PsiSearchScopeUtil; @@ -36,13 +37,21 @@ import com.intellij.refactoring.typeMigration.usageInfo.OverridenUsageInfo; import com.intellij.refactoring.typeMigration.usageInfo.OverriderUsageInfo; import com.intellij.refactoring.typeMigration.usageInfo.TypeMigrationUsageInfo; import com.intellij.usageView.UsageInfo; +import com.intellij.util.Consumer; +import com.intellij.util.Function; import com.intellij.util.IncorrectOperationException; import com.intellij.util.Query; +import com.intellij.util.containers.*; +import com.intellij.util.graph.DFSTBuilder; +import com.intellij.util.graph.GraphGenerator; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; import javax.swing.*; import java.util.*; +import java.util.HashMap; +import java.util.HashSet; /** * @author db @@ -132,15 +141,8 @@ public class TypeMigrationLabeler { int j = 0; - List conversionExprs = new ArrayList(myConversions.keySet()); - Collections.sort(conversionExprs, new Comparator() { - public int compare(final PsiElement e1, final PsiElement e2) { - return e2.getTextRange().getStartOffset() - e1.getTextRange().getStartOffset(); - } - }); - for (final PsiElement element : conversionExprs) { + for (final PsiElement element : myConversions.keySet()) { final Object conv = myConversions.get(element); - usages[j++] = new TypeMigrationUsageInfo(element) { public String getTooltipText() { if (conv instanceof String) { //todo @@ -157,6 +159,11 @@ public class TypeMigrationLabeler { if (conv instanceof TypeConversionDescriptorBase) return ((TypeConversionDescriptorBase)conv).getRoot().isExcluded(); return super.isExcluded(); } + + @Override + public TypeMigrationUsageInfo getOwnerRoot() { + return conv instanceof TypeConversionDescriptorBase ? ((TypeConversionDescriptorBase)conv).getRoot() : null; + } }; } @@ -172,10 +179,76 @@ public class TypeMigrationLabeler { for (final TypeMigrationUsageInfo expr : myNewExpressionTypeChange.keySet()) { usages[j++] = expr; } - return usages; + return sortMigratedUsages(usages); } - public void change(final TypeMigrationUsageInfo usageInfo) { + private TypeMigrationUsageInfo[] sortMigratedUsages(TypeMigrationUsageInfo[] infos) { + final DFSTBuilder builder = new DFSTBuilder(GraphGenerator.create( + new GraphGenerator.SemiGraph() { + @Override + public Collection getNodes() { + final Set infos = new HashSet(); + for (Map.Entry>> entry : myRootsTree.entrySet()) { + infos.add(entry.getKey()); + infos.addAll(ContainerUtil.map(entry.getValue(), new Function, TypeMigrationUsageInfo>() { + @Override + public TypeMigrationUsageInfo fun(Pair pair) { + return pair.getFirst(); + } + })); + } + return infos; + } + + @Override + public Iterator getIn(TypeMigrationUsageInfo n) { + final HashSet> rawNodes = myRootsTree.get(n); + if (rawNodes == null) { + return Collections.emptyList().iterator(); + } + final List in = + ContainerUtil.map(rawNodes, new Function, TypeMigrationUsageInfo>() { + @Override + public TypeMigrationUsageInfo fun(Pair pair) { + return pair.getFirst(); + } + }); + return in.iterator(); + } + })); + final Comparator cmp = builder.comparator(); + + Arrays.sort(infos, new Comparator() { + @Override + public int compare(final TypeMigrationUsageInfo info1, final TypeMigrationUsageInfo info2) { + final TypeMigrationUsageInfo i1 = info1.getOwnerRoot(); + final TypeMigrationUsageInfo i2 = info2.getOwnerRoot(); + if (i1 == null && i2 == null) { + return 0; + } + if (i1 == null) { + return 1; + } + if (i2 == null) { + return -1; + } + + final int res = cmp.compare(i1, i2); + if (res != 0) { + return res; + } + final PsiElement element1 = info1.getElement(); + final PsiElement element2 = info2.getElement(); + LOG.assertTrue(element1 != null && element2 != null); + return element2.getTextRange().getStartOffset() - element1.getTextRange().getStartOffset(); + } + }); + + return infos; + } + + + public void change(final TypeMigrationUsageInfo usageInfo, @NotNull Consumer consumer) { final PsiElement element = usageInfo.getElement(); if (element == null) return; final Project project = element.getProject(); @@ -185,7 +258,11 @@ public class TypeMigrationLabeler { for (Map.Entry info : myNewExpressionTypeChange.entrySet()) { final PsiElement expressionToReplace = info.getKey().getElement(); if (expression.equals(expressionToReplace)) { - TypeMigrationReplacementUtil.replaceNewExpressionType(project, (PsiNewExpression)expressionToReplace, info); + final PsiNewExpression newExpression = + TypeMigrationReplacementUtil.replaceNewExpressionType(project, (PsiNewExpression)expressionToReplace, info); + if (newExpression != null) { + consumer.consume(newExpression); + } } } } @@ -212,6 +289,10 @@ public class TypeMigrationLabeler { } } + void postProcessNewExpression(@NotNull PsiNewExpression expression) { + TypeMigrationReplacementUtil.tryToReplaceWithDiamond(expression, null); + } + @Nullable Object getConversion(PsiElement element) { return myConversions.get(element); @@ -310,7 +391,9 @@ public class TypeMigrationLabeler { return; } - myNewExpressionTypeChange.put(new TypeMigrationUsageInfo(expr), migrationType); + final TypeMigrationUsageInfo usageInfo = new TypeMigrationUsageInfo(expr); + usageInfo.setOwnerRoot(myCurrentRoot); + myNewExpressionTypeChange.put(usageInfo, migrationType); getTypeEvaluator().setType(new TypeMigrationUsageInfo(expr), migrationType); return; } else { @@ -322,7 +405,9 @@ public class TypeMigrationLabeler { final PsiType type = TypeEvaluator.substituteType(migrationType, originalType, true, ((PsiClassType)originalType).resolveGenerics().getElement(), JavaPsiFacade.getElementFactory(expr.getProject()).createType(((PsiClassType)originalType).resolve(), PsiSubstitutor.EMPTY)); if (type != null){ - myNewExpressionTypeChange.put(new TypeMigrationUsageInfo(expr), type); + final TypeMigrationUsageInfo usageInfo = new TypeMigrationUsageInfo(expr); + usageInfo.setOwnerRoot(myCurrentRoot); + myNewExpressionTypeChange.put(usageInfo, type); getTypeEvaluator().setType(new TypeMigrationUsageInfo(expr), type); return; } @@ -384,9 +469,6 @@ public class TypeMigrationLabeler { return false; } - //TODO dmitry batkovich - //type = JavaPsiFacade.getElementFactory(element.getProject()).createTypeFromText(type.getCanonicalText(true), element); - if (resolved instanceof PsiMethod) { final PsiMethod method = ((PsiMethod)resolved); final PsiMethod[] methods = OverridingMethodsSearch.search(method, true).toArray(PsiMethod.EMPTY_ARRAY); @@ -610,18 +692,24 @@ public class TypeMigrationLabeler { return validReferences.toArray(new PsiReference[validReferences.size()]); } - public void migrateRoot(final PsiElement root, final PsiType migrationType, final PsiReference[] usages) { + public void setRootAndMigrate(final TypeMigrationUsageInfo newRootUsageInfo, final PsiType migrationType, final PsiReference[] usages) { + final TypeMigrationUsageInfo oldRoot = getCurrentRoot(); + setCurrentRoot(newRootUsageInfo); + PsiElement root = newRootUsageInfo.getElement(); if (root instanceof PsiMethod) { migrateMethodReturnExpression(migrationType, (PsiMethod)root); } else if (root instanceof PsiParameter && ((PsiParameter)root).getDeclarationScope() instanceof PsiMethod) { migrateMethodCallExpressions(migrationType, (PsiParameter)root, null); } - else if (root instanceof PsiVariable || root instanceof PsiExpression){ + else if (root instanceof PsiVariable || root instanceof PsiExpression) { final PsiElement element = getContainingStatement(root); element.accept(new TypeMigrationStatementProcessor(element, this)); - } else if (root instanceof PsiReferenceParameterList) { - myClassTypeArgumentsChange.put(new TypeMigrationUsageInfo(root), (PsiClassType)migrationType); + } + else if (root instanceof PsiReferenceParameterList) { + final TypeMigrationUsageInfo info = new TypeMigrationUsageInfo(root); + info.setOwnerRoot(oldRoot); + myClassTypeArgumentsChange.put(info, (PsiClassType)migrationType); new ClassTypeArgumentMigrationProcessor(this).migrateClassTypeParameter((PsiReferenceParameterList)root, migrationType); } @@ -726,8 +814,7 @@ public class TypeMigrationLabeler { j = 0; for (final Pair root : roots) { - myCurrentRoot = root.getFirst(); - migrateRoot(root.getFirst().getElement(), root.getSecond(), cachedUsages[j++]); + setRootAndMigrate(root.getFirst(), root.getSecond(), cachedUsages[j++]); } } @@ -760,6 +847,10 @@ public class TypeMigrationLabeler { myCurrentRoot = currentRoot; } + TypeMigrationUsageInfo getCurrentRoot() { + return myCurrentRoot; + } + public LinkedList> getMigrationRoots() { return myMigrationRoots; } diff --git a/java/java-impl/src/com/intellij/refactoring/typeMigration/TypeMigrationProcessor.java b/java/java-impl/src/com/intellij/refactoring/typeMigration/TypeMigrationProcessor.java index 675340e4a3bf..8081efe89c3d 100644 --- a/java/java-impl/src/com/intellij/refactoring/typeMigration/TypeMigrationProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/typeMigration/TypeMigrationProcessor.java @@ -33,7 +33,9 @@ import com.intellij.ui.content.Content; import com.intellij.usageView.UsageInfo; import com.intellij.usageView.UsageViewDescriptor; import com.intellij.usageView.UsageViewManager; +import com.intellij.util.Consumer; import com.intellij.util.IncorrectOperationException; +import com.intellij.util.SmartList; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; @@ -180,6 +182,8 @@ public class TypeMigrationProcessor extends BaseRefactoringProcessor { } public static void change(TypeMigrationLabeler labeler, UsageInfo[] usages) { + final List newExpressionsToCheckDiamonds = new SmartList(); + List nonCodeUsages = new ArrayList(); for (UsageInfo usage : usages) { if (((TypeMigrationUsageInfo)usage).isExcluded()) continue; @@ -188,12 +192,22 @@ public class TypeMigrationProcessor extends BaseRefactoringProcessor { element instanceof PsiMember || element instanceof PsiExpression || element instanceof PsiReferenceParameterList) { - labeler.change((TypeMigrationUsageInfo)usage); + labeler.change((TypeMigrationUsageInfo)usage, new Consumer() { + @Override + public void consume(@NotNull PsiNewExpression expression) { + newExpressionsToCheckDiamonds.add(expression); + } + }); } else { nonCodeUsages.add(usage); } } + + for (PsiNewExpression newExpression : newExpressionsToCheckDiamonds) { + labeler.postProcessNewExpression(newExpression); + } + for (UsageInfo usageInfo : nonCodeUsages) { final PsiElement element = usageInfo.getElement(); if (element != null) { diff --git a/java/java-impl/src/com/intellij/refactoring/typeMigration/TypeMigrationReplacementUtil.java b/java/java-impl/src/com/intellij/refactoring/typeMigration/TypeMigrationReplacementUtil.java index 873f6230f09b..0611af494c47 100644 --- a/java/java-impl/src/com/intellij/refactoring/typeMigration/TypeMigrationReplacementUtil.java +++ b/java/java-impl/src/com/intellij/refactoring/typeMigration/TypeMigrationReplacementUtil.java @@ -25,6 +25,7 @@ import com.intellij.psi.impl.source.tree.CompositeElement; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.refactoring.typeMigration.usageInfo.TypeMigrationUsageInfo; import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.Nullable; import java.util.Map; @@ -138,7 +139,7 @@ public class TypeMigrationReplacementUtil { } } - static void replaceNewExpressionType(final Project project, final PsiNewExpression expression, final Map.Entry info) { + static PsiNewExpression replaceNewExpressionType(final Project project, final PsiNewExpression expression, final Map.Entry info) { final PsiType changeType = info.getValue(); if (changeType != null) { try { @@ -147,11 +148,8 @@ public class TypeMigrationReplacementUtil { if (classReference != null) { final PsiElement psiElement = replaceTypeWithClassReferenceOrKeyword(project, componentType, classReference); final PsiNewExpression newExpression = PsiTreeUtil.getParentOfType(psiElement, PsiNewExpression.class); - if (newExpression != null && PsiDiamondTypeUtil.canCollapseToDiamond(newExpression, newExpression, changeType)) { - final PsiJavaCodeReferenceElement anonymousClassReference = newExpression.getClassOrAnonymousClassReference(); - if (anonymousClassReference != null) { - PsiDiamondTypeUtil.replaceExplicitWithDiamond(anonymousClassReference.getParameterList()); - } + if (!tryToReplaceWithDiamond(newExpression, changeType)) { + return newExpression; } } else { @@ -165,6 +163,18 @@ public class TypeMigrationReplacementUtil { LOG.error(e); } } + return null; + } + + static boolean tryToReplaceWithDiamond(PsiNewExpression newExpression, @Nullable PsiType changeType) { + if (newExpression != null && PsiDiamondTypeUtil.canCollapseToDiamond(newExpression, newExpression, changeType)) { + final PsiJavaCodeReferenceElement anonymousClassReference = newExpression.getClassOrAnonymousClassReference(); + if (anonymousClassReference != null) { + PsiDiamondTypeUtil.replaceExplicitWithDiamond(anonymousClassReference.getParameterList()); + } + return true; + } + return false; } private static PsiElement replaceTypeWithClassReferenceOrKeyword(Project project, PsiType componentType, PsiElement typePlace) { diff --git a/java/java-impl/src/com/intellij/refactoring/typeMigration/ui/MigrationNode.java b/java/java-impl/src/com/intellij/refactoring/typeMigration/ui/MigrationNode.java index 70acb48def6c..08b1679db0d1 100644 --- a/java/java-impl/src/com/intellij/refactoring/typeMigration/ui/MigrationNode.java +++ b/java/java-impl/src/com/intellij/refactoring/typeMigration/ui/MigrationNode.java @@ -81,10 +81,8 @@ public class MigrationNode extends AbstractTreeNode impl final PsiElement element = myInfo.getElement(); if (element != null) { - myLabeler.setCurrentRoot(myInfo); - try { - myLabeler.migrateRoot(element, myMigrationType, myLabeler.markRootUsages(element, myMigrationType)); + myLabeler.setRootAndMigrate(myInfo, myMigrationType, myLabeler.markRootUsages(element, myMigrationType)); } catch (TypeMigrationLabeler.MigrateException e) { //skip warning diff --git a/java/java-impl/src/com/intellij/refactoring/typeMigration/usageInfo/TypeMigrationUsageInfo.java b/java/java-impl/src/com/intellij/refactoring/typeMigration/usageInfo/TypeMigrationUsageInfo.java index b1a87354df0c..120838a7ce25 100644 --- a/java/java-impl/src/com/intellij/refactoring/typeMigration/usageInfo/TypeMigrationUsageInfo.java +++ b/java/java-impl/src/com/intellij/refactoring/typeMigration/usageInfo/TypeMigrationUsageInfo.java @@ -18,6 +18,7 @@ package com.intellij.refactoring.typeMigration.usageInfo; import com.intellij.psi.PsiElement; import com.intellij.usageView.UsageInfo; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * @author anna @@ -25,6 +26,7 @@ import org.jetbrains.annotations.NotNull; */ public class TypeMigrationUsageInfo extends UsageInfo { private boolean myExcluded; + private TypeMigrationUsageInfo myOwnerRoot; public TypeMigrationUsageInfo(@NotNull PsiElement element) { @@ -39,4 +41,16 @@ public class TypeMigrationUsageInfo extends UsageInfo { return myExcluded; } + public TypeMigrationUsageInfo getOwnerRoot() { + return myOwnerRoot; + } + + public void setOwnerRoot(TypeMigrationUsageInfo ownerRoot) { + myOwnerRoot = ownerRoot; + } + + @Override + public String toString() { + return getClass().getSimpleName() + ":" + getElement(); + } } diff --git a/java/java-impl/src/com/intellij/unscramble/UnscrambleDialog.java b/java/java-impl/src/com/intellij/unscramble/UnscrambleDialog.java index 7cca43290ffe..152d084258ae 100644 --- a/java/java-impl/src/com/intellij/unscramble/UnscrambleDialog.java +++ b/java/java-impl/src/com/intellij/unscramble/UnscrambleDialog.java @@ -19,7 +19,6 @@ import com.intellij.execution.ui.RunContentDescriptor; import com.intellij.icons.AllIcons; import com.intellij.ide.IdeBundle; import com.intellij.ide.util.PropertiesComponent; -import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.fileChooser.FileChooser; import com.intellij.openapi.fileChooser.FileChooserDescriptor; import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; @@ -54,7 +53,6 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import static com.intellij.util.containers.ContainerUtil.ar; @@ -93,18 +91,21 @@ public class UnscrambleDialog extends DialogWrapper { populateRegisteredUnscramblerList(); myUnscrambleChooser.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { UnscrambleSupport unscrambleSupport = getSelectedUnscrambler(); GuiUtils.enableChildren(myLogFileChooserPanel, unscrambleSupport != null); } }); myUseUnscrambler.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { useUnscramblerChanged(); } }); myOnTheFly.setSelected(Registry.get("analyze.exceptions.on.the.fly").asBoolean()); myOnTheFly.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { Registry.get("analyze.exceptions.on.the.fly").setValue(myOnTheFly.isSelected()); } @@ -172,10 +173,9 @@ public class UnscrambleDialog extends DialogWrapper { @Nullable private UnscrambleSupport getSavedUnscrambler() { - final List registeredUnscramblers = getRegisteredUnscramblers(); final String savedUnscramblerName = getPropertyValue(PROPERTY_UNSCRAMBLER_NAME_USED); UnscrambleSupport selectedUnscrambler = null; - for (final UnscrambleSupport unscrambleSupport : registeredUnscramblers) { + for (UnscrambleSupport unscrambleSupport : UnscrambleSupport.EP_NAME.getExtensions()) { if (Comparing.strEqual(unscrambleSupport.getPresentableName(), savedUnscramblerName)) { selectedUnscrambler = unscrambleSupport; } @@ -206,11 +206,13 @@ public class UnscrambleDialog extends DialogWrapper { myEditorPanel.add(myStacktraceEditorPanel, BorderLayout.CENTER); } + @Override @NotNull protected Action[] createActions(){ return new Action[]{createNormalizeTextAction(), getOKAction(), getCancelAction(), getHelpAction()}; } + @Override public JComponent getPreferredFocusedComponent() { JRootPane pane = getRootPane(); return pane != null ? pane.getDefaultButton() : super.getPreferredFocusedComponent(); @@ -219,6 +221,7 @@ public class UnscrambleDialog extends DialogWrapper { private void createLogFileChooser() { myLogFile = new TextFieldWithHistory(); JPanel panel = GuiUtils.constructFieldWithBrowseButton(myLogFile, new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor(); FileChooser.chooseFiles(descriptor, myProject, null, new Consumer>() { @@ -234,10 +237,11 @@ public class UnscrambleDialog extends DialogWrapper { } private void populateRegisteredUnscramblerList() { - List unscrambleComponents = getRegisteredUnscramblers(); - for (final UnscrambleSupport unscrambleSupport : unscrambleComponents) { + for (UnscrambleSupport unscrambleSupport : UnscrambleSupport.EP_NAME.getExtensions()) { + //noinspection unchecked myUnscrambleChooser.addItem(unscrambleSupport); } + //noinspection unchecked myUnscrambleChooser.setRenderer(new ListCellRendererWrapper() { @Override public void customize(JList list, UnscrambleSupport unscrambleSupport, int index, boolean selected, boolean hasFocus) { @@ -246,26 +250,29 @@ public class UnscrambleDialog extends DialogWrapper { }); } - private static List getRegisteredUnscramblers() { - final UnscrambleSupport[] components = Extensions.getExtensions(UnscrambleSupport.EP_NAME); - return Arrays.asList(components); - } - + @Override protected JComponent createCenterPanel() { return myPanel; } + @Override public void dispose() { if (isOK()){ final List list = myLogFile.getHistory(); PropertiesComponent.getInstance().setValue(PROPERTY_LOG_FILE_HISTORY_URLS, list.isEmpty() ? null : StringUtil.join(list, ":::"), null); UnscrambleSupport selectedUnscrambler = getSelectedUnscrambler(); - PropertiesComponent.getInstance().setValue(PROPERTY_UNSCRAMBLER_NAME_USED, selectedUnscrambler == null ? null : selectedUnscrambler.getPresentableName(), null); - PropertiesComponent.getInstance().setValue(PROPERTY_LOG_FILE_LAST_URL, StringUtil.nullize(myLogFile.getText()), null); + saveProperty(PROPERTY_UNSCRAMBLER_NAME_USED, selectedUnscrambler == null ? null : selectedUnscrambler.getPresentableName()); + saveProperty(PROPERTY_LOG_FILE_LAST_URL, StringUtil.nullize(myLogFile.getText())); } super.dispose(); } + // IDEA-125302 The Analyze Stacktrace menu option remembers only one log file across multiple projects + private void saveProperty(@NotNull String name, @Nullable String value) { + PropertiesComponent.getInstance(myProject).setValue(name, value); + PropertiesComponent.getInstance().setValue(name, value); + } + @Nullable private String getPropertyValue(@NotNull String name) { String projectValue = PropertiesComponent.getInstance(myProject).getValue(name); @@ -289,6 +296,7 @@ public class UnscrambleDialog extends DialogWrapper { putValue(DEFAULT_ACTION, Boolean.FALSE); } + @Override public void actionPerformed(ActionEvent e){ String text = myStacktraceEditorPanel.getText(); myStacktraceEditorPanel.setText(normalizeText(text)); @@ -366,6 +374,7 @@ public class UnscrambleDialog extends DialogWrapper { return false; } + @Override protected void doOKAction() { if (myConfigurable != null && myConfigurable.isModified()) { try { @@ -382,6 +391,7 @@ public class UnscrambleDialog extends DialogWrapper { } } + @Override public void doHelpAction() { HelpManager.getInstance().invokeHelp("find.analyzeStackTrace"); } @@ -423,6 +433,7 @@ public class UnscrambleDialog extends DialogWrapper { return AnalyzeStacktraceUtil.addConsole(project, threadDump.size() > 1 ? new ThreadDumpConsoleFactory(project, threadDump) : null, message, unscrambledTrace, icon); } + @Override protected String getDimensionServiceKey(){ return "#com.intellij.unscramble.UnscrambleDialog"; } @@ -474,5 +485,4 @@ public class UnscrambleDialog extends DialogWrapper { } return builder.toString(); } - } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/StrictSubtypingConstraint.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/StrictSubtypingConstraint.java index e84125be57c4..e4fdee4605fb 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/StrictSubtypingConstraint.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/StrictSubtypingConstraint.java @@ -160,6 +160,6 @@ public class StrictSubtypingConstraint implements ConstraintFormula { @Override public String toString() { - return myS.getPresentableText() + " < " + myT.getPresentableText(); + return myT.getPresentableText() + " < " + myS.getPresentableText(); } } diff --git a/java/java-psi-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java b/java/java-psi-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java index 39187e807fb9..5ce45ebe04dd 100644 --- a/java/java-psi-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java +++ b/java/java-psi-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java @@ -24,6 +24,7 @@ import com.intellij.openapi.util.Computable; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; import com.intellij.psi.impl.PsiSuperMethodImplUtil; +import com.intellij.psi.impl.source.PsiImmediateClassType; import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession; import com.intellij.psi.infos.CandidateInfo; import com.intellij.psi.infos.MethodCandidateInfo; @@ -687,26 +688,29 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{ private boolean isApplicableTo(@NotNull PsiType[] types2AtSite, @NotNull PsiMethod method1, - @NotNull LanguageLevel languageLevel, + @NotNull final LanguageLevel languageLevel, boolean varargsPosition, @NotNull PsiSubstitutor methodSubstitutor1, - @NotNull PsiMethod method2, - PsiSubstitutor siteSubstitutor1) { + @NotNull PsiMethod method2, + final PsiSubstitutor siteSubstitutor1) { if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8) && method1.getTypeParameters().length > 0 && myArgumentsList instanceof PsiExpressionList) { final PsiElement parent = myArgumentsList.getParent(); if (parent instanceof PsiCallExpression) { return InferenceSession.isMoreSpecific(method2, method1, siteSubstitutor1, ((PsiExpressionList)myArgumentsList).getExpressions(), myArgumentsList, varargsPosition); } } - final PsiUtil.ApplicabilityChecker applicabilityChecker = languageLevel.isAtLeast(LanguageLevel.JDK_1_8) - ? new PsiUtil.ApplicabilityChecker() { + final PsiUtil.ApplicabilityChecker applicabilityChecker = new PsiUtil.ApplicabilityChecker() { @Override - public boolean isApplicable(PsiType left, PsiType right, - boolean allowUncheckedConversion, int argId) { - return isTypeMoreSpecific(left, right, argId); + public boolean isApplicable(PsiType left, PsiType right, boolean allowUncheckedConversion, int argId) { + if (right instanceof PsiClassType) { + final PsiClass rightClass = ((PsiClassType)right).resolve(); + if (rightClass instanceof PsiTypeParameter) { + right = new PsiImmediateClassType(rightClass, siteSubstitutor1); + } + } + return languageLevel.isAtLeast(LanguageLevel.JDK_1_8) ? isTypeMoreSpecific(left, right, argId) : TypeConversionUtil.isAssignable(left, right, allowUncheckedConversion); } - } - : PsiUtil.ApplicabilityChecker.ASSIGNABILITY_CHECKER; + }; final int applicabilityLevel = PsiUtil.getApplicabilityLevel(method1, methodSubstitutor1, types2AtSite, languageLevel, false, varargsPosition, applicabilityChecker); return applicabilityLevel > MethodCandidateInfo.ApplicabilityLevel.NOT_APPLICABLE; } diff --git a/java/java-tests/testData/codeInsight/completion/keywords/noPrimitivesInBooleanAnnotationAttribute.java b/java/java-tests/testData/codeInsight/completion/keywords/noPrimitivesInBooleanAnnotationAttribute.java new file mode 100644 index 000000000000..7cf8d9765b33 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/keywords/noPrimitivesInBooleanAnnotationAttribute.java @@ -0,0 +1,5 @@ +@interface Foo { + boolean foo(); +} + +@Foo(foo=) \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/keywords/noPrimitivesInEnumAnnotationAttribute.java b/java/java-tests/testData/codeInsight/completion/keywords/noPrimitivesInEnumAnnotationAttribute.java new file mode 100644 index 000000000000..a1ba97625987 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/keywords/noPrimitivesInEnumAnnotationAttribute.java @@ -0,0 +1,6 @@ +@interface Foo { + MyEnum value(); +} +enum MyEnum { x, y } + +@Foo() \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/keywords/noPrimitivesInIntAnnotationValueAttribute.java b/java/java-tests/testData/codeInsight/completion/keywords/noPrimitivesInIntAnnotationValueAttribute.java new file mode 100644 index 000000000000..12868cf66116 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/keywords/noPrimitivesInIntAnnotationValueAttribute.java @@ -0,0 +1,5 @@ +@interface Foo { + int value(); +} + +@Foo() \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/keywords/primitivesInClassAnnotationAttribute.java b/java/java-tests/testData/codeInsight/completion/keywords/primitivesInClassAnnotationAttribute.java new file mode 100644 index 000000000000..3ed64ecd7353 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/keywords/primitivesInClassAnnotationAttribute.java @@ -0,0 +1,5 @@ +@interface Foo { + Class attr(); +} + +@Foo(attr = ) \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/keywords/primitivesInClassAnnotationValueAttribute.java b/java/java-tests/testData/codeInsight/completion/keywords/primitivesInClassAnnotationValueAttribute.java new file mode 100644 index 000000000000..915beafdbeba --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/keywords/primitivesInClassAnnotationValueAttribute.java @@ -0,0 +1,5 @@ +@interface Foo { + Class value(); +} + +@Foo() \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/overloadResolution/SiteSubstituteTypeParameterBoundsWhenCheckForMostSpecific.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/overloadResolution/SiteSubstituteTypeParameterBoundsWhenCheckForMostSpecific.java new file mode 100644 index 000000000000..b98bf2e4af8a --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/overloadResolution/SiteSubstituteTypeParameterBoundsWhenCheckForMostSpecific.java @@ -0,0 +1,21 @@ + +import java.util.ArrayList; +import java.util.List; + +class Test { + + public static class Foo { + public void set(Iterable v) { + System.out.println(v); + } + + public > void set(Y v) { + System.out.println(v); + } + } + + static void main(Foo doubleFoo, ArrayList data) { + doubleFoo.set(data); + } + +} diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/KeywordCompletionTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/completion/KeywordCompletionTest.java index ec6cf94b62ac..ddc784b08a25 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/KeywordCompletionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/KeywordCompletionTest.java @@ -112,7 +112,13 @@ public class KeywordCompletionTest extends LightCompletionTestCase { selectItem(myItems[0], '!'); checkResultByFile(BASE_PATH + "/" + getTestName(true) + "_after.java"); } - + + public void testNoPrimitivesInBooleanAnnotationAttribute() { doTest(1, "true", "int", "boolean"); } + public void testNoPrimitivesInIntAnnotationValueAttribute() { doTest(0, "true", "int", "boolean"); } + public void testNoPrimitivesInEnumAnnotationAttribute() { doTest(0, "true", "int", "boolean"); } + public void testPrimitivesInClassAnnotationValueAttribute() { doTest(2, "true", "int", "boolean"); } + public void testPrimitivesInClassAnnotationAttribute() { doTest(3, "true", "int", "boolean"); } + public void testImportStatic() throws Exception { doTest(1, "static"); } public void testAbstractInInterface() throws Exception { doTest(1, "abstract"); } public void testCharInAnnotatedParameter() throws Exception { doTest(1, "char"); } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/MagicConstantCompletionTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/completion/MagicConstantCompletionTest.groovy index 4181ec97b95a..fcdd3a794120 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/MagicConstantCompletionTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/MagicConstantCompletionTest.groovy @@ -54,7 +54,7 @@ class Foo { myFixture.configureByText "a.java", """ class Bar { - static void foo(ModifierList ml) { + static void foo() { if (getConstant() == ) {} } @@ -75,6 +75,28 @@ interface Foo { myFixture.assertPreferredCompletionItems 0, 'BAR', 'FOO' } + public void "test magic constant in equality before another equality"() { + addMagicConstant() + + myFixture.configureByText "a.java", """ +class Bar { + static void foo() { + if (getConstant() == getConstant() == 2) {} + } + + @org.intellij.lang.annotations.MagicConstant(flagsFromClass = Foo.class) + public native int getConstant(); +} + +interface Foo { + int FOO = 1; + int BAR = 2; +} +""" + myFixture.complete(CompletionType.SMART) + myFixture.assertPreferredCompletionItems 0, 'BAR', 'FOO' + } + private PsiClass addModifierList() { addMagicConstant() diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/OverloadResolutionTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/OverloadResolutionTest.java index d63551e22943..5f6403ff976d 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/OverloadResolutionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/OverloadResolutionTest.java @@ -159,6 +159,10 @@ public class OverloadResolutionTest extends LightDaemonAnalyzerTestCase { doTest(); } + public void testSiteSubstituteTypeParameterBoundsWhenCheckForMostSpecific() throws Exception { + doTest(); + } + private void doTest() { doTest(true); } diff --git a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavadocFormatterTest.java b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavadocFormatterTest.java index 6ba198548f53..ee8b628cd478 100644 --- a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavadocFormatterTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavadocFormatterTest.java @@ -227,9 +227,11 @@ public class JavadocFormatterTest extends AbstractJavaFormatterTest { getSettings().getRootSettings().ENABLE_JAVADOC_FORMATTING = true; getSettings().getRootSettings().WRAP_COMMENTS = true; getSettings().RIGHT_MARGIN = 20; - LanguageLevelProjectExtension.getInstance(getProject()).setLanguageLevel(LanguageLevel.JDK_1_7); - - doTextTest("/**\n" + " *

\n" + " * Another paragraph of the description placed after blank line.\n" + " */\n" + "class A{}", + doTextTest("/**\n" + + " *

\n" + + " * Another paragraph of the description placed after blank line.\n" + + " */\n" + + "class A{}", "/**\n" + " *

\n" + " * Another paragraph\n" + @@ -240,26 +242,36 @@ public class JavadocFormatterTest extends AbstractJavaFormatterTest { "class A {\n" + "}"); } - - public void testSCR2632_JDK8_LanguageLevel() throws Exception { + + public void test_PreserveExistingSelfClosingTags_AndGenerateOnlyPTag() { getSettings().getRootSettings().ENABLE_JAVADOC_FORMATTING = true; - getSettings().getRootSettings().WRAP_COMMENTS = true; - getSettings().RIGHT_MARGIN = 20; - LanguageLevelProjectExtension.getInstance(getProject()).setLanguageLevel(LanguageLevel.JDK_1_8); - - doTextTest("/**\n" + " *

\n" + " * Another paragraph of the description placed after blank line.\n" + " */\n" + "class A{}", - "/**\n" + - " *

\n" + - " * Another paragraph\n" + - " * of the description\n" + - " * placed after\n" + - " * blank line.\n" + - " */\n" + - "class A {\n" + - "}"); + LanguageLevel before = LanguageLevelProjectExtension.getInstance(getProject()).getLanguageLevel(); + LanguageLevelProjectExtension.getInstance(getProject()).setLanguageLevel(LanguageLevel.JDK_1_7); + try { + doTextTest( + "/**\n" + + " * My test comment\n" + + " *

\n" + + " * \n" + + " * With empty line\n" + + " */\n" + + "class T {\n" + + "}", + "/**\n" + + " * My test comment\n" + + " *

\n" + + " *

\n" + + " * With empty line\n" + + " */\n" + + "class T {\n" + + "}" + ); + } + finally { + LanguageLevelProjectExtension.getInstance(getProject()).setLanguageLevel(before); + } } - - + public void testParagraphTagGeneration() { // Inspired by IDEA-61811 getSettings().getRootSettings().ENABLE_JAVADOC_FORMATTING = true; @@ -280,7 +292,7 @@ public class JavadocFormatterTest extends AbstractJavaFormatterTest { "}", "/**\n" + " * line 1\n" + - " *

\n" + + " *

\n" + " * line 2\n" + " *

\n" +
       " *   line 3\n" +
@@ -793,7 +805,7 @@ public class JavadocFormatterTest extends AbstractJavaFormatterTest {
     doClassTest(before, after);
   }
 
-  public void testGenerateSelfClosingPTagIfLanguageLevelNotJava8() throws Exception {
+  public void testPTagIfLanguageLevelNotJava8() throws Exception {
     getSettings().getRootSettings().JD_P_AT_EMPTY_LINES = true;
     getSettings().getRootSettings().ENABLE_JAVADOC_FORMATTING = true;
     LanguageLevelProjectExtension.getInstance(getProject()).setLanguageLevel(LanguageLevel.JDK_1_7);
@@ -806,7 +818,7 @@ public class JavadocFormatterTest extends AbstractJavaFormatterTest {
                     "}\n";
     String after = "/**\n" +
                    " * Super method\n" +
-                   " * 

\n" + + " *

\n" + " * Super multiple times\n" + " */\n" + "public void voo() {\n" + diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchingVisitor.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchingVisitor.java index 15a8491bd7b9..f0cb8ebb9646 100644 --- a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchingVisitor.java +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchingVisitor.java @@ -21,7 +21,6 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.javadoc.PsiDocComment; import com.intellij.psi.javadoc.PsiDocTag; -import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.structuralsearch.MatchOptions; @@ -444,7 +443,6 @@ public class JavaMatchingVisitor extends JavaElementVisitor { final MatchingHandler allRemainingClassContentElementHandler = unmatchedHandlerName != null ? pattern.getHandler(unmatchedHandlerName) : null; MatchContext.MatchedElementsListener newListener = null; - assert javaPattern instanceof JavaCompiledPattern; if (allRemainingClassContentElementHandler != null) { myMatchingVisitor.getMatchContext().setMatchedElementsListener( newListener = new MatchContext.MatchedElementsListener() { @@ -686,29 +684,47 @@ public class JavaMatchingVisitor extends JavaElementVisitor { myMatchingVisitor.setResult(false); } - private static int countCStyleArrayDeclarationDims(final PsiElement type2) { - if (type2 != null) { - final PsiElement parentElement = type2.getParent(); - - if (parentElement instanceof PsiVariable) { - final PsiIdentifier psiIdentifier = ((PsiVariable)parentElement).getNameIdentifier(); - if (psiIdentifier == null) return 0; - - int count = 0; - for (PsiElement sibling = psiIdentifier.getNextSibling(); sibling != null; sibling = sibling.getNextSibling()) { - if (sibling instanceof PsiJavaToken) { - final IElementType tokenType = ((PsiJavaToken)sibling).getTokenType(); - if (tokenType == JavaTokenType.LBRACKET) ++count; - else if (tokenType != JavaTokenType.RBRACKET) break; - } - } - - return count; - } + private static int getArrayDimensions(final PsiElement element) { + if (element == null) { + return 0; + } + final PsiElement parent = element.getParent(); + if (parent instanceof PsiVariable) { + final PsiVariable variable = (PsiVariable)parent; + final PsiType type = variable.getType(); + return type.getArrayDimensions(); + } + else if (parent instanceof PsiMethod) { + final PsiMethod method = (PsiMethod)parent; + final PsiType type = method.getReturnType(); + return (type == null) ? 0 : type.getArrayDimensions(); + } + else if (element instanceof PsiTypeElement) { + final PsiTypeElement typeElement = (PsiTypeElement)element; + final PsiType type = typeElement.getType(); + return type.getArrayDimensions(); } return 0; } + private static PsiTypeElement getInnermostComponentTypeElement(PsiTypeElement typeElement) { + PsiElement child = typeElement.getFirstChild(); + while (child instanceof PsiTypeElement) { + typeElement = (PsiTypeElement)child; + child = typeElement.getFirstChild(); + } + return typeElement; + } + + private static PsiElement getInnermostComponent(PsiElement element) { + if (!(element instanceof PsiTypeElement)) { + return element; + } + final PsiTypeElement typeElement = (PsiTypeElement)element; + final PsiJavaCodeReferenceElement referenceElement = typeElement.getInnermostComponentReferenceElement(); + return (referenceElement != null) ? referenceElement : getInnermostComponentTypeElement(typeElement); + } + private void copyResults(final MatchResultImpl ourResult) { if (ourResult.hasSons()) { for (MatchResult son : ourResult.getAllSons()) { @@ -717,93 +733,62 @@ public class JavaMatchingVisitor extends JavaElementVisitor { } } - private boolean matchType(final PsiElement _type, final PsiElement _type2) { - PsiElement el = _type; - PsiElement el2 = _type2; - PsiType type1 = null; - PsiType type2 = null; + private boolean matchType(final PsiElement patternType, final PsiElement matchedType) { + PsiElement patternElement = getInnermostComponent(patternType); + PsiElement matchedElement = getInnermostComponent(matchedType); - // check for generics - if (_type instanceof PsiTypeElement && - ((PsiTypeElement)_type).getInnermostComponentReferenceElement() != null - ) { - el = ((PsiTypeElement)_type).getInnermostComponentReferenceElement(); - type1 = ((PsiTypeElement)_type).getType(); - } - - if (_type2 instanceof PsiTypeElement && - ((PsiTypeElement)_type2).getInnermostComponentReferenceElement() != null - ) { - el2 = ((PsiTypeElement)_type2).getInnermostComponentReferenceElement(); - type2 = ((PsiTypeElement)_type2).getType(); - } - - PsiElement[] typeparams = null; - if (el2 instanceof PsiJavaCodeReferenceElement) { - final PsiReferenceParameterList parameterList = ((PsiJavaCodeReferenceElement)el2).getParameterList(); + PsiElement[] typeParameters = null; + if (matchedElement instanceof PsiJavaCodeReferenceElement) { + final PsiReferenceParameterList parameterList = ((PsiJavaCodeReferenceElement)matchedElement).getParameterList(); if (parameterList != null) { - typeparams = parameterList.getTypeParameterElements(); + typeParameters = parameterList.getTypeParameterElements(); } } - else if (el2 instanceof PsiTypeParameter) { - el2 = ((PsiTypeParameter)el2).getNameIdentifier(); + else if (matchedElement instanceof PsiTypeParameter) { + matchedElement = ((PsiTypeParameter)matchedElement).getNameIdentifier(); } - else if (el2 instanceof PsiClass && ((PsiClass)el2).hasTypeParameters()) { - typeparams = ((PsiClass)el2).getTypeParameters(); - el2 = ((PsiClass)el2).getNameIdentifier(); + else if (matchedElement instanceof PsiClass && ((PsiClass)matchedElement).hasTypeParameters()) { + typeParameters = ((PsiClass)matchedElement).getTypeParameters(); + matchedElement = ((PsiClass)matchedElement).getNameIdentifier(); } - else if (el2 instanceof PsiMethod && ((PsiMethod)el2).hasTypeParameters()) { - typeparams = ((PsiMethod)_type2).getTypeParameters(); - el2 = ((PsiMethod)_type2).getNameIdentifier(); + else if (matchedElement instanceof PsiMethod && ((PsiMethod)matchedElement).hasTypeParameters()) { + typeParameters = ((PsiMethod)matchedType).getTypeParameters(); + matchedElement = ((PsiMethod)matchedType).getNameIdentifier(); } - PsiReferenceParameterList list = null; - if (el instanceof PsiJavaCodeReferenceElement) { - list = ((PsiJavaCodeReferenceElement)el).getParameterList(); - el = ((PsiJavaCodeReferenceElement)el).getReferenceNameElement(); - } - - if (list != null && list.getTypeParameterElements().length > 0) { - boolean result = typeparams != null && myMatchingVisitor.matchSequentially(list.getTypeParameterElements(), typeparams); - - if (!result) return false; - } - else { - if (_type2 instanceof PsiTypeElement) { - type2 = ((PsiTypeElement)_type2).getType(); - - if (typeparams == null || typeparams.length == 0) { - final PsiJavaCodeReferenceElement innermostComponentReferenceElement = - ((PsiTypeElement)_type2).getInnermostComponentReferenceElement(); - if (innermostComponentReferenceElement != null) el2 = innermostComponentReferenceElement; - } - else { - el2 = _type2; - } - } - } - - final int array2Dims = (type2 != null ? type2.getArrayDimensions() : 0) + countCStyleArrayDeclarationDims(_type2); - final int arrayDims = (type1 != null ? type1.getArrayDimensions() : 0) + countCStyleArrayDeclarationDims(_type); - - if (myMatchingVisitor.getMatchContext().getPattern().isTypedVar(el)) { - final SubstitutionHandler handler = (SubstitutionHandler)myMatchingVisitor.getMatchContext().getPattern().getHandler(el); - - RegExpPredicate regExpPredicate = null; - - if (arrayDims != 0) { - if (arrayDims != array2Dims) { + if (patternElement instanceof PsiJavaCodeReferenceElement) { + final PsiJavaCodeReferenceElement referenceElement = (PsiJavaCodeReferenceElement)patternElement; + final PsiReferenceParameterList list = referenceElement.getParameterList(); + if (list != null) { + final PsiTypeElement[] elements = list.getTypeParameterElements(); + if (elements.length > 0 && (typeParameters == null || !myMatchingVisitor.matchSequentially(elements, typeParameters))) { return false; } } - else if (array2Dims != 0) { + patternElement = referenceElement.getReferenceNameElement(); + } + + final int matchedArrayDimensions = getArrayDimensions(matchedType); + final int patternArrayDimensions = getArrayDimensions(patternType); + + if (myMatchingVisitor.getMatchContext().getPattern().isTypedVar(patternElement)) { + final SubstitutionHandler handler = (SubstitutionHandler)myMatchingVisitor.getMatchContext().getPattern().getHandler(patternElement); + + RegExpPredicate regExpPredicate = null; + + if (patternArrayDimensions != 0) { + if (patternArrayDimensions != matchedArrayDimensions) { + return false; + } + } + else if (matchedArrayDimensions != 0) { regExpPredicate = MatchingHandler.getSimpleRegExpPredicate(handler); if (regExpPredicate != null) { regExpPredicate.setNodeTextGenerator(new RegExpPredicate.NodeTextGenerator() { public String getText(PsiElement element) { StringBuilder builder = new StringBuilder(RegExpPredicate.getMeaningfulText(element)); - for (int i = 0; i < array2Dims; ++i) builder.append("[]"); + for (int i = 0; i < matchedArrayDimensions; ++i) builder.append("[]"); return builder.toString(); } }); @@ -812,10 +797,10 @@ public class JavaMatchingVisitor extends JavaElementVisitor { try { if (handler.isSubtype() || handler.isStrictSubtype()) { - return checkMatchWithingHierarchy(el2, handler, el); + return checkMatchWithingHierarchy(matchedElement, handler, patternElement); } else { - return handler.handle(el2, myMatchingVisitor.getMatchContext()); + return handler.handle(matchedElement, myMatchingVisitor.getMatchContext()); } } finally { @@ -823,31 +808,31 @@ public class JavaMatchingVisitor extends JavaElementVisitor { } } - if (array2Dims != arrayDims) { + if (matchedArrayDimensions != patternArrayDimensions) { return false; } - if (el instanceof PsiIdentifier) { - final PsiElement parent = el.getParent(); + if (patternElement instanceof PsiIdentifier) { + final PsiElement parent = patternElement.getParent(); if (parent instanceof PsiJavaCodeReferenceElement) { - el = parent; + patternElement = parent; } } - if (el2 instanceof PsiIdentifier) { - final PsiElement parent = el2.getParent(); + if (matchedElement instanceof PsiIdentifier) { + final PsiElement parent = matchedElement.getParent(); if (parent instanceof PsiJavaCodeReferenceElement) { - el2 = parent; + matchedElement = parent; } } - final String text = getText(el); - final String text2 = getText(el2); + final String text = getText(patternElement); + final String text2 = getText(matchedElement); final boolean caseSensitive = myMatchingVisitor.getMatchContext().getOptions().isCaseSensitiveMatch(); final boolean equalsIgnorePackage = MatchUtils.compareWithNoDifferenceToPackage(text, text2, !caseSensitive); - if (equalsIgnorePackage || !(el2 instanceof PsiJavaReference)) { + if (equalsIgnorePackage || !(matchedElement instanceof PsiJavaReference)) { return equalsIgnorePackage; } else { - final PsiElement element2 = ((PsiJavaReference)el2).resolve(); + final PsiElement element2 = ((PsiJavaReference)matchedElement).resolve(); if (element2 instanceof PsiClass) { final String name = ((PsiClass)element2).getQualifiedName(); diff --git a/java/typeMigration/test/com/intellij/codeInsight/inspections/GuavaInspectionTest.java b/java/typeMigration/test/com/intellij/codeInsight/inspections/GuavaInspectionTest.java index ca47339be872..8afd272fb25a 100644 --- a/java/typeMigration/test/com/intellij/codeInsight/inspections/GuavaInspectionTest.java +++ b/java/typeMigration/test/com/intellij/codeInsight/inspections/GuavaInspectionTest.java @@ -47,19 +47,19 @@ public class GuavaInspectionTest extends JavaCodeInsightFixtureTestCase { moduleBuilder.addJdk(IdeaTestUtil.getMockJdk18Path().getPath()); } - public void _testOptional() { + public void testOptional() { doTest(); } - public void _testOptional2() { + public void testOptional2() { doTest(); } - public void _testSimpleFluentIterable() { + public void testSimpleFluentIterable() { doTest(); } - public void _testChainedFluentIterable() { + public void testChainedFluentIterable() { doTest(); } @@ -67,7 +67,7 @@ public class GuavaInspectionTest extends JavaCodeInsightFixtureTestCase { doTest(); } - public void _testChainedFluentIterableWithChainedInitializer() { + public void testChainedFluentIterableWithChainedInitializer() { doTest(); } diff --git a/native/fileWatcher/fileWatcher3.cpp b/native/fileWatcher/fileWatcher3.cpp index 229bb4017082..358298ff286a 100644 --- a/native/fileWatcher/fileWatcher3.cpp +++ b/native/fileWatcher/fileWatcher3.cpp @@ -127,7 +127,7 @@ void PrintRemapForSubstDrives() { const int BUFSIZE = 1024; void PrintDirectoryReparsePoint(const char *path) { - int size = strlen(path) + 2; + int size = (int)(strlen(path) + 2); char *directory = (char *) malloc(size); strcpy_s(directory, size, path); NormalizeSlashes(directory, '\\'); diff --git a/native/fileWatcher/fileWatcher3.sln b/native/fileWatcher/fileWatcher3.sln index 7d39f3e10289..0ba0194e7b59 100644 --- a/native/fileWatcher/fileWatcher3.sln +++ b/native/fileWatcher/fileWatcher3.sln @@ -8,13 +8,19 @@ EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 Release|Win32 = Release|Win32 + Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {0EF2C2DB-0E16-4BC3-9927-9C51AF00EB57}.Debug|Win32.ActiveCfg = Debug|Win32 {0EF2C2DB-0E16-4BC3-9927-9C51AF00EB57}.Debug|Win32.Build.0 = Debug|Win32 + {0EF2C2DB-0E16-4BC3-9927-9C51AF00EB57}.Debug|x64.ActiveCfg = Debug|x64 + {0EF2C2DB-0E16-4BC3-9927-9C51AF00EB57}.Debug|x64.Build.0 = Debug|x64 {0EF2C2DB-0E16-4BC3-9927-9C51AF00EB57}.Release|Win32.ActiveCfg = Release|Win32 {0EF2C2DB-0E16-4BC3-9927-9C51AF00EB57}.Release|Win32.Build.0 = Release|Win32 + {0EF2C2DB-0E16-4BC3-9927-9C51AF00EB57}.Release|x64.ActiveCfg = Release|x64 + {0EF2C2DB-0E16-4BC3-9927-9C51AF00EB57}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/native/fileWatcher/fileWatcher3.vcxproj b/native/fileWatcher/fileWatcher3.vcxproj index 5115e0ae1b45..1e92da2cb084 100644 --- a/native/fileWatcher/fileWatcher3.vcxproj +++ b/native/fileWatcher/fileWatcher3.vcxproj @@ -5,10 +5,18 @@ Debug Win32 + + Debug + x64 + Release Win32 + + Release + x64 + {0EF2C2DB-0E16-4BC3-9927-9C51AF00EB57} @@ -22,20 +30,37 @@ Unicode true + + Application + v120_xp + Unicode + true + Application v120_xp Unicode + + Application + v120_xp + Unicode + + + + + + + <_ProjectFileVersion>12.0.21005.1 @@ -46,12 +71,24 @@ false fsnotifier + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + false + fsnotifier64 + $(SolutionDir)$(Configuration)\ $(Configuration)\ false fsnotifier + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + false + fsnotifier64 + Disabled @@ -70,6 +107,24 @@ MachineX86 + + + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + Level3 + EditAndContinue + + + mpr.lib;%(AdditionalDependencies) + true + Console + MachineX64 + + MaxSpeed @@ -90,6 +145,26 @@ MachineX86 + + + MaxSpeed + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreaded + true + + Level3 + ProgramDatabase + + + mpr.lib;%(AdditionalDependencies) + true + Console + true + true + MachineX64 + + diff --git a/platform/core-api/src/com/intellij/openapi/components/PersistentStateComponent.java b/platform/core-api/src/com/intellij/openapi/components/PersistentStateComponent.java index 70f556d479b0..64d3cfaf8a32 100644 --- a/platform/core-api/src/com/intellij/openapi/components/PersistentStateComponent.java +++ b/platform/core-api/src/com/intellij/openapi/components/PersistentStateComponent.java @@ -15,29 +15,29 @@ */ package com.intellij.openapi.components; -import com.intellij.util.xmlb.XmlSerializer; import org.jetbrains.annotations.Nullable; /** * Every component which would like to persist its state across IDEA restarts * should implement this interface. * - * See JetBrains WIKI + * See IntelliJ Platform SDK DevGuide * for detailed description. + * + * Implementation must be thread-safe - a component should expect these methods to be called at any moment of its lifecycle. */ public interface PersistentStateComponent { /** - * @return a component state. All properties and public fields are serialized. Only values, which differ + * @return a component state. All properties, public and annotated fields are serialized. Only values, which differ * from default (i.e. the value of newly instantiated class) are serialized. null value indicates - * that no state should be stored - * @see XmlSerializer + * that no state should be stored. + * @see com.intellij.util.xmlb.XmlSerializer */ @Nullable T getState(); /** - * This method is called when new component state is loaded. A component should expect this method - * to be called at any moment of its lifecycle. The method can and will be called several times, if + * This method is called when new component state is loaded. The method can and will be called several times, if * config files were externally changed while IDEA running. * @param state loaded component state * @see com.intellij.util.xmlb.XmlSerializerUtil#copyBean(Object, Object) diff --git a/platform/core-api/src/com/intellij/psi/HintedReferenceHost.java b/platform/core-api/src/com/intellij/psi/HintedReferenceHost.java new file mode 100644 index 000000000000..cfb86476a3c4 --- /dev/null +++ b/platform/core-api/src/com/intellij/psi/HintedReferenceHost.java @@ -0,0 +1,51 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi; + +import org.jetbrains.annotations.NotNull; + +/** + * Implementing this interface allows for better control over the references associated with a PSI element. + * Instead of {@link PsiElement#getReferences()}, clients can call {@link #getReferences(PsiReferenceService.Hints)} and the implementation + * may use the hints for performance optimizations, e.g. to avoid finding and creating references that won't intersect the given offset + * ({@link com.intellij.psi.PsiReferenceService.Hints#offsetInElement}) or that have no change of resolving to a particular target + * ({@link com.intellij.psi.PsiReferenceService.Hints#target}). + * + * @since 144.* + * @author peter + */ +public interface HintedReferenceHost extends PsiElement { + + /** + * Same as {@link PsiElement#getReferences()}, but the implementation may take hints into account and return only references that match these hints. + * But it's not a hard requirement, so the clients should not rely that only matching references will be returned. + * @param hints the hints about the desired references + * @return the array of references, or an empty array if the element has no associated references. + */ + @NotNull + PsiReference[] getReferences(@NotNull PsiReferenceService.Hints hints); + + /** + * Normally in {@link PsiElement#findReferenceAt(int)}, all tree hierarchy is traversed bottom-up and each element is asked for references. + * Quite often it's not needed, because references tend to be contained close to the tree leaves, and their ancestors won't return anything + * useful for given offsets anyway. This method makes it possible to stop such bottom-up traversals early and thus improve performance, + * if the implementation knows for sure that no tree ancestor of this element can contain references matching the specified hints. + * @param hints the hints about the desired references + * @return false if there's no use in asking this element's ancestors for references with specified hints, true otherwise. + */ + boolean shouldAskParentForReferences(@NotNull PsiReferenceService.Hints hints); + +} diff --git a/platform/core-impl/src/com/intellij/ide/plugins/IdeaPluginDescriptorImpl.java b/platform/core-impl/src/com/intellij/ide/plugins/IdeaPluginDescriptorImpl.java index 6bba3ef86fcb..e28d31d5db81 100644 --- a/platform/core-impl/src/com/intellij/ide/plugins/IdeaPluginDescriptorImpl.java +++ b/platform/core-impl/src/com/intellij/ide/plugins/IdeaPluginDescriptorImpl.java @@ -686,10 +686,14 @@ public class IdeaPluginDescriptorImpl implements IdeaPluginDescriptor { } catch (IOException e) { path = getPath().getAbsolutePath(); } - if (ApplicationManager.getApplication() != null && ApplicationManager.getApplication().isInternal()) { + Application app = ApplicationManager.getApplication(); + if (app != null && app.isInternal()) { if (path.startsWith(PathManager.getHomePath() + File.separator + "out" + File.separator + "classes")) { return true; } + if (app.isUnitTestMode() && !path.startsWith(PathManager.getPluginsPath() + File.separatorChar)) { + return true; + } } return path.startsWith(PathManager.getPreInstalledPluginsPath()); diff --git a/platform/core-impl/src/com/intellij/lang/impl/PsiBuilderImpl.java b/platform/core-impl/src/com/intellij/lang/impl/PsiBuilderImpl.java index ec9cc0496de3..16f9eb90772f 100644 --- a/platform/core-impl/src/com/intellij/lang/impl/PsiBuilderImpl.java +++ b/platform/core-impl/src/com/intellij/lang/impl/PsiBuilderImpl.java @@ -780,16 +780,21 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder { myCachedTokenType = null; } + private IElementType remapCurrentToken() { + if (myCachedTokenType != null) return myCachedTokenType; + if (myRemapper != null) { + remapCurrentToken(myRemapper.filter(myLexTypes[myCurrentLexeme], myLexStarts[myCurrentLexeme], + myLexStarts[myCurrentLexeme + 1], myLexer.getBufferSequence())); + } + return myLexTypes[myCurrentLexeme]; + } + private IElementType calcTokenType() { if (eof()) return null; if (myRemapper != null) { - IElementType type = myLexTypes[myCurrentLexeme]; - type = myRemapper.filter(type, myLexStarts[myCurrentLexeme], myLexStarts[myCurrentLexeme + 1], myLexer.getBufferSequence()); - myLexTypes[myCurrentLexeme] = type; // filter may have changed the type + //remaps current token, and following, which remaps to spaces and comments skipWhitespace(); - type = myLexTypes[myCurrentLexeme]; - return type; } return myLexTypes[myCurrentLexeme]; } @@ -870,7 +875,7 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder { } private void skipWhitespace() { - while (myCurrentLexeme < myLexemeCount && whitespaceOrComment(myLexTypes[myCurrentLexeme])) { + while (myCurrentLexeme < myLexemeCount && whitespaceOrComment(remapCurrentToken())) { onSkip(myLexTypes[myCurrentLexeme], myLexStarts[myCurrentLexeme], myCurrentLexeme + 1 < myLexemeCount ? myLexStarts[myCurrentLexeme + 1] : myText.length()); myCurrentLexeme++; clearCachedTokenType(); diff --git a/platform/core-impl/src/com/intellij/openapi/editor/impl/EditorDocumentPriorities.java b/platform/core-impl/src/com/intellij/openapi/editor/impl/EditorDocumentPriorities.java index f105f980091c..157231cbbb36 100644 --- a/platform/core-impl/src/com/intellij/openapi/editor/impl/EditorDocumentPriorities.java +++ b/platform/core-impl/src/com/intellij/openapi/editor/impl/EditorDocumentPriorities.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2010 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,6 +33,7 @@ public class EditorDocumentPriorities { public static final int RANGE_MARKER = 40; public static final int FOLD_MODEL = 60; + public static final int LOGICAL_POSITION_CACHE = 65; public static final int EDITOR_TEXT_LAYOUT_CACHE = 70; public static final int EDITOR_TEXT_WIDTH_CACHE = 75; public static final int LEXER_EDITOR = 80; diff --git a/platform/core-impl/src/com/intellij/openapi/progress/util/ProgressWrapper.java b/platform/core-impl/src/com/intellij/openapi/progress/util/ProgressWrapper.java index 9f456276d5ff..36f1c760fea7 100644 --- a/platform/core-impl/src/com/intellij/openapi/progress/util/ProgressWrapper.java +++ b/platform/core-impl/src/com/intellij/openapi/progress/util/ProgressWrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ */ package com.intellij.openapi.progress.util; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.StandardProgressIndicator; import com.intellij.openapi.progress.WrappedProgressIndicator; @@ -30,8 +31,11 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class ProgressWrapper extends AbstractProgressIndicatorBase implements WrappedProgressIndicator, StandardProgressIndicator { + private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.progress.util.ProgressWrapper"); + private final ProgressIndicator myOriginal; private final boolean myCheckCanceledForMe; + private final int nested; protected ProgressWrapper(@NotNull ProgressIndicator original) { this(original, false); @@ -40,9 +44,12 @@ public class ProgressWrapper extends AbstractProgressIndicatorBase implements Wr protected ProgressWrapper(@NotNull ProgressIndicator original, boolean checkCanceledForMe) { myOriginal = original; myCheckCanceledForMe = checkCanceledForMe; + nested = 1 + (original instanceof ProgressWrapper ? ((ProgressWrapper)original).nested : -1); + if (nested > 50) { + LOG.error("Too many wrapped indicators"); + } } - @Override public final void cancel() { super.cancel(); diff --git a/platform/core-impl/src/com/intellij/psi/PsiReferenceServiceImpl.java b/platform/core-impl/src/com/intellij/psi/PsiReferenceServiceImpl.java index d77eb0a9f794..e493202b2ca3 100644 --- a/platform/core-impl/src/com/intellij/psi/PsiReferenceServiceImpl.java +++ b/platform/core-impl/src/com/intellij/psi/PsiReferenceServiceImpl.java @@ -31,8 +31,9 @@ public class PsiReferenceServiceImpl extends PsiReferenceService { if (element instanceof ContributedReferenceHost) { return Arrays.asList(ReferenceProvidersRegistry.getReferencesFromProviders(element, hints)); } - else { - return Arrays.asList(element.getReferences()); + if (element instanceof HintedReferenceHost) { + return Arrays.asList(((HintedReferenceHost)element).getReferences(hints)); } + return Arrays.asList(element.getReferences()); } } diff --git a/platform/core-impl/src/com/intellij/psi/impl/SharedPsiElementImplUtil.java b/platform/core-impl/src/com/intellij/psi/impl/SharedPsiElementImplUtil.java index c202ef617d18..6ed07a8e1ac6 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/SharedPsiElementImplUtil.java +++ b/platform/core-impl/src/com/intellij/psi/impl/SharedPsiElementImplUtil.java @@ -47,8 +47,12 @@ public class SharedPsiElementImplUtil { List referencesList = new ArrayList(); while (element != null) { addReferences(offset, element, referencesList); - offset = element.getStartOffsetInParent() + offset; if (element instanceof PsiFile) break; + if (element instanceof HintedReferenceHost && + !((HintedReferenceHost)element).shouldAskParentForReferences(new PsiReferenceService.Hints(null, offset))) { + break; + } + offset = element.getStartOffsetInParent() + offset; element = element.getParent(); } @@ -64,9 +68,15 @@ public class SharedPsiElementImplUtil { } private static void addReferences(int offset, PsiElement element, final Collection outReferences) { - for (final PsiReference reference : element.getReferences()) { + PsiReference[] references; + if (element instanceof HintedReferenceHost) { + references = ((HintedReferenceHost)element).getReferences(new PsiReferenceService.Hints(null, offset)); + } else { + references = element.getReferences(); + } + for (final PsiReference reference : references) { if (reference == null) { - LOG.error(element); + LOG.error("Null reference returned from " + element + " of " + element.getClass()); continue; } for (TextRange range : ReferenceRange.getRanges(reference)) { diff --git a/platform/editor-ui-api/src/com/intellij/ide/ui/AntialiasingType.java b/platform/editor-ui-api/src/com/intellij/ide/ui/AntialiasingType.java index 4cc37b39ea59..227e1fb17350 100644 --- a/platform/editor-ui-api/src/com/intellij/ide/ui/AntialiasingType.java +++ b/platform/editor-ui-api/src/com/intellij/ide/ui/AntialiasingType.java @@ -15,6 +15,7 @@ */ package com.intellij.ide.ui; +import com.intellij.util.ui.UIUtil; import sun.swing.SwingUtilities2; import java.awt.*; @@ -24,29 +25,31 @@ public enum AntialiasingType { GREYSCALE, OFF; - private static final SwingUtilities2.AATextInfo aaEnabled = - new SwingUtilities2.AATextInfo(RenderingHints.VALUE_TEXT_ANTIALIAS_ON, 140); - - private static final SwingUtilities2.AATextInfo lcdEnabled = - new SwingUtilities2.AATextInfo(RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB, 140); - private static final SwingUtilities2.AATextInfo aaDisabled = null; + private static SwingUtilities2.AATextInfo getLCDEnabledTextInfo () { + return new SwingUtilities2.AATextInfo(RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB, UIUtil.getLcdContrastValue()); + } + + private static SwingUtilities2.AATextInfo getAAEnabledTextInfo () { + return new SwingUtilities2.AATextInfo(RenderingHints.VALUE_TEXT_ANTIALIAS_ON, UIUtil.getLcdContrastValue()); + } + public static Object getAAHintForSwingComponent() { UISettings uiSettings = UISettings.getInstance(); - if (uiSettings == null) return aaEnabled; + if (uiSettings == null) return getAAEnabledTextInfo(); switch (uiSettings.IDE_AA_TYPE) { case SUBPIXEL: - return lcdEnabled; + return getLCDEnabledTextInfo(); case GREYSCALE: - return aaEnabled; + return getAAEnabledTextInfo(); case OFF: return aaDisabled; } - return aaEnabled; + return getAAEnabledTextInfo(); } public Object getRenderingHintValue () { diff --git a/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.java b/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.java index 5f0fb7766179..79b277ce1b7b 100644 --- a/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.java +++ b/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.java @@ -281,6 +281,9 @@ public class UISettings extends SimpleModificationTracker implements PersistentS */ public static void setupAntialiasing(final Graphics g) { + Graphics2D g2d = (Graphics2D)g; + g2d.setRenderingHint(RenderingHints.KEY_TEXT_LCD_CONTRAST, UIUtil.getLcdContrastValue()); + Application application = ApplicationManager.getApplication(); if (application == null) { // We cannot use services while Aplication has not been loaded yet @@ -289,7 +292,6 @@ public class UISettings extends SimpleModificationTracker implements PersistentS return; } - Graphics2D g2d = (Graphics2D)g; UISettings uiSettings = getInstance(); if (uiSettings != null) { diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/CodeStyleSchemeExporterUI.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/CodeStyleSchemeExporterUI.java new file mode 100644 index 000000000000..927485a3ad50 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/CodeStyleSchemeExporterUI.java @@ -0,0 +1,119 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.application.options.codeStyle; + +import com.intellij.openapi.application.ApplicationBundle; +import com.intellij.openapi.fileChooser.FileChooserFactory; +import com.intellij.openapi.fileChooser.FileSaverDescriptor; +import com.intellij.openapi.fileChooser.FileSaverDialog; +import com.intellij.openapi.options.SchemeExporter; +import com.intellij.openapi.options.SchemeExporterEP; +import com.intellij.openapi.options.SchemeExporterException; +import com.intellij.openapi.ui.MessageType; +import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.ui.popup.ListPopup; +import com.intellij.openapi.ui.popup.PopupStep; +import com.intellij.openapi.ui.popup.util.BaseListPopupStep; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileWrapper; +import com.intellij.psi.codeStyle.CodeStyleScheme; +import com.intellij.util.ArrayUtil; +import org.jetbrains.annotations.NotNull; + +import java.awt.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + + +/** + * @author Rustam Vishnyakov + */ +class CodeStyleSchemeExporterUI { + @NotNull private final Component myParentComponent; + @NotNull private final CodeStyleScheme myScheme; + @NotNull private final StatusCallback myStatusCallback; + + CodeStyleSchemeExporterUI(@NotNull Component parentComponent, @NotNull CodeStyleScheme scheme, @NotNull StatusCallback statusCallback) { + myParentComponent = parentComponent; + myScheme = scheme; + myStatusCallback = statusCallback; + } + + void export() { + ListPopup popup = JBPopupFactory.getInstance().createListPopup( + new BaseListPopupStep(ApplicationBundle.message("code.style.scheme.exporter.ui.export.as.title"), enumExporters()) { + @Override + public PopupStep onChosen(final String selectedValue, boolean finalChoice) { + return doFinalStep(new Runnable() { + @Override + public void run() { + exportSchemeUsing(selectedValue); + } + }); + } + }); + popup.showInCenterOf(myParentComponent); + } + + private static String[] enumExporters() { + List names = new ArrayList(); + Collection> extensions = SchemeExporterEP.getExtensions(CodeStyleScheme.class); + for (SchemeExporterEP extension : extensions) { + names.add(extension.name); + } + return ArrayUtil.toStringArray(names); + } + + private void exportSchemeUsing(@NotNull String exporterName) { + SchemeExporter exporter = SchemeExporterEP.getExporter(exporterName, CodeStyleScheme.class); + if (exporter != null) { + String ext = exporter.getExtension(); + FileSaverDialog saver = + FileChooserFactory.getInstance() + .createSaveFileDialog(new FileSaverDescriptor( + ApplicationBundle.message("code.style.scheme.exporter.ui.file.chooser.title"), + ApplicationBundle.message("code.style.scheme.exporter.ui.file.chooser.message"), + ext), myParentComponent); + VirtualFileWrapper target = saver.save(null, getFileNameSuggestion() + "." + ext); + if (target != null) { + VirtualFile targetFile = target.getVirtualFile(true); + if (targetFile != null) { + try { + exporter.exportScheme(myScheme, targetFile); + myStatusCallback + .showMessage(ApplicationBundle.message("code.style.scheme.exporter.ui.code.style.exported.message", myScheme.getName(), + targetFile.getPresentableUrl()), MessageType.INFO); + } + catch (SchemeExporterException e) { + myStatusCallback.showMessage(e.getMessage(), MessageType.ERROR); + } + } + else { + myStatusCallback.showMessage(ApplicationBundle.message("code.style.scheme.exporter.ui.cannot.write.message"), MessageType.ERROR); + } + } + } + } + + private String getFileNameSuggestion() { + return myScheme.getName(); + } + + interface StatusCallback { + void showMessage(@NotNull String message, @NotNull MessageType messageType); + } +} diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/ManageCodeStyleSchemesDialog.form b/platform/lang-impl/src/com/intellij/application/options/codeStyle/ManageCodeStyleSchemesDialog.form index 01eb0fb7de87..8c11896369d9 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/ManageCodeStyleSchemesDialog.form +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/ManageCodeStyleSchemesDialog.form @@ -75,7 +75,7 @@ - + diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/ManageCodeStyleSchemesDialog.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/ManageCodeStyleSchemesDialog.java index 40acf0f0b279..495a80772124 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/ManageCodeStyleSchemesDialog.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/ManageCodeStyleSchemesDialog.java @@ -22,10 +22,7 @@ import com.intellij.openapi.application.ApplicationBundle; import com.intellij.openapi.fileChooser.FileChooserDescriptor; import com.intellij.openapi.fileChooser.FileChooserDialog; import com.intellij.openapi.fileChooser.FileChooserFactory; -import com.intellij.openapi.options.SchemeFactory; -import com.intellij.openapi.options.SchemeImportException; -import com.intellij.openapi.options.SchemeImporter; -import com.intellij.openapi.options.SchemeImporterEP; +import com.intellij.openapi.options.*; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.MessageType; @@ -128,6 +125,19 @@ public class ManageCodeStyleSchemesDialog extends DialogWrapper { }); } + if (SchemeExporterEP.getExtensions(CodeStyleScheme.class).isEmpty()) { + myExportButton.setVisible(false); + } + else { + myExportButton.setVisible(true); + myExportButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + exportSelectedScheme(); + } + }); + } + init(); } @@ -453,4 +463,12 @@ public class ManageCodeStyleSchemesDialog extends DialogWrapper { } } + private void exportSelectedScheme() { + new CodeStyleSchemeExporterUI(myExportButton, getSelectedScheme(), new CodeStyleSchemeExporterUI.StatusCallback() { + @Override + public void showMessage(@NotNull String message, @NotNull MessageType messageType) { + showStatus(myExportButton, message, messageType); + } + }).export(); + } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonCodeAnalyzerImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonCodeAnalyzerImpl.java index 7b7dfb64941f..f6b857334c01 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonCodeAnalyzerImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonCodeAnalyzerImpl.java @@ -808,9 +808,24 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzerEx implements Pers return; } if (HeavyProcessLatch.INSTANCE.isRunning()) { - if (myAlarm.isEmpty()) { - // wait for heavy processing to stop, re-schedule daemon but not too soon - myAlarm.addRequest(myUpdateRunnable, Math.max(mySettings.AUTOREPARSE_DELAY,100)); + final Disposable removeListenerDisposable = Disposer.newDisposable(); + Disposer.register(DaemonCodeAnalyzerImpl.this, removeListenerDisposable); + // wait for heavy processing to stop, re-schedule daemon but not too soon + HeavyProcessLatch.HeavyProcessListener heavyProcessListener = new HeavyProcessLatch.HeavyProcessListener() { + @Override + public void processStarted() { + } + + @Override + public void processFinished() { + Disposer.dispose(removeListenerDisposable); + myAlarm.addRequest(myUpdateRunnable, Math.max(mySettings.AUTOREPARSE_DELAY, 100), ModalityState.NON_MODAL); + } + }; + HeavyProcessLatch.INSTANCE.addListener(removeListenerDisposable, heavyProcessListener); + if (!HeavyProcessLatch.INSTANCE.isRunning()) { + // in case heavy operation finished right before listener added + heavyProcessListener.processFinished(); } return; } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupCellRenderer.java b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupCellRenderer.java index 863c718a78c6..e3cf89da17ce 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupCellRenderer.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupCellRenderer.java @@ -468,7 +468,7 @@ public class LookupCellRenderer implements ListCellRenderer { @Override protected void applyAdditionalHints(@NotNull Graphics2D g) { - GraphicsUtil.setupAntialiasing(g); + super.applyAdditionalHints(g); } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/EditVariableDialog.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/EditVariableDialog.java index bab6ade7b935..50ca64075d5d 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/EditVariableDialog.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/EditVariableDialog.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -258,11 +258,13 @@ class EditVariableDialog extends DialogWrapper { public void setValueAt(Object aValue, int row, int col) { Variable variable = myVariables.get(row); if (col == 0) { - String varName = (String) aValue; - Variable newVar = new Variable (varName, variable.getExpressionString(), variable.getDefaultValueString(), - variable.isAlwaysStopAt()); - myVariables.set(row, newVar); - updateTemplateTextByVarNameChange(variable, newVar); + String varName = (String) aValue; + if (TemplateImplUtil.parseVariables("$" + varName + "$").containsKey(varName)) { + Variable newVar = new Variable(varName, variable.getExpressionString(), variable.getDefaultValueString(), + variable.isAlwaysStopAt()); + myVariables.set(row, newVar); + updateTemplateTextByVarNameChange(variable, newVar); + } } else if (col == 1) { variable.setExpressionString((String)aValue); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateState.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateState.java index 5b9bee532eb4..6369b09af00e 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateState.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateState.java @@ -166,7 +166,7 @@ public class TemplateState implements Disposable { } private boolean isCaretOutsideCurrentSegment() { - if (myEditor != null) { + if (myEditor != null && myCurrentSegmentNumber >= 0) { final int offset = myEditor.getCaretModel().getOffset(); return offset < mySegments.getSegmentStart(myCurrentSegmentNumber) || offset > mySegments.getSegmentEnd(myCurrentSegmentNumber); } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleSchemeCopyExporter.java b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleSchemeCopyExporter.java new file mode 100644 index 000000000000..4714a0e541eb --- /dev/null +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleSchemeCopyExporter.java @@ -0,0 +1,82 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.impl.source.codeStyle; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.options.SchemeExporter; +import com.intellij.openapi.options.SchemeExporterException; +import com.intellij.openapi.util.ThrowableComputable; +import com.intellij.openapi.util.WriteExternalException; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.codeStyle.CodeStyleScheme; +import org.jdom.Element; +import org.jdom.output.Format; +import org.jdom.output.XMLOutputter; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; + +/** + * Exports (copies) a code style scheme to an external file as is. + * + * @author Rustam Vishnyakov + */ +public class CodeStyleSchemeCopyExporter implements SchemeExporter { + @Override + public void exportScheme(@NotNull final CodeStyleScheme scheme, @NotNull final VirtualFile targetFile) throws SchemeExporterException { + assert scheme instanceof CodeStyleSchemeImpl; + try { + final Element root = schemeToDom((CodeStyleSchemeImpl)scheme); + ApplicationManager.getApplication().runWriteAction(new ThrowableComputable() { + @Override + public Boolean compute() throws IOException { + writeToFile(targetFile, root); + return Boolean.TRUE; + } + }); + } + catch (Exception e) { + throw new SchemeExporterException("Export failed: " + e.getMessage()); + } + } + + @Override + public String getExtension() { + return "xml"; + } + + private static Element schemeToDom(@NotNull CodeStyleSchemeImpl scheme) throws WriteExternalException { + Element newElement = new Element("code_scheme"); + newElement.setAttribute("name", scheme.getName()); + scheme.writeExternal(newElement); + return newElement; + } + + private void writeToFile(@NotNull VirtualFile file, @NotNull Element element) throws IOException { + OutputStream targetStream = file.getOutputStream(this); + try { + OutputStreamWriter writer = new OutputStreamWriter(targetStream); + Format format = Format.getPrettyFormat(); + format.setLineSeparator("\n"); + new XMLOutputter(format).output(element, writer); + } + finally { + targetStream.close(); + } + } +} diff --git a/platform/platform-api/src/com/intellij/openapi/options/SchemeExporter.java b/platform/platform-api/src/com/intellij/openapi/options/SchemeExporter.java new file mode 100644 index 000000000000..c1139d2aad70 --- /dev/null +++ b/platform/platform-api/src/com/intellij/openapi/options/SchemeExporter.java @@ -0,0 +1,41 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.options; + +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; + +/** + * Base interface to export a scheme. + * + * @author Rustam Vishnyakov + */ +public interface SchemeExporter { + /** + * Writes a scheme to existing targetFile. An implementor may use targetFile.getOutputStream() method to + * obtain a stream to write to. + * + * @param scheme The scheme to export. + * @param targetFile The target virtual file. + * @throws SchemeExporterException + */ + void exportScheme(@NotNull T scheme, @NotNull VirtualFile targetFile) throws SchemeExporterException; + + /** + * @return Target file extension without a dot, for example "xml". + */ + String getExtension(); +} diff --git a/platform/platform-api/src/com/intellij/openapi/options/SchemeExporterException.java b/platform/platform-api/src/com/intellij/openapi/options/SchemeExporterException.java new file mode 100644 index 000000000000..55a12ee0a03f --- /dev/null +++ b/platform/platform-api/src/com/intellij/openapi/options/SchemeExporterException.java @@ -0,0 +1,25 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.options; + +/** + * @author Rustam Vishnyakov + */ +public class SchemeExporterException extends Exception { + public SchemeExporterException(String message) { + super(message); + } +} diff --git a/platform/platform-api/src/com/intellij/ui/ExpandedItemListCellRendererWrapper.java b/platform/platform-api/src/com/intellij/ui/ExpandedItemListCellRendererWrapper.java index ce32d367fc68..1dfd31c825b8 100644 --- a/platform/platform-api/src/com/intellij/ui/ExpandedItemListCellRendererWrapper.java +++ b/platform/platform-api/src/com/intellij/ui/ExpandedItemListCellRendererWrapper.java @@ -15,9 +15,11 @@ */ package com.intellij.ui; +import com.intellij.ide.ui.AntialiasingType; import com.intellij.util.ObjectUtils; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; +import sun.swing.SwingUtilities2; import javax.swing.*; import java.awt.*; @@ -33,6 +35,7 @@ public class ExpandedItemListCellRendererWrapper implements ListCellRenderer { @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + list.putClientProperty(SwingUtilities2.AA_TEXT_PROPERTY_KEY, AntialiasingType.getAAHintForSwingComponent()); Component result = myWrappee.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); if (!myHandler.getExpandedItems().contains(index)) return result; Rectangle bounds = result.getBounds(); diff --git a/platform/platform-api/src/com/intellij/ui/SimpleColoredComponent.java b/platform/platform-api/src/com/intellij/ui/SimpleColoredComponent.java index 1a2587543d99..f1b87678a711 100644 --- a/platform/platform-api/src/com/intellij/ui/SimpleColoredComponent.java +++ b/platform/platform-api/src/com/intellij/ui/SimpleColoredComponent.java @@ -733,7 +733,6 @@ public class SimpleColoredComponent extends JComponent implements Accessible, Co final List searchMatches = new ArrayList(); - UISettings.setupAntialiasing(g); applyAdditionalHints(g); final Font ownFont = getFont(); if (ownFont != null) { @@ -915,6 +914,7 @@ public class SimpleColoredComponent extends JComponent implements Accessible, Co } protected void applyAdditionalHints(@NotNull Graphics2D g) { + UISettings.setupAntialiasing(g); } @Override diff --git a/platform/platform-api/src/com/intellij/ui/components/editors/JBComboBoxTableCellEditorComponent.java b/platform/platform-api/src/com/intellij/ui/components/editors/JBComboBoxTableCellEditorComponent.java index fe69d4e77ee2..82b49fa96c8a 100644 --- a/platform/platform-api/src/com/intellij/ui/components/editors/JBComboBoxTableCellEditorComponent.java +++ b/platform/platform-api/src/com/intellij/ui/components/editors/JBComboBoxTableCellEditorComponent.java @@ -15,6 +15,7 @@ */ package com.intellij.ui.components.editors; +import com.intellij.ide.ui.AntialiasingType; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.JBPopupAdapter; import com.intellij.openapi.ui.popup.JBPopupFactory; @@ -31,6 +32,7 @@ import com.intellij.util.Function; import com.intellij.util.PlatformIcons; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.EmptyIcon; +import sun.swing.SwingUtilities2; import javax.swing.*; import javax.swing.event.TableModelEvent; @@ -78,6 +80,7 @@ public class JBComboBoxTableCellEditorComponent extends JBLabel { } else { label.setIcon(getEmptyIcon()); } + label.putClientProperty(SwingUtilities2.AA_TEXT_PROPERTY_KEY, AntialiasingType.getAAHintForSwingComponent()); return label; } diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/TabLabel.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/TabLabel.java index 02fc3d28191d..14c188927d76 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/TabLabel.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/TabLabel.java @@ -149,11 +149,6 @@ public class TabLabel extends JPanel { g.setClip(oldClip); } } - - @Override - protected void applyAdditionalHints(@NotNull Graphics2D g) { - UISettings.setupAntialiasing(g); - } }; label.setOpaque(false); label.setBorder(null); diff --git a/platform/platform-impl/src/com/intellij/ide/ui/AppearanceConfigurable.java b/platform/platform-impl/src/com/intellij/ide/ui/AppearanceConfigurable.java index ec092b3c7e39..950efd89f2cb 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/AppearanceConfigurable.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/AppearanceConfigurable.java @@ -175,6 +175,11 @@ public class AppearanceConfigurable extends BaseConfigurable implements Searchab if (!myComponent.myAntialiasingInIDE.getSelectedItem().equals(settings.IDE_AA_TYPE)) { settings.IDE_AA_TYPE = (AntialiasingType)myComponent.myAntialiasingInIDE.getSelectedItem(); + for (Window w : Window.getWindows()) { + for (JComponent c : UIUtil.uiTraverser(w).filter(JComponent.class)) { + c.putClientProperty(SwingUtilities2.AA_TEXT_PROPERTY_KEY, AntialiasingType.getAAHintForSwingComponent()); + } + } shouldUpdateUI = true; } @@ -523,8 +528,10 @@ public class AppearanceConfigurable extends BaseConfigurable implements Searchab } private static class AAListCellRenderer extends ListCellRendererWrapper { - private static final SwingUtilities2.AATextInfo SUBPIXEL_HINT = new SwingUtilities2.AATextInfo(RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB, 140); - private static final SwingUtilities2.AATextInfo GREYSCALE_HINT = new SwingUtilities2.AATextInfo(RenderingHints.VALUE_TEXT_ANTIALIAS_ON, 140); + private static final SwingUtilities2.AATextInfo SUBPIXEL_HINT = new SwingUtilities2.AATextInfo( + RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB, UIUtil.getLcdContrastValue()); + private static final SwingUtilities2.AATextInfo GREYSCALE_HINT = new SwingUtilities2.AATextInfo( + RenderingHints.VALUE_TEXT_ANTIALIAS_ON, UIUtil.getLcdContrastValue()); @Override public void customize(JList list, AntialiasingType value, int index, boolean selected, boolean hasFocus) { diff --git a/platform/platform-impl/src/com/intellij/internal/DebugAttachDetector.java b/platform/platform-impl/src/com/intellij/internal/DebugAttachDetector.java index 681671a9f18a..52e366d81d73 100644 --- a/platform/platform-impl/src/com/intellij/internal/DebugAttachDetector.java +++ b/platform/platform-impl/src/com/intellij/internal/DebugAttachDetector.java @@ -91,7 +91,7 @@ public class DebugAttachDetector { } myAlarm.request(); } - }, 5000, Alarm.ThreadToUse.POOLED_THREAD, null); + }, 5000, Alarm.ThreadToUse.POOLED_THREAD, app); myAlarm.request(); } } 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 f5c57cd067dc..0f816cdc9d0f 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 @@ -328,7 +328,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi assertIsDispatchThread(); myProject = project; myDocument = (DocumentEx)document; - if (myDocument instanceof DocumentImpl) { + if (myDocument instanceof DocumentImpl && !myUseNewRendering) { ((DocumentImpl)myDocument).requestTabTracking(); } myScheme = createBoundColorSchemeDelegate(null); @@ -888,7 +888,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi if (myConnection != null) { myConnection.disconnect(); } - if (myDocument instanceof DocumentImpl) { + if (myDocument instanceof DocumentImpl && !myUseNewRendering) { ((DocumentImpl)myDocument).giveUpTabTracking(); } Disposer.dispose(myDisposable); @@ -1955,7 +1955,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi } public boolean hasTabs() { - return !(myDocument instanceof DocumentImpl) || ((DocumentImpl)myDocument).mightContainTabs(); + return myUseNewRendering || !(myDocument instanceof DocumentImpl) || ((DocumentImpl)myDocument).mightContainTabs(); } public boolean isScrollToCaret() { diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/ApproximationFragment.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/ApproximationFragment.java new file mode 100644 index 000000000000..143a59c54de0 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/ApproximationFragment.java @@ -0,0 +1,88 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.editor.impl.view; + +import org.jetbrains.annotations.NotNull; + +import java.awt.*; + +/** + * Used for quick estimation of editor's viewable area size (without full text layout) + */ +class ApproximationFragment implements LineFragment { + private final int myLength; + private final int myColumnCount; + private final float myWidth; + + ApproximationFragment(int length, int columnCount, float charWidth) { + myLength = length; + myColumnCount = columnCount; + myWidth = charWidth * columnCount; + } + + @Override + public int getLength() { + return myLength; + } + + @Override + public int getLogicalColumnCount(int startColumn) { + return myColumnCount; + } + + @Override + public int getVisualColumnCount(float startX) { + return myColumnCount; + } + + @Override + public int logicalToVisualColumn(float startX, int startColumn, int column) { + return column; + } + + @Override + public int visualToLogicalColumn(float startX, int startColumn, int column) { + return column; + } + + @Override + public float visualColumnToX(float startX, int column) { + return column < myColumnCount ? 0 : myWidth; + } + + @Override + public int[] xToVisualColumn(float startX, float x) { + float relX = x - startX; + int column = relX < myWidth / 2 ? 0 : myColumnCount; + return new int[] {column, relX <= visualColumnToX(startX, column) ? 0 : 1}; + } + + @Override + public float offsetToX(float startX, int startOffset, int offset) { + return offset < myLength ? 0 : myWidth; + } + + @Override + public void draw(Graphics2D g, float x, float y, int startColumn, int endColumn) { + throw new UnsupportedOperationException(); + } + + @NotNull + @Override + public LineFragment subFragment(int startOffset, int endOffset) { + throw new UnsupportedOperationException(); + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorCoordinateMapper.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorCoordinateMapper.java index 1411c2dd1f62..89256fe2edb8 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorCoordinateMapper.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorCoordinateMapper.java @@ -15,7 +15,6 @@ */ package com.intellij.openapi.editor.impl.view; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.ex.util.EditorUtil; import com.intellij.openapi.editor.impl.EditorImpl; @@ -55,58 +54,11 @@ class EditorCoordinateMapper { @NotNull LogicalPosition offsetToLogicalPosition(int offset) { - int textLength = myDocument.getTextLength(); - if (offset <= 0 || textLength == 0) { - return new LogicalPosition(0, 0); - } - offset = Math.min(offset, textLength); - int line = myDocument.getLineNumber(offset); - offset = Math.min(offset, myDocument.getLineEndOffset(line)); - int column = 0; - if (ApplicationManager.getApplication().isDispatchThread()) { - LineLayout lineLayout = myView.getTextLayoutCache().getLineLayout(line); - column = lineLayout.offsetToLogicalColumn(offset - myDocument.getLineStartOffset(line)); - } - else { - CharSequence text = myDocument.getImmutableCharSequence(); - int tabSize = myView.getTabSize(); - for (int i = myDocument.getLineStartOffset(line); i < offset; i++) { - if (text.charAt(i) == '\t') { - column = (column / tabSize + 1) * tabSize; - } - else { - column++; - } - } - } - return new LogicalPosition(line, column); + return myView.getLogicalPositionCache().offsetToLogicalPosition(offset); } int logicalPositionToOffset(@NotNull LogicalPosition pos) { - int line = pos.line; - if (line >= myDocument.getLineCount()) return myDocument.getTextLength(); - - int lineStartOffset = myDocument.getLineStartOffset(line); - if (ApplicationManager.getApplication().isDispatchThread()) { - LineLayout lineLayout = myView.getTextLayoutCache().getLineLayout(line); - return lineStartOffset + lineLayout.logicalColumnToOffset(pos.column); - } - else { - int lineEndOffset = myDocument.getLineEndOffset(line); - CharSequence text = myDocument.getImmutableCharSequence(); - int tabSize = myView.getTabSize(); - int column = 0; - for (int i = lineStartOffset; i < lineEndOffset; i++) { - if (text.charAt(i) == '\t') { - column = (column / tabSize + 1) * tabSize; - } - else { - column++; - } - if (pos.column < column) return i; - } - return lineEndOffset; - } + return myView.getLogicalPositionCache().logicalPositionToOffset(pos); } @NotNull @@ -121,7 +73,7 @@ class EditorCoordinateMapper { int visualLine = offsetToVisualLine(offset, beforeSoftWrap); int maxVisualColumn = 0; int maxLogicalColumn = 0; - for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, offset, beforeSoftWrap)) { + for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, offset, beforeSoftWrap, false)) { if (!pos.leansForward && offset == fragment.getVisualLineStartOffset()) { return new VisualPosition(visualLine, fragment.getStartVisualColumn()); } @@ -173,7 +125,7 @@ class EditorCoordinateMapper { int maxVisualColumn = 0; int maxLogicalColumn = 0; int maxOffset = offset; - for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, offset, false)) { + for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, offset, false, false)) { int minColumn = fragment.getStartVisualColumn(); int maxColumn = fragment.getEndVisualColumn(); if (column < minColumn || column == minColumn && !pos.leansRight) { @@ -234,7 +186,7 @@ class EditorCoordinateMapper { return myDocument.getLineNumber(offset) - myFoldingModel.getFoldedLinesCountBefore(offset) + softWrapsBeforeOrAtOffset; } - private int visualLineToOffset(int visualLine) { + int visualLineToOffset(int visualLine) { int start = 0; int end = myDocument.getTextLength(); int current = 0; @@ -288,7 +240,7 @@ class EditorCoordinateMapper { if (visualLine < myView.getEditor().getVisibleLineCount()) { int visualLineStartOffset = visualLineToOffset(visualLine); int maxOffset = 0; - for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, visualLineStartOffset, false)) { + for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, visualLineStartOffset, false, false)) { if (p.x <= fragment.getStartX()) { int markerWidth = myView.getEditor().getSoftWrapModel().getMinDrawingWidthInPixels(SoftWrapDrawingType.AFTER_SOFT_WRAP); float indent = fragment.getStartX() - markerWidth; @@ -334,7 +286,7 @@ class EditorCoordinateMapper { if (visualLine < myView.getEditor().getVisibleLineCount()) { int visualLineStartOffset = visualLineToOffset(visualLine); int maxOffset = 0; - for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, visualLineStartOffset, false)) { + for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, visualLineStartOffset, false, false)) { if (column < fragment.getStartVisualColumn()) { break; } @@ -365,7 +317,7 @@ class EditorCoordinateMapper { float x = getStartX(logicalLine); if (myDocument.getTextLength() > 0) { boolean firstFragment = true; - for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, offset, beforeSoftWrap)) { + for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, offset, beforeSoftWrap, false)) { if (firstFragment && offset == visualLineStartOffset && !leanTowardsLargerOffsets) { x = fragment.getStartX(); break; diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorPainter.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorPainter.java index 4251f7dfb0a6..8752e9c18c6e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorPainter.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorPainter.java @@ -465,7 +465,7 @@ class EditorPainter implements TextDrawingCallback { Collection extensions = painter.getLineExtensions(project, virtualFile, line); if (extensions != null) { for (LineExtensionInfo info : extensions) { - LineLayout layout = new LineLayout(myView, info.getText(), info.getFontType(), g.getFontRenderContext()); + LineLayout layout = new LineLayout(myView, info.getText(), info.getFontType()); g.setColor(info.getColor()); x = paintLineLayoutWithEffect(g, layout, x, y, info.getEffectColor(), info.getEffectType()); int currentLineWidth = (int)x; @@ -584,7 +584,7 @@ class EditorPainter implements TextDrawingCallback { } } else { - int maxWidth = myView.getMaxWidthInLineRange(startPosition.line, endPosition.line) - 1; + int maxWidth = myView.getMaxWidthInLineRange(startPosition.line, endPosition.line, false) - 1; TFloatArrayList leadingRanges = adjustedLogicalRangeToVisualRanges(startOffset, myView.visualPositionToOffset(new VisualPosition( startPosition.line, Integer.MAX_VALUE, true))); @@ -691,7 +691,7 @@ class EditorPainter implements TextDrawingCallback { private TFloatArrayList logicalRangeToVisualRanges(int startOffset, int endOffset) { assert startOffset <= endOffset; TFloatArrayList result = new TFloatArrayList(); - for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, startOffset, false)) { + for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, startOffset, false, false)) { int minOffset = fragment.getMinOffset(); int maxOffset = fragment.getMaxOffset(); if (startOffset == endOffset) { @@ -770,8 +770,9 @@ class EditorPainter implements TextDrawingCallback { g.fillRect(startX, y, width, lineHeight - 1); if (myDocument.getTextLength() > 0 && caret != null) { int targetVisualColumn = caret.getVisualPosition().column; - for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, - caret.getVisualLineStart(), false)) { + for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, + caret.getVisualLineStart(), + false, false)) { int startVisualColumn = fragment.getStartVisualColumn(); int endVisualColumn = fragment.getEndVisualColumn(); if (startVisualColumn < targetVisualColumn && endVisualColumn > targetVisualColumn || @@ -809,7 +810,7 @@ class EditorPainter implements TextDrawingCallback { int prevEndOffset = -1; boolean firstFragment = true; int maxColumn = 0; - for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, offset, false)) { + for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, offset, false, false)) { int fragmentStartOffset = fragment.getStartOffset(); int start = fragmentStartOffset; int end = fragment.getEndOffset(); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorSizeManager.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorSizeManager.java index 9b4b4b1dd717..aa46a4aaeb85 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorSizeManager.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorSizeManager.java @@ -29,36 +29,35 @@ import com.intellij.openapi.editor.impl.EditorImpl; import com.intellij.openapi.editor.impl.softwrap.mapping.IncrementalCacheUpdateEvent; import com.intellij.openapi.editor.impl.softwrap.mapping.SoftWrapAwareDocumentParsingListenerAdapter; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VirtualFile; import gnu.trove.TIntArrayList; -import gnu.trove.TIntHashSet; -import gnu.trove.TIntProcedure; import org.jetbrains.annotations.NotNull; import java.awt.*; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; /** * Calculates width (in pixels) of editor contents. */ class EditorSizeManager implements PrioritizedDocumentListener, Disposable, FoldingListener { - private static final int UNKNOWN_WIDTH = Integer.MAX_VALUE; + private static final int UNKNOWN_WIDTH = -1; private final EditorView myView; private final EditorImpl myEditor; private final DocumentEx myDocument; private final TIntArrayList myLineWidths = new TIntArrayList(); // cached widths of visual lines (in pixels) - // negative value means an estimated (not precise) width - // UNKNOWN_WIDTH(Integer.MAX_VALUE) means no value + // UNKNOWN_WIDTH(-1) means no value private int myWidthInPixels; private int myMaxLineWithExtensionWidth; private int myWidestLineWithExtension; - private boolean myApproximateWidthCalculated; - private final TIntHashSet myLinesWithCreatedLayout = new TIntHashSet(); - + private final List myDeferredRanges = new ArrayList(); + private final SoftWrapAwareDocumentParsingListenerAdapter mySoftWrapChangeListener = new SoftWrapAwareDocumentParsingListenerAdapter() { @Override public void onRecalculationEnd(@NotNull IncrementalCacheUpdateEvent event) { @@ -112,15 +111,11 @@ class EditorSizeManager implements PrioritizedDocumentListener, Disposable, Fold } foldingChangeStartOffset = Integer.MAX_VALUE; foldingChangeEndOffset = Integer.MIN_VALUE; - - myLinesWithCreatedLayout.forEach(new TIntProcedure() { - @Override - public boolean execute(int logicalLine) { - onLineLayoutCreated(logicalLine); - return true; - } - }); - myLinesWithCreatedLayout.clear(); + + for (TextRange range : myDeferredRanges) { + onTextLayoutPerformed(range.getStartOffset(), range.getEndOffset()); + } + myDeferredRanges.clear(); } private void onSoftWrapRecalculationEnd(IncrementalCacheUpdateEvent event) { @@ -128,7 +123,8 @@ class EditorSizeManager implements PrioritizedDocumentListener, Disposable, Fold } Dimension getPreferredSize() { - int width = getPreferredWidth(); + int widthWithoutCaret = getPreferredWidth(); + int width = widthWithoutCaret; if (!myDocument.isInBulkUpdate()) { for (Caret caret : myEditor.getCaretModel().getAllCarets()) { if (caret.isUpToDate()) { @@ -137,10 +133,18 @@ class EditorSizeManager implements PrioritizedDocumentListener, Disposable, Fold } } } - width += myEditor.getSettings().getAdditionalColumnsCount() * myView.getPlainSpaceWidth(); + if (shouldRespectAdditionalColumns(widthWithoutCaret)) { + width += myEditor.getSettings().getAdditionalColumnsCount() * myView.getPlainSpaceWidth(); + } return new Dimension(width, myEditor.getPreferredHeight()); } + private boolean shouldRespectAdditionalColumns(int widthWithoutCaret) { + return !myEditor.getSoftWrapModel().isSoftWrappingEnabled() + || myEditor.getSoftWrapModel().isRespectAdditionalColumns() + || widthWithoutCaret > myEditor.getScrollingModel().getVisibleArea().getWidth(); + } + private int getPreferredWidth() { if (myWidthInPixels < 0) { myWidthInPixels = calculatePreferredWidth(); @@ -171,15 +175,7 @@ class EditorSizeManager implements PrioritizedDocumentListener, Disposable, Fold for (int i = 0; i < lineCount; i++) { int width = myLineWidths.get(i); if (width == UNKNOWN_WIDTH) { - myApproximateWidthCalculated = false; - myView.getTextLayoutCache().enableQuickLayouts(true); - try { - width = myView.getMaxWidthInLineRange(i, i); - } - finally { - myView.getTextLayoutCache().enableQuickLayouts(false); - } - if (myApproximateWidthCalculated) width = - width; + width = myView.getMaxWidthInLineRange(i, i, true); myLineWidths.set(i, width); } maxWidth = Math.max(maxWidth, Math.abs(width)); @@ -217,38 +213,28 @@ class EditorSizeManager implements PrioritizedDocumentListener, Disposable, Fold myMaxLineWithExtensionWidth = width; } - void quickLineLayoutCreated() { - myApproximateWidthCalculated = true; - } - - void lineLayoutCreated(int logicalLine) { + void textLayoutPerformed(int startOffset, int endOffset) { if (myEditor.getFoldingModel().isInBatchFoldingOperation()) { - myLinesWithCreatedLayout.add(logicalLine); + myDeferredRanges.add(new TextRange(startOffset, endOffset)); } else { - onLineLayoutCreated(logicalLine); + onTextLayoutPerformed(startOffset, endOffset); } } - private void onLineLayoutCreated(int logicalLine) { + private void onTextLayoutPerformed(int startOffset, int endOffset) { boolean purePaintingMode = myEditor.isPurePaintingMode(); boolean foldingEnabled = myEditor.getFoldingModel().isFoldingEnabled(); myEditor.setPurePaintingMode(false); myEditor.getFoldingModel().setFoldingEnabled(true); try { - int startVisualLine = myView.offsetToVisualLine(myDocument.getLineStartOffset(logicalLine), false); - int endVisualLine = myView.offsetToVisualLine(myDocument.getLineEndOffset(logicalLine), false); - boolean sizeInvalidated = false; + int startVisualLine = myView.offsetToVisualLine(startOffset, false); + int endVisualLine = myView.offsetToVisualLine(endOffset, true); for (int i = startVisualLine; i <= endVisualLine; i++) { - if (myLineWidths.get(i) < 0) { - myLineWidths.set(i, UNKNOWN_WIDTH); - sizeInvalidated = true; - } - } - if (sizeInvalidated) { - myWidthInPixels = -1; - myEditor.getContentComponent().revalidate(); + myLineWidths.set(i, UNKNOWN_WIDTH); } + myWidthInPixels = -1; + myEditor.getContentComponent().revalidate(); } finally { myEditor.setPurePaintingMode(purePaintingMode); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorView.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorView.java index 7a350d5514bb..44399311da19 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorView.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorView.java @@ -55,6 +55,7 @@ public class EditorView implements TextDrawingCallback, Disposable { private final EditorCoordinateMapper myMapper; private final EditorSizeManager mySizeManager; private final TextLayoutCache myTextLayoutCache; + private final LogicalPositionCache myLogicalPositionCache; private final TabFragment myTabFragment; private String myPrefixText; // accessed only in EDT @@ -79,8 +80,10 @@ public class EditorView implements TextDrawingCallback, Disposable { myMapper = new EditorCoordinateMapper(this); mySizeManager = new EditorSizeManager(this); myTextLayoutCache = new TextLayoutCache(this); + myLogicalPositionCache = new LogicalPositionCache(this); myTabFragment = new TabFragment(this); + Disposer.register(this, myLogicalPositionCache); Disposer.register(this, myTextLayoutCache); Disposer.register(this, mySizeManager); } @@ -108,6 +111,10 @@ public class EditorView implements TextDrawingCallback, Disposable { TabFragment getTabFragment() { return myTabFragment; } + + LogicalPositionCache getLogicalPositionCache() { + return myLogicalPositionCache; + } @Override public void dispose() { @@ -191,7 +198,7 @@ public class EditorView implements TextDrawingCallback, Disposable { myPrefixText = prefixText; synchronized (myLock) { myPrefixLayout = prefixText == null || prefixText.isEmpty() ? null : - new LineLayout(this, prefixText, attributes.getFontType(), myFontRenderContext); + new LineLayout(this, prefixText, attributes.getFontType()); } myPrefixAttributes = attributes; mySizeManager.invalidateRange(0, 0); @@ -231,18 +238,17 @@ public class EditorView implements TextDrawingCallback, Disposable { public int getMaxWidthInRange(int startOffset, int endOffset) { assertIsDispatchThread(); - return getMaxWidthInLineRange(offsetToVisualLine(startOffset, false), offsetToVisualLine(endOffset, true)); + return getMaxWidthInLineRange(offsetToVisualLine(startOffset, false), offsetToVisualLine(endOffset, true), false); } - int getMaxWidthInLineRange(int startVisualLine, int endVisualLine) { + int getMaxWidthInLineRange(int startVisualLine, int endVisualLine, boolean quick) { int maxWidth = 0; + endVisualLine = Math.min(endVisualLine, myEditor.getVisibleLineCount() - 1); for (int i = startVisualLine; i <= endVisualLine; i++) { - LogicalPosition startPosition = visualToLogicalPosition(new VisualPosition(i, 0)); - if (startPosition.line >= myDocument.getLineCount()) break; - int startOffset = logicalPositionToOffset(startPosition); + int startOffset = myMapper.visualLineToOffset(i); float x = 0; int maxOffset = 0; - for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(this, startOffset, false)) { + for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(this, startOffset, false, quick)) { x = fragment.getEndX(); maxOffset = Math.max(maxOffset, fragment.getMaxOffset()); } @@ -264,9 +270,11 @@ public class EditorView implements TextDrawingCallback, Disposable { myMaxCharWidth = -1; myTabSize = -1; } - reset(); - setPrefix(myPrefixText, myPrefixAttributes); // recreate prefix layout + myLogicalPositionCache.reset(false); + myTextLayoutCache.resetToDocumentSize(false); invalidateFoldRegionLayouts(); + setPrefix(myPrefixText, myPrefixAttributes); // recreate prefix layout + mySizeManager.reset(); } public void invalidateRange(int startOffset, int endOffset) { @@ -277,14 +285,18 @@ public class EditorView implements TextDrawingCallback, Disposable { } int startLine = myDocument.getLineNumber(Math.max(0, startOffset)); int endLine = myDocument.getLineNumber(Math.min(textLength, endOffset)); - myTextLayoutCache.invalidateLines(startLine, endLine, endLine); + myTextLayoutCache.invalidateLines(startLine, endLine); mySizeManager.invalidateRange(startOffset, endOffset); } + /** + * Invoked when document might have changed, but no notifications were sent (for a hacky document in EditorTextFieldCellRenderer) + */ public void reset() { assertIsDispatchThread(); + myLogicalPositionCache.reset(true); + myTextLayoutCache.resetToDocumentSize(true); mySizeManager.reset(); - myTextLayoutCache.resetToDocumentSize(); } public boolean isRtlLocation(@NotNull VisualPosition visualPosition) { @@ -422,8 +434,7 @@ public class EditorView implements TextDrawingCallback, Disposable { if (layout == null) { TextAttributes placeholderAttributes = myEditor.getFoldingModel().getPlaceholderAttributes(); layout = new LineLayout(this, foldRegion.getPlaceholderText(), - placeholderAttributes == null ? Font.PLAIN : placeholderAttributes.getFontType(), - myFontRenderContext); + placeholderAttributes == null ? Font.PLAIN : placeholderAttributes.getFontType()); foldRegion.putUserData(FOLD_REGION_TEXT_LAYOUT, layout); } return layout; diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/LineFragment.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/LineFragment.java index 28dc9e9fccee..ceb3153c12eb 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/LineFragment.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/LineFragment.java @@ -32,10 +32,6 @@ interface LineFragment { int getLogicalColumnCount(int startColumn); int getVisualColumnCount(float startX); - - int offsetToLogicalColumn(int startColumn, int offset); - - int logicalColumnToOffset(int startColumn, int column); // columns are visual int logicalToVisualColumn(float startX, int startColumn, int column); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/LineLayout.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/LineLayout.java index e45865219098..0d691ab2e388 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/LineLayout.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/LineLayout.java @@ -35,100 +35,84 @@ import java.util.List; /** * Layout of a single line of document text. Consists of a series of BidiRuns, which, in turn, consist of TextFragments. + * TextFragments within BidiRun are grouped into Chunks for performance reasons, glyph layout is performed per-Chunk, and only + * for required Chunks. */ class LineLayout { private final BidiRun[] myBidiRunsInLogicalOrder; private final BidiRun[] myBidiRunsInVisualOrder; private final float myWidth; - - /** - * Creates a dummy layout used for editor size estimations - */ - LineLayout(EditorView view, int lineLength) { - this(createDummyFragment(view, lineLength), false); - } /** * Creates a layout for a fragment of text from editor. */ - LineLayout(@NotNull EditorView view, - int startOffset, int endOffset, - @NotNull FontRenderContext fontRenderContext) { - this(createFragments(view, startOffset, endOffset, fontRenderContext), false); + LineLayout(@NotNull EditorView view, int startOffset, int endOffset, boolean skipBidiLayout) { + this(createFragments(view, startOffset, endOffset, skipBidiLayout), false); } /** * Creates a layout for an arbitrary piece of text (using a common font style). */ - LineLayout(@NotNull EditorView view, - @NotNull CharSequence text, @JdkConstants.FontStyle int fontStyle, - @NotNull FontRenderContext fontRenderContext) { - this(createFragments(view, text, fontStyle, fontRenderContext), true); + LineLayout(@NotNull EditorView view, @NotNull CharSequence text, @JdkConstants.FontStyle int fontStyle) { + this(createFragments(view, text, fontStyle), true); } private LineLayout(@NotNull List runs, boolean calculateWidth) { myBidiRunsInLogicalOrder = runs.toArray(new BidiRun[runs.size()]); myBidiRunsInVisualOrder = myBidiRunsInLogicalOrder.clone(); - - reorderRunsVisually(myBidiRunsInVisualOrder); - int logicalColumn = 0; - for (BidiRun run : runs) { - logicalColumn = run.createIndices(logicalColumn); - } + reorderRunsVisually(myBidiRunsInVisualOrder); + myWidth = calculateWidth ? calculateWidth() : -1; } - - private static void reorderRunsVisually(BidiRun[] bidiRunsInLogicalOrder) { - if (bidiRunsInLogicalOrder.length > 1) { - byte[] levels = new byte[bidiRunsInLogicalOrder.length]; - for (int i = 0; i < bidiRunsInLogicalOrder.length; i++) { - levels[i] = bidiRunsInLogicalOrder[i].level; + + // runs are supposed to be in logical order initially + private static void reorderRunsVisually(BidiRun[] bidiRuns) { + if (bidiRuns.length > 1) { + byte[] levels = new byte[bidiRuns.length]; + for (int i = 0; i < bidiRuns.length; i++) { + levels[i] = bidiRuns[i].level; } - Bidi.reorderVisually(levels, 0, bidiRunsInLogicalOrder, 0, levels.length); + Bidi.reorderVisually(levels, 0, bidiRuns, 0, levels.length); } } + + static boolean isBidiLayoutRequired(@NotNull CharSequence text) { + char[] chars = CharArrayUtil.fromSequence(text); + return Bidi.requiresBidi(chars, 0, chars.length); + } - private static List createFragments(@NotNull EditorView view, int lineStartOffset, int lineEndOffset, - @NotNull FontRenderContext fontRenderContext) { + private static List createFragments(@NotNull EditorView view, int lineStartOffset, int lineEndOffset, boolean skipBidiLayout) { if (lineEndOffset <= lineStartOffset) return Collections.emptyList(); EditorImpl editor = view.getEditor(); - FontPreferences fontPreferences = editor.getColorsScheme().getFontPreferences(); - char[] chars = CharArrayUtil.fromSequence(editor.getDocument().getImmutableCharSequence(), lineStartOffset, lineEndOffset); - List runs = createRuns(editor, chars, lineStartOffset); - for (BidiRun run : runs) { - IterationState it = new IterationState(editor, lineStartOffset + run.startOffset, lineStartOffset + run.endOffset, - false, false, false, false); - while (!it.atEnd()) { - addFragments(run, chars, it.getStartOffset() - lineStartOffset, it.getEndOffset() - lineStartOffset, - it.getMergedAttributes().getFontType(), fontPreferences, fontRenderContext, view.getTabFragment()); - it.advance(); - } - assert !run.fragments.isEmpty(); + List runs; + if (skipBidiLayout) { + runs = Collections.singletonList(new BidiRun((byte)0, 0, lineEndOffset - lineStartOffset)); + } + else { + CharSequence text = editor.getDocument().getImmutableCharSequence().subSequence(lineStartOffset, lineEndOffset); + char[] chars = CharArrayUtil.fromSequence(text); + runs = createRuns(editor, chars, lineStartOffset); } return runs; } private static List createFragments(@NotNull EditorView view, @NotNull CharSequence text, - @JdkConstants.FontStyle int fontStyle, @NotNull FontRenderContext fontRenderContext) { + @JdkConstants.FontStyle int fontStyle) { if (text.length() == 0) return Collections.emptyList(); EditorImpl editor = view.getEditor(); + FontRenderContext fontRenderContext = view.getFontRenderContext(); FontPreferences fontPreferences = editor.getColorsScheme().getFontPreferences(); char[] chars = CharArrayUtil.fromSequence(text); List runs = createRuns(editor, chars, -1); for (BidiRun run : runs) { - addFragments(run, chars, run.startOffset, run.endOffset, fontStyle, fontPreferences, fontRenderContext, null); - assert !run.fragments.isEmpty(); + for (Chunk chunk : run.getChunks()) { + addFragments(chunk, run.isRtl(), chars, chunk.startOffset, chunk.endOffset, fontStyle, fontPreferences, fontRenderContext, null); + } } return runs; } - private static List createDummyFragment(@NotNull EditorView view, int lineLength) { - BidiRun run = new BidiRun((byte)0, 0, lineLength); - run.fragments.add(new DummyLineFragment(lineLength, view.getMaxCharWidth())); - return Collections.singletonList(run); - } - private static List createRuns(EditorImpl editor, char[] text, int startOffsetInEditor) { int textLength = text.length; if (editor.myDisableRtl) return Collections.singletonList(new BidiRun((byte)0, 0, textLength)); @@ -186,37 +170,39 @@ class LineLayout { } } - private static void addFragments(BidiRun run, char[] text, int start, int end, int fontStyle, + private static void addFragments(Chunk chunk, boolean isRtl, char[] text, int start, int end, int fontStyle, FontPreferences fontPreferences, FontRenderContext fontRenderContext, @Nullable TabFragment tabFragment) { + assert start < end; Font currentFont = null; int currentIndex = start; for(int i = start; i < end; i++) { char c = text[i]; if (c == '\t' && tabFragment != null) { - assert run.level == 0; - addTextFragmentIfNeeded(run, text, currentIndex, i, currentFont, fontRenderContext, run.isRtl()); - run.fragments.add(tabFragment); + assert !isRtl; + addTextFragmentIfNeeded(chunk, text, currentIndex, i, currentFont, fontRenderContext, false); + chunk.fragments.add(tabFragment); currentFont = null; currentIndex = i + 1; } else { Font font = ComplementaryFontsRegistry.getFontAbleToDisplay(c, fontStyle, fontPreferences).getFont(); if (!font.equals(currentFont)) { - addTextFragmentIfNeeded(run, text, currentIndex, i, currentFont, fontRenderContext, run.isRtl()); + addTextFragmentIfNeeded(chunk, text, currentIndex, i, currentFont, fontRenderContext, isRtl); currentFont = font; currentIndex = i; } } } - addTextFragmentIfNeeded(run, text, currentIndex, end, currentFont, fontRenderContext, run.isRtl()); + addTextFragmentIfNeeded(chunk, text, currentIndex, end, currentFont, fontRenderContext, isRtl); + assert !chunk.fragments.isEmpty(); } - private static void addTextFragmentIfNeeded(BidiRun run, char[] chars, int from, int to, Font font, + private static void addTextFragmentIfNeeded(Chunk chunk, char[] chars, int from, int to, Font font, FontRenderContext fontRenderContext, boolean isRtl) { if (to > from) { assert font != null; - run.fragments.add(new TextFragment(chars, from, to, isRtl, font, fontRenderContext)); + chunk.fragments.add(new TextFragment(chars, from, to, isRtl, font, fontRenderContext)); } } @@ -233,38 +219,24 @@ class LineLayout { return myWidth; } - int offsetToLogicalColumn(int offset) { - if (offset <= 0) return 0; - for (BidiRun run : myBidiRunsInLogicalOrder) { - if (offset <= run.endOffset) return run.offsetToLogicalColumn(offset); - } - return myBidiRunsInLogicalOrder.length == 0 ? 0 : myBidiRunsInLogicalOrder[myBidiRunsInLogicalOrder.length - 1].endLogicalColumn; - } - - int logicalColumnToOffset(int logicalColumn) { - if (logicalColumn <= 0) return 0; - for (BidiRun run : myBidiRunsInLogicalOrder) { - if (logicalColumn <= run.endLogicalColumn) return run.logicalColumnToOffset(logicalColumn); - } - return myBidiRunsInLogicalOrder.length == 0 ? 0 : myBidiRunsInLogicalOrder[myBidiRunsInLogicalOrder.length - 1].endOffset; - } - Iterable getFragmentsInVisualOrder(final float startX) { return new Iterable() { @Override public Iterator iterator() { - return new VisualOrderIterator(startX, 0, 0, 0, myBidiRunsInVisualOrder); + return new VisualOrderIterator(null, 0, startX, 0, 0, 0, myBidiRunsInVisualOrder); } }; } - Iterable getFragmentsInVisualOrder(final float startX, - final int startVisualColumn, - final int startOffset, - int endOffset) { + Iterable getFragmentsInVisualOrder(@NotNull final EditorView view, + final int lineStartOffset, + final float startX, + final int startVisualColumn, + final int startOffset, + int endOffset, + boolean quick) { assert startOffset <= endOffset; final BidiRun[] runs; - int startLogicalColumn = 0; if (startOffset == endOffset) { runs = new BidiRun[0]; } @@ -273,19 +245,16 @@ class LineLayout { for (BidiRun run : myBidiRunsInLogicalOrder) { if (run.endOffset <= startOffset) continue; if (run.startOffset >= endOffset) break; - runList.add(run.subRun(startOffset, endOffset)); - if (run.startOffset <= startOffset) { - startLogicalColumn = run.offsetToLogicalColumn(startOffset); - } + runList.add(run.subRun(view, lineStartOffset, startOffset, endOffset, quick)); } runs = runList.toArray(new BidiRun[runList.size()]); reorderRunsVisually(runs); } - final int finalStartLogicalColumn = startLogicalColumn; + final int startLogicalColumn = view.offsetToLogicalPosition(lineStartOffset + startOffset).column; return new Iterable() { @Override public Iterator iterator() { - return new VisualOrderIterator(startX, startVisualColumn, finalStartLogicalColumn, startOffset, runs); + return new VisualOrderIterator(view, lineStartOffset, startX, startVisualColumn, startLogicalColumn, startOffset, runs); } }; } @@ -332,17 +301,12 @@ class LineLayout { } private static class BidiRun { + private static final int CHUNK_CHARACTERS = 1024; + private final byte level; private final int startOffset; private final int endOffset; - private final List fragments = new ArrayList(); // in logical order - - // values cached for performance - private static final int INDEX_FREQUENCY = 100; - private int startLogicalColumn; - private int endLogicalColumn; - private int[] offsetIndex; // stores start offsets for each INDEX_FREQUENCY-th fragment, for faster lookup by offset - private int[] logicalColumnIndex; // stores start logical column for each INDEX_FREQUENCY-th fragment, for faster offset <-> logical column conversions + private Chunk[] chunks; // in logical order private BidiRun(byte level, int startOffset, int endOffset) { this.level = level; @@ -353,124 +317,108 @@ class LineLayout { private boolean isRtl() { return (level & 1) != 0; } - - private BidiRun subRun(int targetStartOffset, int targetEndOffset) { - assert targetStartOffset < endOffset; - assert targetEndOffset > startOffset; - if (targetStartOffset <= startOffset && targetEndOffset >= this.endOffset) { - return this; - } - int start = Math.max(startOffset, targetStartOffset); - int end = Math.min(endOffset, targetEndOffset); - BidiRun run = new BidiRun(level, start, end); - int offset = startOffset; - int startFragment = 0; - if (offsetIndex != null) { - int pos = Arrays.binarySearch(offsetIndex, start); - if (pos < 0) pos = - pos - 2; - if (pos >= 0) { - startFragment = (pos + 1) * INDEX_FREQUENCY; - offset = offsetIndex[pos]; + + private Chunk[] getChunks() { + if (chunks == null) { + int chunkCount = (endOffset - startOffset + CHUNK_CHARACTERS - 1) / CHUNK_CHARACTERS; + chunks = new Chunk[chunkCount]; + for (int i = 0; i < chunkCount; i++) { + int from = startOffset + i * CHUNK_CHARACTERS; + int to = (i == chunkCount - 1) ? endOffset : from + CHUNK_CHARACTERS; + Chunk chunk = new Chunk(from, to); + chunks[i] = chunk; } } - for (int i = startFragment; i < fragments.size(); i++) { - LineFragment fragment = fragments.get(i); + return chunks; + } + + private BidiRun subRun(@NotNull EditorView view, int lineStartOffset, int targetStartOffset, int targetEndOffset, boolean quick) { + assert targetStartOffset < endOffset; + assert targetEndOffset > startOffset; + int start = Math.max(startOffset, targetStartOffset); + int end = Math.min(endOffset, targetEndOffset); + BidiRun subRun = new BidiRun(level, start, end); + List subChunks = new ArrayList(); + for (Chunk chunk : getChunks()) { + if (chunk.endOffset <= start) continue; + if (chunk.startOffset >= end) break; + subChunks.add(chunk.subChunk(view, lineStartOffset, isRtl(), start, end, quick)); + } + subRun.chunks = subChunks.toArray(new Chunk[subChunks.size()]); + return subRun; + } + } + + private static class Chunk { + private final List fragments = new ArrayList(); // in logical order + private int startOffset; + private int endOffset; + + private Chunk(int startOffset, int endOffset) { + this.startOffset = startOffset; + this.endOffset = endOffset; + } + + private void ensureLayout(@NotNull EditorView view, int lineStartOffset, boolean isRtl) { + if (!fragments.isEmpty()) return; + int start = lineStartOffset + startOffset; + int end = lineStartOffset + endOffset; + IterationState it = new IterationState(view.getEditor(), start, end, false, false, false, false); + FontPreferences fontPreferences = view.getEditor().getColorsScheme().getFontPreferences(); + char[] chars = CharArrayUtil.fromSequence(view.getEditor().getDocument().getImmutableCharSequence(), start, end); + while (!it.atEnd()) { + addFragments(this, isRtl, chars, it.getStartOffset() - start, it.getEndOffset() - start, + it.getMergedAttributes().getFontType(), fontPreferences, view.getFontRenderContext(), view.getTabFragment()); + it.advance(); + } + view.getSizeManager().textLayoutPerformed(start, end); + assert !fragments.isEmpty(); + } + + private Chunk subChunk(EditorView view, int lineStartOffset, boolean isRtl, int targetStartOffset, int targetEndOffset, boolean quick) { + assert targetStartOffset < endOffset; + assert targetEndOffset > startOffset; + int start = Math.max(startOffset, targetStartOffset); + int end = Math.min(endOffset, targetEndOffset); + if (quick && fragments.isEmpty()) { + Chunk chunk = new Chunk(start, end); + int startColumn = view.offsetToLogicalPosition(lineStartOffset + start).column; + int endColumn = view.offsetToLogicalPosition(lineStartOffset + end).column; + chunk.fragments.add(new ApproximationFragment(end - start, endColumn - startColumn, view.getMaxCharWidth())); + return chunk; + } + if (start == startOffset && end == this.endOffset) { + return this; + } + ensureLayout(view, lineStartOffset, isRtl); + Chunk chunk = new Chunk(start, end); + int offset = startOffset; + for (LineFragment fragment : fragments) { if (end <= offset) break; int endOffset = offset + fragment.getLength(); if (start < endOffset) { - run.fragments.add(fragment.subFragment(Math.max(start, offset) - offset, Math.min(end, endOffset) - offset)); + chunk.fragments.add(fragment.subFragment(Math.max(start, offset) - offset, Math.min(end, endOffset) - offset)); } offset = endOffset; } - return run; - } - - private int logicalColumnToOffset(int logicalColumn) { - if (logicalColumn <= startLogicalColumn) return startOffset; - if (logicalColumn >= endLogicalColumn) return endOffset; - int currentOffset = startOffset; - int currentLogicalColumn = startLogicalColumn; - int startFragment = 0; - if (offsetIndex != null) { - int pos = Arrays.binarySearch(logicalColumnIndex == null ? offsetIndex : logicalColumnIndex, logicalColumn); - if (pos >= 0) return offsetIndex[pos]; - int beforeInsertPosition = - pos - 2; - currentOffset = beforeInsertPosition < 0 ? startOffset : offsetIndex[beforeInsertPosition]; - currentLogicalColumn = beforeInsertPosition < 0 ? startLogicalColumn : logicalColumnIndex == null ? currentOffset : - logicalColumnIndex[beforeInsertPosition]; - startFragment = INDEX_FREQUENCY * (beforeInsertPosition + 1); - } - for (int i = startFragment; i < fragments.size(); i++) { - LineFragment fragment = fragments.get(i); - int endLogicalColumn = currentLogicalColumn + fragment.getLogicalColumnCount(currentLogicalColumn); - if (logicalColumn <= endLogicalColumn) return currentOffset + fragment.logicalColumnToOffset(currentLogicalColumn, logicalColumn); - currentOffset += fragment.getLength(); - currentLogicalColumn = endLogicalColumn; - } - throw new IllegalStateException(); - } - - private int offsetToLogicalColumn(int offset) { - if (offset <= startOffset) return startLogicalColumn; - if (offset >= endOffset) return endLogicalColumn; - int currentOffset = startOffset; - int currentLogicalColumn = startLogicalColumn; - int startFragment = 0; - if (offsetIndex != null) { - int pos = Arrays.binarySearch(offsetIndex, offset); - if (pos >= 0) return logicalColumnIndex == null ? offset : logicalColumnIndex[pos]; - int beforeInsertPosition = - pos - 2; - currentOffset = beforeInsertPosition < 0 ? startOffset : offsetIndex[beforeInsertPosition]; - currentLogicalColumn = beforeInsertPosition < 0 ? startLogicalColumn : logicalColumnIndex == null ? currentOffset : - logicalColumnIndex[beforeInsertPosition]; - startFragment = INDEX_FREQUENCY * (beforeInsertPosition + 1); - } - for (int i = startFragment; i < fragments.size(); i++) { - LineFragment fragment = fragments.get(i); - int endOffset = currentOffset + fragment.getLength(); - if (offset <= endOffset) return fragment.offsetToLogicalColumn(currentLogicalColumn, offset - currentOffset); - currentOffset = endOffset; - currentLogicalColumn += fragment.getLogicalColumnCount(currentLogicalColumn); - } - throw new IllegalStateException(); - } - - private int createIndices(int startLogicalColumn) { - this.startLogicalColumn = endLogicalColumn = startLogicalColumn; - int indexSize = (fragments.size() - 1) / INDEX_FREQUENCY; - if (indexSize > 0) { - offsetIndex = new int[indexSize]; - } - int i = 0; - int offset = startOffset; - for (LineFragment fragment : fragments) { - if (offsetIndex != null && i > 0 && (i % INDEX_FREQUENCY) == 0) { - int pos = i / INDEX_FREQUENCY - 1; - offsetIndex[pos] = offset; - if (logicalColumnIndex == null && offset != endLogicalColumn) { - logicalColumnIndex = new int[indexSize]; - System.arraycopy(offsetIndex, 0, logicalColumnIndex, 0, pos); - } - if (logicalColumnIndex != null) { - logicalColumnIndex[pos] = endLogicalColumn; - } - } - i++; - offset += fragment.getLength(); - endLogicalColumn += fragment.getLogicalColumnCount(endLogicalColumn); - } - return endLogicalColumn; + return chunk; } } private static class VisualOrderIterator implements Iterator { - private BidiRun[] myRuns; + private final EditorView myView; + private final int myLineStartOffset; + private final BidiRun[] myRuns; private int myRunIndex = 0; + private int myChunkIndex = 0; private int myFragmentIndex = 0; private int myOffsetInsideRun = 0; private VisualFragment myFragment = new VisualFragment(); - private VisualOrderIterator(float startX, int startVisualColumn, int startLogicalColumn, int startOffset, BidiRun[] runsInVisualOrder) { + private VisualOrderIterator(EditorView view, int lineStartOffset, + float startX, int startVisualColumn, int startLogicalColumn, int startOffset, BidiRun[] runsInVisualOrder) { + myView = view; + myLineStartOffset = lineStartOffset; myRuns = runsInVisualOrder; myFragment.startX = startX; myFragment.startVisualColumn = startVisualColumn; @@ -480,7 +428,15 @@ class LineLayout { @Override public boolean hasNext() { - return myRunIndex < myRuns.length && myFragmentIndex < myRuns[myRunIndex].fragments.size(); + if (myRunIndex >= myRuns.length) return false; + BidiRun run = myRuns[myRunIndex]; + Chunk[] chunks = run.getChunks(); + if (myChunkIndex >= chunks.length) return false; + Chunk chunk = chunks[run.isRtl() ? chunks.length - 1 - myChunkIndex : myChunkIndex]; + if (myView != null) { + chunk.ensureLayout(myView, myLineStartOffset, run.isRtl()); + } + return myFragmentIndex < chunk.fragments.size(); } @Override @@ -490,12 +446,12 @@ class LineLayout { } BidiRun run = myRuns[myRunIndex]; - if (myRunIndex == 0 && myFragmentIndex == 0) { + if (myRunIndex == 0 && myChunkIndex == 0 && myFragmentIndex == 0) { myFragment.startLogicalColumn += (run.isRtl() ? run.endOffset : run.startOffset) - myFragment.startOffset; } else { myFragment.startLogicalColumn = myFragment.getEndLogicalColumn(); - if (myFragmentIndex == 0) { + if (myChunkIndex == 0 && myFragmentIndex == 0) { myFragment.startLogicalColumn += (run.isRtl() ? run.endOffset : run.startOffset) - myFragment.getEndOffset(); } myFragment.startVisualColumn = myFragment.getEndVisualColumn(); @@ -503,15 +459,22 @@ class LineLayout { } myFragment.isRtl = run.isRtl(); - myFragment.delegate = run.fragments.get(run.isRtl() ? run.fragments.size() - 1 - myFragmentIndex : myFragmentIndex); + Chunk[] chunks = run.getChunks(); + Chunk chunk = chunks[run.isRtl() ? chunks.length - 1 - myChunkIndex : myChunkIndex]; + assert !chunk.fragments.isEmpty(); + myFragment.delegate = chunk.fragments.get(run.isRtl() ? chunk.fragments.size() - 1 - myFragmentIndex : myFragmentIndex); myFragment.startOffset = run.isRtl() ? run.endOffset - myOffsetInsideRun : run.startOffset + myOffsetInsideRun; myOffsetInsideRun += myFragment.getLength(); myFragmentIndex++; - if (myFragmentIndex >= run.fragments.size()) { + if (myFragmentIndex >= chunk.fragments.size()) { myFragmentIndex = 0; - myOffsetInsideRun = 0; - myRunIndex++; + myChunkIndex++; + if (myChunkIndex >= chunks.length) { + myChunkIndex = 0; + myOffsetInsideRun = 0; + myRunIndex++; + } } return myFragment; @@ -649,77 +612,4 @@ class LineLayout { return isRtl ? startOffset - offset : offset - startOffset; } } - - public static class DummyLineFragment implements LineFragment { - private final int myLength; - private final float myCharWidth; - - public DummyLineFragment(int length, float charWidth) { - myLength = length; - myCharWidth = charWidth; - } - - @Override - public int getLength() { - return myLength; - } - - @Override - public int getLogicalColumnCount(int startColumn) { - return myLength; - } - - @Override - public int getVisualColumnCount(float startX) { - return myLength; - } - - @Override - public int offsetToLogicalColumn(int startColumn, int offset) { - return startColumn + offset; - } - - @Override - public int logicalColumnToOffset(int startColumn, int column) { - return column - startColumn; - } - - @Override - public int logicalToVisualColumn(float startX, int startColumn, int column) { - return column; - } - - @Override - public int visualToLogicalColumn(float startX, int startColumn, int column) { - return column; - } - - @Override - public float visualColumnToX(float startX, int column) { - return column * myCharWidth; - } - - @Override - public int[] xToVisualColumn(float startX, float x) { - float relX = x - startX; - int column = Math.max(0, Math.min(myLength, Math.round(relX / myCharWidth))); - return new int[] {column, relX <= column * myCharWidth ? 0 : 1}; - } - - @Override - public float offsetToX(float startX, int startOffset, int offset) { - return offset * myCharWidth; - } - - @Override - public void draw(Graphics2D g, float x, float y, int startColumn, int endColumn) { - throw new UnsupportedOperationException(); - } - - @NotNull - @Override - public LineFragment subFragment(int startOffset, int endOffset) { - return new DummyLineFragment(endOffset - startOffset, myCharWidth); - } - } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/LogicalPositionCache.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/LogicalPositionCache.java new file mode 100644 index 000000000000..b86b722d8a09 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/LogicalPositionCache.java @@ -0,0 +1,205 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.editor.impl.view; + +import com.intellij.openapi.Disposable; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.LogicalPosition; +import com.intellij.openapi.editor.event.DocumentEvent; +import com.intellij.openapi.editor.ex.PrioritizedDocumentListener; +import com.intellij.openapi.editor.impl.EditorDocumentPriorities; +import com.intellij.util.text.CharArrayUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; + +/** + * Caches information allowing faster offset<->logicalPosition conversions even for long lines. + * Requests for conversion can be made from under read action, document changes and cache invalidation should be done in EDT. + */ +@SuppressWarnings("SynchronizeOnThis") +class LogicalPositionCache implements PrioritizedDocumentListener, Disposable { + private final Document myDocument; + private final EditorView myView; + private ArrayList myLines = new ArrayList(); + private int myTabSize = -1; + private int myDocumentChangeOldEndLine; + + LogicalPositionCache(EditorView view) { + myView = view; + myDocument = view.getEditor().getDocument(); + myDocument.addDocumentListener(this, this); + } + + @Override + public int getPriority() { + return EditorDocumentPriorities.LOGICAL_POSITION_CACHE; + } + + @Override + public void beforeDocumentChange(DocumentEvent event) { + myDocumentChangeOldEndLine = getAdjustedLineNumber(event.getOffset() + event.getOldLength()); + } + + @Override + public void documentChanged(DocumentEvent event) { + int startLine = myDocument.getLineNumber(event.getOffset()); + int newEndLine = getAdjustedLineNumber(event.getOffset() + event.getNewLength()); + invalidateLines(startLine, myDocumentChangeOldEndLine, newEndLine, CharArrayUtil.indexOf(event.getNewFragment(), "\t", 0) == -1); + } + + synchronized void reset(boolean force) { + checkDisposed(); + int oldTabSize = myTabSize; + myTabSize = myView.getTabSize(); + if (force || oldTabSize != myTabSize) { + invalidateLines(0, myLines.size() - 1, myDocument.getLineCount() - 1, true); + } + } + + @NotNull + synchronized LogicalPosition offsetToLogicalPosition(int offset) { + int textLength = myDocument.getTextLength(); + if (offset <= 0 || textLength == 0) { + return new LogicalPosition(0, 0); + } + offset = Math.min(offset, textLength); + int line = myDocument.getLineNumber(offset); + LineData lineData = getLineInfo(line); + return new LogicalPosition(line, lineData.offsetToLogicalColumn(myDocument, line, myTabSize, offset)); + } + + synchronized int logicalPositionToOffset(@NotNull LogicalPosition pos) { + int line = pos.line; + if (line >= myDocument.getLineCount()) return myDocument.getTextLength(); + LineData lineData = getLineInfo(line); + return lineData.logicalColumnToOffset(myDocument, line, myTabSize, pos.column); + } + + private int getAdjustedLineNumber(int offset) { + return myDocument.getTextLength() == 0 ? -1 : myDocument.getLineNumber(offset); + } + + private synchronized void invalidateLines(int startLine, int oldEndLine, int newEndLine, boolean preserveTrivialLines) { + checkDisposed(); + int endLine = Math.min(oldEndLine, newEndLine); + for (int line = startLine; line <= endLine; line++) { + LineData data = myLines.get(line); + if (data != null && (!preserveTrivialLines || data.columnCache != null)) myLines.set(line, null); + } + if (oldEndLine < newEndLine) { + myLines.addAll(oldEndLine + 1, Collections.nCopies(newEndLine - oldEndLine, (LineData)null)); + } else if (oldEndLine > newEndLine) { + myLines.subList(newEndLine + 1, oldEndLine + 1).clear(); + } + } + + @NotNull + private LineData getLineInfo(int line) { + checkDisposed(); + LineData result = myLines.get(line); + if (result == null) { + result = new LineData(myDocument, line, myTabSize); + myLines.set(line, result); + } + return result; + } + + @Override + public synchronized void dispose() { + myLines = null; + } + + private void checkDisposed() { + if (myLines == null) throw new IllegalStateException("Editor is already disposed"); + } + + private static class LineData { + private static final int CACHE_FREQUENCY = 1024; // logical column will be cached for each CACHE_FREQUENCY-th character on the line + + private final int[] columnCache; + + private LineData(@NotNull Document document, int line, int tabSize) { + int start = document.getLineStartOffset(line); + int end = document.getLineEndOffset(line); + int cacheSize = (end - start) / CACHE_FREQUENCY; + int[] cache = new int[cacheSize]; + CharSequence text = document.getImmutableCharSequence(); + int column = 0; + boolean hasTabs = false; + for (int i = start; i < end; i++) { + if (i > start && (i - start) % CACHE_FREQUENCY == 0) { + cache[(i - start) / CACHE_FREQUENCY - 1] = column; + } + if (text.charAt(i) == '\t') { + column = (column / tabSize + 1) * tabSize; + hasTabs = true; + } + else { + column++; + } + } + columnCache = hasTabs ? cache : null; + } + + private int offsetToLogicalColumn(@NotNull Document document, int line, int tabSize, int offset) { + offset = Math.min(offset, document.getLineEndOffset(line)); + int lineStartOffset = document.getLineStartOffset(line); + int relOffset = offset - lineStartOffset; + if (columnCache == null) return relOffset; + int cacheIndex = relOffset / CACHE_FREQUENCY; + int startOffset = lineStartOffset + cacheIndex * CACHE_FREQUENCY; + int column = cacheIndex == 0 ? 0 : columnCache[cacheIndex - 1]; + CharSequence text = document.getImmutableCharSequence(); + for (int i = startOffset; i < offset; i++) { + if (text.charAt(i) == '\t') { + column = (column / tabSize + 1) * tabSize; + } + else { + column++; + } + } + return column; + } + + private int logicalColumnToOffset(@NotNull Document document, int line, int tabSize, int logicalColumn) { + int lineStartOffset = document.getLineStartOffset(line); + int lineEndOffset = document.getLineEndOffset(line); + if (columnCache == null) { + int result = lineStartOffset + logicalColumn; + return result < 0 || // guarding over overflow + result > lineEndOffset ? lineEndOffset : result; + } + int pos = Arrays.binarySearch(columnCache, logicalColumn); + if (pos >= 0) return lineStartOffset + (pos + 1) * CACHE_FREQUENCY; + int startOffset = lineStartOffset + (- pos - 1) * CACHE_FREQUENCY; + int column = pos == -1 ? 0 : columnCache[- pos - 2]; + CharSequence text = document.getImmutableCharSequence(); + for (int i = startOffset; i < lineEndOffset; i++) { + if (text.charAt(i) == '\t') { + column = (column / tabSize + 1) * tabSize; + } + else { + column++; + } + if (logicalColumn < column) return i; + } + return lineEndOffset; + } + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/TabFragment.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/TabFragment.java index 5dbd1f503ea8..1b2f2135572d 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/TabFragment.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/TabFragment.java @@ -50,16 +50,6 @@ class TabFragment implements LineFragment { return EditorUtil.columnsNumber((int)(x - startX), myView.getPlainSpaceWidth()); } - @Override - public int offsetToLogicalColumn(int startColumn, int offset) { - return startColumn + (offset == 0 ? 0 : getLogicalColumnCount(startColumn)); - } - - @Override - public int logicalColumnToOffset(int startColumn, int column) { - return column < (startColumn + getLogicalColumnCount(startColumn)) ? 0 : 1; - } - @Override public void draw(Graphics2D g, float x, float y, int startColumn, int endColumn) { } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/TextFragment.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/TextFragment.java index 2dcb3be2d09f..c11a07a8075c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/TextFragment.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/TextFragment.java @@ -72,16 +72,6 @@ class TextFragment implements LineFragment { return myCharPositions.length; } - @Override - public int offsetToLogicalColumn(int startColumn, int offset) { - return startColumn + offset; - } - - @Override - public int logicalColumnToOffset(int startColumn, int column) { - return column - startColumn; - } - @Override public void draw(Graphics2D g, float x, float y, int startColumn, int endColumn) { assert startColumn >= 0; @@ -201,16 +191,6 @@ class TextFragment implements LineFragment { return getLength(); } - @Override - public int offsetToLogicalColumn(int startColumn, int offset) { - return startColumn + offset; - } - - @Override - public int logicalColumnToOffset(int startColumn, int column) { - return column - startColumn; - } - @Override public int logicalToVisualColumn(float startX, int startColumn, int column) { return column; diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/TextLayoutCache.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/TextLayoutCache.java index 4c6d884fa60a..1e30841f26dc 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/TextLayoutCache.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/TextLayoutCache.java @@ -22,24 +22,28 @@ import com.intellij.openapi.editor.ex.PrioritizedDocumentListener; import com.intellij.openapi.editor.impl.EditorDocumentPriorities; import org.jetbrains.annotations.NotNull; +import java.awt.*; import java.util.ArrayList; import java.util.Collections; /** - * Editor text layout storage. It's cached on a per-line basis, created lazily (when requested), and invalidated on document changes or - * when explicitly requested. + * Editor text layout storage. Layout is stored on a per-logical-line basis, + * it's created lazily (when requested) and invalidated on document changes or when explicitly requested. + * + * @see LineLayout */ class TextLayoutCache implements PrioritizedDocumentListener, Disposable { private final EditorView myView; private final Document myDocument; + private final LineLayout myBidiNotRequiredMarker; private ArrayList myLines = new ArrayList(); private int myDocumentChangeOldEndLine; - private boolean myQuickLayoutsEnabled; TextLayoutCache(EditorView view) { myView = view; myDocument = view.getEditor().getDocument(); myDocument.addDocumentListener(this, this); + myBidiNotRequiredMarker = new LineLayout(view, "", Font.PLAIN); } @Override @@ -56,7 +60,7 @@ class TextLayoutCache implements PrioritizedDocumentListener, Disposable { public void documentChanged(DocumentEvent event) { int startLine = myDocument.getLineNumber(event.getOffset()); int newEndLine = getAdjustedLineNumber(event.getOffset() + event.getNewLength()); - invalidateLines(startLine, myDocumentChangeOldEndLine, newEndLine); + invalidateLines(startLine, myDocumentChangeOldEndLine, newEndLine, !LineLayout.isBidiLayoutRequired(event.getNewFragment())); } @Override @@ -67,17 +71,24 @@ class TextLayoutCache implements PrioritizedDocumentListener, Disposable { private int getAdjustedLineNumber(int offset) { return myDocument.getTextLength() == 0 ? -1 : myDocument.getLineNumber(offset); } - - void resetToDocumentSize() { + + void resetToDocumentSize(boolean documentChangedWithoutNotification) { checkDisposed(); - invalidateLines(0, myLines.size() - 1, myDocument.getLineCount() - 1); + invalidateLines(0, myLines.size() - 1, myDocument.getLineCount() - 1, !documentChangedWithoutNotification); + } + + void invalidateLines(int startLine, int endLine) { + invalidateLines(startLine, endLine, endLine, true); } - void invalidateLines(int startLine, int oldEndLine, int newEndLine) { + private void invalidateLines(int startLine, int oldEndLine, int newEndLine, boolean keepBidiNotRequiredState) { checkDisposed(); int endLine = Math.min(oldEndLine, newEndLine); for (int line = startLine; line <= endLine; line++) { - myLines.set(line, null); + LineLayout lineLayout = myLines.get(line); + if (lineLayout != null) { + myLines.set(line, keepBidiNotRequiredState && lineLayout.isLtr() ? myBidiNotRequiredMarker : null); + } } if (oldEndLine < newEndLine) { myLines.addAll(oldEndLine + 1, Collections.nCopies(newEndLine - oldEndLine, (LineLayout)null)); @@ -85,31 +96,25 @@ class TextLayoutCache implements PrioritizedDocumentListener, Disposable { myLines.subList(newEndLine + 1, oldEndLine + 1).clear(); } } - + @NotNull LineLayout getLineLayout(int line) { checkDisposed(); LineLayout result = myLines.get(line); - if (result == null) { - int lineStart = myDocument.getLineStartOffset(line); - int lineEnd = myDocument.getLineEndOffset(line); - if (myQuickLayoutsEnabled) { - result = new LineLayout(myView, lineEnd - lineStart); - myView.getSizeManager().quickLineLayoutCreated(); - } - else { - result = new LineLayout(myView, lineStart, lineEnd, myView.getFontRenderContext()); - myLines.set(line, result); - myView.getSizeManager().lineLayoutCreated(line); - } + if (result == null || result == myBidiNotRequiredMarker) { + result = createLineLayout(line, result == myBidiNotRequiredMarker); + myLines.set(line, result); } return result; } - - void enableQuickLayouts(boolean enabled) { - myQuickLayoutsEnabled = enabled; + + @NotNull + private LineLayout createLineLayout(int line, boolean skipBidiLayout) { + int lineStart = myDocument.getLineStartOffset(line); + int lineEnd = myDocument.getLineEndOffset(line); + return new LineLayout(myView, lineStart, lineEnd, skipBidiLayout); } - + private void checkDisposed() { if (myLines == null) throw new IllegalStateException("Editor is already disposed"); } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/VisualLineFragmentsIterator.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/VisualLineFragmentsIterator.java index 0db5dbcaefc4..2afdcb30e755 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/VisualLineFragmentsIterator.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/VisualLineFragmentsIterator.java @@ -34,11 +34,11 @@ import java.util.NoSuchElementException; */ class VisualLineFragmentsIterator implements Iterator { - static Iterable create(final EditorView view, final int offset, final boolean beforeSoftWrap) { + static Iterable create(final EditorView view, final int offset, final boolean beforeSoftWrap, final boolean quick) { return new Iterable() { @Override public Iterator iterator() { - return new VisualLineFragmentsIterator(view, offset, beforeSoftWrap); + return new VisualLineFragmentsIterator(view, offset, beforeSoftWrap, quick); } }; } @@ -48,6 +48,7 @@ class VisualLineFragmentsIterator implements Iterator extends AbstractExtensionPointBean { + public static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.schemeExporter"); + + @Attribute("name") + public String name; + + @Attribute("schemeClass") + public String schemeClass; + + @Attribute("implementationClass") + public String implementationClass; + + private final LazyInstance> myExporterInstance = new LazyInstance>() { + @Override + protected Class> getInstanceClass() throws ClassNotFoundException { + return findClass(implementationClass); + } + }; + + public SchemeExporter getInstance() { + return myExporterInstance.getValue(); + } + + /** + * Finds extensions supporting the given schemeClass + * @param schemeClass The class of the scheme to search extensions for. + * @return A collection of exporters capable of exporting schemes of the given class. An empty collection is returned if there are + * no matching exporters. + */ + @NotNull + public static Collection> getExtensions(Class schemeClass) { + List> exporters = new ArrayList>(); + for (SchemeExporterEP exporterEP : EP_NAME.getExtensions()) { + if (schemeClass.getName().equals(exporterEP.schemeClass)) { + //noinspection unchecked + exporters.add((SchemeExporterEP)exporterEP); + } + } + return exporters; + } + + /** + * Find an exporter for the given name and scheme class. It is allowed for exporters to have the same name but different scheme classes. + * @param name The exporter name as defined in plug-in configuration. + * @param schemeClass The scheme class the exporter has to support. + * @return The found exporter or null if there are no exporters for the given name and scheme class. + */ + @Nullable + public static SchemeExporter getExporter(@NotNull String name, Class schemeClass) { + for (SchemeExporterEP exporterEP : getExtensions(schemeClass)) { + if (name.equals(exporterEP.name)) { + return exporterEP.getInstance(); + } + } + return null; + } +} 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 727a8b70a91f..9eda882e61f6 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 @@ -18,6 +18,7 @@ package com.intellij.openapi.ui.impl; import com.intellij.ide.DataManager; import com.intellij.ide.IdeEventQueue; import com.intellij.ide.impl.TypeSafeDataProviderAdapter; +import com.intellij.ide.ui.AntialiasingType; import com.intellij.ide.ui.UISettings; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.Application; @@ -57,6 +58,7 @@ import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import sun.swing.SwingUtilities2; import javax.swing.*; import java.awt.*; @@ -955,12 +957,15 @@ public class DialogWrapperPeerImpl extends DialogWrapperPeer implements FocusTra } @Override - public void windowOpened(WindowEvent e) { + public void windowOpened(final WindowEvent e) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { myOpened = true; final DialogWrapper activeWrapper = getActiveWrapper(); + for (JComponent c : UIUtil.uiTraverser(e.getWindow()).filter(JComponent.class)) { + c.putClientProperty(SwingUtilities2.AA_TEXT_PROPERTY_KEY, AntialiasingType.getAAHintForSwingComponent()); + } if (activeWrapper == null) { myFocusedCallback.setRejected(); myTypeAheadDone.setRejected(); diff --git a/platform/platform-impl/src/com/intellij/openapi/util/extensions.kt b/platform/platform-impl/src/com/intellij/openapi/util/extensions.kt index 34c17de215e8..18c500ac8df9 100644 --- a/platform/platform-impl/src/com/intellij/openapi/util/extensions.kt +++ b/platform/platform-impl/src/com/intellij/openapi/util/extensions.kt @@ -34,5 +34,5 @@ package com.intellij.openapi.util * Put here extension functions and variables for classes from util/util-rt modules */ -operator fun Pair.component1() = this.first -operator fun Pair.component2() = this.second +/*operator*/ fun Pair.component1() = this.first +/*operator*/ fun Pair.component2() = this.second diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/NativeFileWatcherImpl.java b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/NativeFileWatcherImpl.java index 5b9980e4641b..0191311514eb 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/NativeFileWatcherImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/NativeFileWatcherImpl.java @@ -168,11 +168,11 @@ public class NativeFileWatcherImpl extends PluggableFileWatcher { @Nullable private static String getExecutableName(final boolean withSubDir) { - if (SystemInfo.isWindows) return (withSubDir ? "win" + File.separator : "") + "fsnotifier.exe"; - else if (SystemInfo.isMac) return (withSubDir ? "mac" + File.separator : "") + "fsnotifier"; - else if (SystemInfo.isLinux) return (withSubDir ? "linux" + File.separator : "") + - ("arm".equals(SystemInfo.OS_ARCH) ? (SystemInfo.is32Bit ? "fsnotifier-arm" : null) - : (SystemInfo.isAMD64 ? "fsnotifier64" : "fsnotifier")); + if (SystemInfo.isWindows) return (withSubDir ? "win" + File.separator : "") + (SystemInfo.isAMD64 ? "fsnotifier64.exe" : "fsnotifier.exe"); + if (SystemInfo.isMac) return (withSubDir ? "mac" + File.separator : "") + "fsnotifier"; + if (SystemInfo.isLinux) return (withSubDir ? "linux" + File.separator : "") + + ("arm".equals(SystemInfo.OS_ARCH) ? (SystemInfo.is32Bit ? "fsnotifier-arm" : null) + : (SystemInfo.isAMD64 ? "fsnotifier64" : "fsnotifier")); return null; } diff --git a/platform/platform-resources-en/src/fileTemplates/internal/HTML4 File.html.ft b/platform/platform-resources-en/src/fileTemplates/internal/HTML4 File.html.ft index 2dfc22e288d9..ced4071a80e2 100644 --- a/platform/platform-resources-en/src/fileTemplates/internal/HTML4 File.html.ft +++ b/platform/platform-resources-en/src/fileTemplates/internal/HTML4 File.html.ft @@ -5,6 +5,6 @@ #[[$Title$]]# - +#[[$END$]]# \ No newline at end of file diff --git a/platform/platform-resources-en/src/messages/ApplicationBundle.properties b/platform/platform-resources-en/src/messages/ApplicationBundle.properties index 663253c49fdc..9132547c25ee 100644 --- a/platform/platform-resources-en/src/messages/ApplicationBundle.properties +++ b/platform/platform-resources-en/src/messages/ApplicationBundle.properties @@ -483,7 +483,7 @@ checkbox.keep.empty.throws.tags=Keep empty @throws tags checkbox.enable.leading.asterisks=Enable leading asterisks checkbox.use.throws.rather.than.exception=Use @throws rather than @exception checkbox.wrap.at.right.margin=Wrap at right margin -checkbox.generate.p.on.empty.lines=Generate "

" on empty lines +checkbox.generate.p.on.empty.lines=Generate "

" on empty lines checkbox.keep.empty.lines=Keep empty lines checkbox.do.not.wrap.one.line.comments=Do not wrap one line comments checkbox.preserve.line.feeds=Preserve line feeds @@ -506,6 +506,11 @@ message.code.style.scheme.already.exists=Scheme {0} already exists. Overwrite se code.style.scheme.import.unnamed=Unnamed message.code.style.scheme.import.success={0} settings were imported to {1} scheme.{2} message.code.style.scheme.import.failure={0} import failed with error message: {1} +code.style.scheme.exporter.ui.export.as.title=Export As +code.style.scheme.exporter.ui.file.chooser.title=Target File +code.style.scheme.exporter.ui.file.chooser.message=Export to +code.style.scheme.exporter.ui.code.style.exported.message={0} code style exported to {1} +code.style.scheme.exporter.ui.cannot.write.message=Cannot write to selected file/location. label.name=Name: error.scheme.must.have.a.name=Scheme must have a name diff --git a/platform/platform-resources/src/META-INF/LangExtensions.xml b/platform/platform-resources/src/META-INF/LangExtensions.xml index f0adcc31831e..7dc527d1a97d 100644 --- a/platform/platform-resources/src/META-INF/LangExtensions.xml +++ b/platform/platform-resources/src/META-INF/LangExtensions.xml @@ -928,6 +928,11 @@ + + diff --git a/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml b/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml index 70609a231b33..ebb00b13df86 100644 --- a/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml @@ -219,6 +219,11 @@ + + + + + diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/LiteralWithSubstitutionHandler.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/LiteralWithSubstitutionHandler.java index 78967106755f..57e37f3a273e 100644 --- a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/LiteralWithSubstitutionHandler.java +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/LiteralWithSubstitutionHandler.java @@ -2,11 +2,10 @@ package com.intellij.structuralsearch.impl.matcher.handlers; import com.intellij.psi.PsiElement; import com.intellij.structuralsearch.impl.matcher.MatchContext; -import com.intellij.structuralsearch.impl.matcher.predicates.RegExpPredicate; +import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.List; /** * Created by IntelliJ IDEA. @@ -20,30 +19,30 @@ public class LiteralWithSubstitutionHandler extends MatchingHandler { private Matcher matcher; private final List handlers; - public LiteralWithSubstitutionHandler(String _matchedExpression,List _handlers) { + public LiteralWithSubstitutionHandler(String _matchedExpression, List _handlers) { matchExpression = _matchedExpression; handlers = _handlers; } + @Override public boolean match(PsiElement patternNode, PsiElement matchedNode, MatchContext context) { - final String text = RegExpPredicate.getMeaningfulText(matchedNode); - int offset = matchedNode.getText().indexOf(text); + final String text = matchedNode.getText(); if (matcher==null) { matcher = Pattern.compile(matchExpression).matcher(text); } else { matcher.reset(text); } - while (matcher.find()) { - for (int i = 0; i < handlers.size(); ++i) { - SubstitutionHandler handler = handlers.get(i); - - if (!handler.handle(matchedNode,offset + matcher.start(i+1), offset + matcher.end(i+1),context)) { - return false; - } - } - return true; + if (!matcher.find()) { + return false; } - return false; + for (int i = 0; i < handlers.size(); ++i) { + final SubstitutionHandler handler = handlers.get(i); + + if (!handler.handle(matchedNode, matcher.start(i + 1), matcher.end(i + 1), context)) { + return false; + } + } + return true; } } diff --git a/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java b/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java index a0ff99c09ac9..e8b23ffd922c 100644 --- a/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java +++ b/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java @@ -685,14 +685,42 @@ public class StructuralSearchTest extends StructuralSearchTestCase { 1, findMatchesCount(s11,s12_3) ); + } + + public void testFindArrayDeclarations() { + String source = "class A {" + + " String ss[][];" + + " int f()[] {" + + " return null;" + + " }" + + "}"; - String source = "class A { String ss[][]; }"; String target = "String[][] $s$;"; - assertEquals( - "should find multi dimensional c-style array declarations", - 1, - findMatchesCount(source, target) - ); + assertEquals("should find multi-dimensional c-style array declarations", 1, findMatchesCount(source, target)); + + String target2 = "class '_A { int[] 'f(); }"; + assertEquals("should find c-style method return type declarations", 1, findMatchesCount(source, target2)); + + String target3 = "class '_A { int 'f(); }"; + assertEquals("should not find methods with array return types",0, findMatchesCount(source, target3)); + + String source2 = "class A {" + + " void y(int... i) {}" + + " void y(String... ss) {}" + + " void y(boolean b) {}" + + "}"; + assertEquals("find ellipsis type 1", 1, findMatchesCount(source2, "String[] '_a")); + assertEquals("find ellipsis type 2", 1, findMatchesCount(source2, "int[] '_a")); + assertEquals("find ellipsis type 3", 1, findMatchesCount(source2, "class '_X { void '_m(int... '_a); }")); + assertEquals("find ellipsis type 4", 2, findMatchesCount(source2, "'_T[] '_a")); + + String source3 = "class A {" + + " private int[] is;" + + "}"; + assertEquals("find primitive array 1", 1, findMatchesCount(source3, "int[] '_a;")); + assertEquals("find primitive array 2", 1, findMatchesCount(source3, "'_T[] '_a;")); + assertEquals("find primitive array 3", 1, findMatchesCount(source3, "'_T:[regex( int )][] '_a;")); + assertEquals("find primitive array 4", 1, findMatchesCount(source3, "'_T:[regex( int\\[\\] )] '_a;")); } // @todo support back references (\1 in another reg exp or as fild member) diff --git a/platform/testFramework/test-framework-java8/path.kt b/platform/testFramework/test-framework-java8/path.kt index 47fe2577fbbc..2dd37a778a7f 100644 --- a/platform/testFramework/test-framework-java8/path.kt +++ b/platform/testFramework/test-framework-java8/path.kt @@ -1,32 +1,22 @@ package com.intellij.testFramework +import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.vfs.LocalFileSystem import com.intellij.openapi.vfs.VfsUtil import com.intellij.openapi.vfs.VirtualFile import java.io.File -import java.io.IOException -import java.nio.file.FileVisitResult import java.nio.file.Files import java.nio.file.Path -import java.nio.file.SimpleFileVisitor -import java.nio.file.attribute.BasicFileAttributes import java.nio.file.attribute.FileTime fun Path.exists(): Boolean = Files.exists(this) fun Path.createDirectories(): Path = Files.createDirectories(this) -fun Path.deleteRecursively(): Path = if (exists()) Files.walkFileTree(this, object : SimpleFileVisitor() { - override fun visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult { - Files.delete(file) - return FileVisitResult.CONTINUE - } - - override fun postVisitDirectory(dir: Path, exc: IOException?): FileVisitResult { - Files.delete(dir) - return FileVisitResult.CONTINUE - } -}) else this +fun Path.deleteRecursively(): Path { + FileUtil.delete(toFile()) + return this +} fun Path.getLastModifiedTime(): FileTime? = Files.getLastModifiedTime(this) diff --git a/platform/util/src/com/intellij/util/io/storage/HeavyProcessLatch.java b/platform/util/src/com/intellij/util/io/storage/HeavyProcessLatch.java index 6e55c2cdf967..3bd6468d289e 100644 --- a/platform/util/src/com/intellij/util/io/storage/HeavyProcessLatch.java +++ b/platform/util/src/com/intellij/util/io/storage/HeavyProcessLatch.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,7 +38,7 @@ public class HeavyProcessLatch { } /** - * @deprecated use {@link #processStarted(java.lang.String)} instead + * @deprecated use {@link #processStarted(String)} instead */ @Deprecated public void processStarted() { @@ -54,17 +54,22 @@ public class HeavyProcessLatch { return new AccessToken() { @Override public void finish() { - synchronized (myHeavyProcesses) { - myHeavyProcesses.remove(operationName); - } + processFinished(operationName); } }; } - @Deprecated // use processStarted(String) + /** + * @deprecated use {@link #processStarted(String)} instead + */ + @Deprecated public void processFinished() { + processFinished(""); + } + + private void processFinished(@NotNull String operationName) { synchronized (myHeavyProcesses) { - myHeavyProcesses.remove(""); + myHeavyProcesses.remove(operationName); } myEventDispatcher.getMulticaster().processFinished(); } @@ -83,9 +88,8 @@ public class HeavyProcessLatch { public interface HeavyProcessListener extends EventListener { - public void processStarted(); - - public void processFinished(); + void processStarted(); + void processFinished(); } public void addListener(@NotNull Disposable parentDisposable, @NotNull HeavyProcessListener listener) { diff --git a/platform/util/src/com/intellij/util/ui/UIUtil.java b/platform/util/src/com/intellij/util/ui/UIUtil.java index daed73559738..b5bca86ac6e9 100644 --- a/platform/util/src/com/intellij/util/ui/UIUtil.java +++ b/platform/util/src/com/intellij/util/ui/UIUtil.java @@ -3077,13 +3077,25 @@ public class UIUtil { // Evaluate the value depending on our current theme if (lcdContrastValue == 0) { - lcdContrastValue = UIUtil.isUnderDarcula() ? 100 : 250; + if (SystemInfo.isMacIntel64) { + lcdContrastValue = UIUtil.isUnderDarcula() ? 100 : 250; + } else { + Map map = (Map)Toolkit.getDefaultToolkit().getDesktopProperty("awt.font.desktophints"); + + if (map == null) { + lcdContrastValue = 140; + } else { + Object o = map.get(RenderingHints.KEY_TEXT_LCD_CONTRAST); + lcdContrastValue = (o == null) ? 140 : ((Integer)o); + } + } } if (lcdContrastValue < 100 || lcdContrastValue > 250) { // the default value lcdContrastValue = 140; } + return lcdContrastValue; } 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 655da7d91500..c3574e4d61a2 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 @@ -181,7 +181,8 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec invokeAfterUpdate(new Runnable() { @Override public void run() { - if (getChangeList(oldList.getId()) == null) { + LocalChangeList actualList = getChangeList(oldList.getId()); + if (actualList == null) { return; // removed already } @@ -189,7 +190,7 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec config.REMOVE_EMPTY_INACTIVE_CHANGELISTS != VcsShowConfirmationOption.Value.DO_ACTION_SILENTLY) { myListsToBeDeleted.add(oldList); } else { - deleteEmptyChangeLists(Collections.singletonList(oldList)); + deleteEmptyChangeLists(Collections.singletonList(actualList)); } } }, InvokeAfterUpdateMode.SILENT, null, null); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/ChangeListRemoveConfirmation.kt b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/ChangeListRemoveConfirmation.kt index d74efb6779bc..a8df5b8833ea 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/ChangeListRemoveConfirmation.kt +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/ChangeListRemoveConfirmation.kt @@ -20,7 +20,6 @@ import com.intellij.openapi.vcs.ProjectLevelVcsManager import com.intellij.openapi.vcs.changes.ChangeListManager import com.intellij.openapi.vcs.changes.LocalChangeList import com.intellij.util.ThreeState -import com.intellij.util.containers.ContainerUtil abstract class ChangeListRemoveConfirmation() { @@ -29,32 +28,35 @@ abstract class ChangeListRemoveConfirmation() { companion object { @JvmStatic fun processLists(project: Project, explicitly: Boolean, allLists: Collection, ask: ChangeListRemoveConfirmation) { - val confirmationAsked = ContainerUtil.newIdentityTroveSet() - val doNotRemove = ContainerUtil.newIdentityTroveSet() + val allIds = allLists.map { it.id } + val confirmationAsked = hashSetOf() + val doNotRemove = hashSetOf() - for (list in allLists) { + val manager = ChangeListManager.getInstance(project) + for (id in allIds) { for (vcs in ProjectLevelVcsManager.getInstance(project).getAllActiveVcss()) { - val permission = vcs.mayRemoveChangeList(list, explicitly) + val list = manager.getChangeList(id) + val permission = if (list == null) ThreeState.NO else vcs.mayRemoveChangeList(list, explicitly) if (permission != ThreeState.UNSURE) { - confirmationAsked.add(list) + confirmationAsked.add(id) } if (permission == ThreeState.NO) { - doNotRemove.add(list) + doNotRemove.add(id) break } } } - val toAsk = allLists.filter { it !in confirmationAsked && it !in doNotRemove } - if (toAsk.isNotEmpty() && !ask.askIfShouldRemoveChangeLists(toAsk)) { + val toAsk = allIds.filter { it !in confirmationAsked && it !in doNotRemove } + if (toAsk.isNotEmpty() && !ask.askIfShouldRemoveChangeLists(toAsk.map { manager.getChangeList(it) }.filterNotNull())) { doNotRemove.addAll(toAsk) } - val toRemove = allLists.filter { it !in doNotRemove } - val active = toRemove.find { it.isDefault() } - toRemove.forEach { if (it != active) ChangeListManager.getInstance(project).removeChangeList(it.getName()) } + val toRemove = allIds.filter { it !in doNotRemove }.map { manager.getChangeList(it) }.filterNotNull() + val active = toRemove.find { it.isDefault } + toRemove.forEach { if (it != active) manager.removeChangeList(it.name) } if (active != null && RemoveChangeListAction.confirmActiveChangeListRemoval(project, listOf(active), active.getChanges().isEmpty())) { - ChangeListManager.getInstance(project).removeChangeList(active.getName()) + manager.removeChangeList(active.name) } } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsCheckBoxWithSpinnerConfigurable.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsCheckBoxWithSpinnerConfigurable.java index fa4b7f7ab297..e4fa34ae7687 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsCheckBoxWithSpinnerConfigurable.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsCheckBoxWithSpinnerConfigurable.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package com.intellij.openapi.vcs.configurable; import com.intellij.openapi.options.Configurable; import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.*; @@ -48,6 +49,7 @@ public abstract class VcsCheckBoxWithSpinnerConfigurable implements Configurable } @Override + @NotNull public JComponent createComponent() { JPanel wrapper = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); myHighlightRecentlyChanged = new JCheckBox(myCheckboxText); diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryFinallyCanBeTryWithResourcesInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryFinallyCanBeTryWithResourcesInspection.java index 58266fae1716..c5a318fc17a7 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryFinallyCanBeTryWithResourcesInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryFinallyCanBeTryWithResourcesInspection.java @@ -142,8 +142,8 @@ public class TryFinallyCanBeTryWithResourcesInspection extends BaseInspection { } separator = true; } - int j = 1; if (!unwantedChildren.isEmpty()) { + int j = 1; while (!unwantedChildren.contains(Integer.valueOf(j)) && j < tryBlockChildren.length - 1) { tryStatement.getParent().addBefore(tryBlockChildren[j], tryStatement); unwantedChildren.add(j); @@ -329,7 +329,7 @@ public class TryFinallyCanBeTryWithResourcesInspection extends BaseInspection { hasInitializer = !PsiType.NULL.equals(type); } final int index = findInitialization(tryBlockStatements, variable, hasInitializer); - if (!(index >= 0 ^ hasInitializer) || isVariableUsedOutsideContext(variable, tryBlock)) { + if ((index >= 0) == hasInitializer || isVariableUsedOutsideContext(variable, tryBlock)) { return; } } diff --git a/python/src/com/jetbrains/python/module/PyContentEntriesEditor.java b/python/src/com/jetbrains/python/module/PyContentEntriesEditor.java index 7bb465769749..b47de28d0e40 100644 --- a/python/src/com/jetbrains/python/module/PyContentEntriesEditor.java +++ b/python/src/com/jetbrains/python/module/PyContentEntriesEditor.java @@ -50,14 +50,14 @@ public class PyContentEntriesEditor extends CommonContentEntriesEditor { private final Module myModule; private Disposable myFilePointersDisposable; private MyContentEntryEditor myContentEntryEditor; - private FacetErrorPanel myErrorPanel; + private FacetErrorPanel myWarningPanel; public PyContentEntriesEditor(Module module, ModuleConfigurationState moduleConfigurationState, JpsModuleSourceRootType... rootTypes) { super(module.getName(), moduleConfigurationState, rootTypes); myRootTypeProviders = Extensions.getExtensions(PyRootTypeProvider.EP_NAME); myModule = module; - myErrorPanel = new FacetErrorPanel(); + myWarningPanel = new FacetErrorPanel(); reset(); } @@ -105,6 +105,10 @@ public class PyContentEntriesEditor extends CommonContentEntriesEditor { if (myFilePointersDisposable != null) { Disposer.dispose(myFilePointersDisposable); } + + for (PyRootTypeProvider provider : myRootTypeProviders) { + provider.disposeUIResources(myModule); + } } @Override @@ -292,10 +296,10 @@ public class PyContentEntriesEditor extends CommonContentEntriesEditor { @Override protected void addAdditionalSettingsToPanel(JPanel mainPanel) { - mainPanel.add(myErrorPanel.getComponent(), BorderLayout.SOUTH); + mainPanel.add(myWarningPanel.getComponent(), BorderLayout.SOUTH); } - public FacetErrorPanel getErrorPanel() { - return myErrorPanel; + public FacetErrorPanel getWarningPanel() { + return myWarningPanel; } } diff --git a/python/src/com/jetbrains/python/module/PyRootTypeProvider.java b/python/src/com/jetbrains/python/module/PyRootTypeProvider.java index c417d8f28079..871165cc513a 100644 --- a/python/src/com/jetbrains/python/module/PyRootTypeProvider.java +++ b/python/src/com/jetbrains/python/module/PyRootTypeProvider.java @@ -48,7 +48,7 @@ public abstract class PyRootTypeProvider { } }; - public abstract void reset(@NotNull final Disposable disposable, PyContentEntriesEditor editor, Module module); + public abstract void reset(@NotNull final Disposable disposable, PyContentEntriesEditor editor, @NotNull Module module); public abstract void apply(Module module); @@ -76,6 +76,9 @@ public abstract class PyRootTypeProvider { return null; } + public void disposeUIResources(@NotNull Module module) { + } + protected class RootEntryEditingAction extends ContentEntryEditingAction { private final Disposable myDisposable; @@ -123,12 +126,12 @@ public abstract class PyRootTypeProvider { private void addRoot(VirtualFilePointer root, PyContentEntriesEditor editor) { editor.getContentEntryEditor().addRoot(this, root); - editor.getErrorPanel().getValidatorsManager().validate(); + editor.getWarningPanel().getValidatorsManager().validate(); } protected void removeRoot(VirtualFile selectedFile, PyContentEntriesEditor editor, ModifiableRootModel model) { editor.getContentEntryEditor().removeRoot(null, selectedFile.getUrl(), this); - editor.getErrorPanel().getValidatorsManager().validate(); + editor.getWarningPanel().getValidatorsManager().validate(); } protected boolean hasRoot(VirtualFile file, PyContentEntriesEditor editor) { diff --git a/xml/xml-psi-impl/src/com/intellij/psi/impl/source/xml/XmlAttributeImpl.java b/xml/xml-psi-impl/src/com/intellij/psi/impl/source/xml/XmlAttributeImpl.java index a057e950b672..46a8def50565 100644 --- a/xml/xml-psi-impl/src/com/intellij/psi/impl/source/xml/XmlAttributeImpl.java +++ b/xml/xml-psi-impl/src/com/intellij/psi/impl/source/xml/XmlAttributeImpl.java @@ -49,7 +49,7 @@ import static com.intellij.codeInsight.completion.CompletionUtilCore.DUMMY_IDENT /** * @author Mike */ -public class XmlAttributeImpl extends XmlElementImpl implements XmlAttribute { +public class XmlAttributeImpl extends XmlElementImpl implements XmlAttribute, HintedReferenceHost { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.xml.XmlAttributeImpl"); private final int myHC = ourHC++; @@ -330,14 +330,36 @@ public class XmlAttributeImpl extends XmlElementImpl implements XmlAttribute { @Override public PsiReference getReference() { - final PsiReference[] refs = getReferences(); + final PsiReference[] refs = getReferences(PsiReferenceService.Hints.NO_HINTS); if (refs.length > 0) return refs[0]; return null; } @Override + public boolean shouldAskParentForReferences(@NotNull PsiReferenceService.Hints hints) { + return false; + } + + /** + * Use {@link #getReferences(PsiReferenceService.Hints)} instead of calling or overriding this method. + */ + @Deprecated @NotNull - public PsiReference[] getReferences() { + @Override + public final PsiReference[] getReferences() { + return getReferences(PsiReferenceService.Hints.NO_HINTS); + } + + @NotNull + @Override + public PsiReference[] getReferences(@NotNull PsiReferenceService.Hints hints) { + if (hints.offsetInElement != null) { + XmlElement nameElement = getNameElement(); + if (nameElement == null || hints.offsetInElement > nameElement.getStartOffsetInParent() + nameElement.getTextLength()) { + return PsiReference.EMPTY_ARRAY; + } + } + final PsiReference[] referencesFromProviders = ReferenceProvidersRegistry.getReferencesFromProviders(this); PsiReference[] refs; if (isNamespaceDeclaration()) { diff --git a/xml/xml-psi-impl/src/com/intellij/psi/impl/source/xml/XmlTagImpl.java b/xml/xml-psi-impl/src/com/intellij/psi/impl/source/xml/XmlTagImpl.java index 728df741bb88..986ff7540a96 100644 --- a/xml/xml-psi-impl/src/com/intellij/psi/impl/source/xml/XmlTagImpl.java +++ b/xml/xml-psi-impl/src/com/intellij/psi/impl/source/xml/XmlTagImpl.java @@ -74,7 +74,7 @@ import java.util.*; * @author Mike */ -public class XmlTagImpl extends XmlElementImpl implements XmlTag { +public class XmlTagImpl extends XmlElementImpl implements XmlTag, HintedReferenceHost { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.xml.XmlTagImpl"); @NonNls private static final String XML_NS_PREFIX = "xml"; private static final RecursionGuard ourGuard = RecursionManager.createGuard("xmlTag"); @@ -93,10 +93,17 @@ public class XmlTagImpl extends XmlElementImpl implements XmlTag { .create(tags, PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT, tag.getContainingFile()); } }; + private static final Comparator RANGE_COMPARATOR = new Comparator() { + @Override + public int compare(TextRange range1, TextRange range2) { + return range1.getStartOffset() - range2.getStartOffset(); + } + }; private final int myHC = ourHC++; private volatile String myName = null; private volatile String myLocalName; private volatile XmlAttribute[] myAttributes = null; + private volatile TextRange[] myTextElements = null; private volatile Map myAttributeValueMap = null; private volatile XmlTagValue myValue = null; private volatile Map> myNSDescriptorsMap = null; @@ -152,6 +159,7 @@ public class XmlTagImpl extends XmlElementImpl implements XmlTag { myCachedDescriptor = null; myDescriptorModCount = -1; myAttributes = null; + myTextElements = null; myAttributeValueMap = null; myHasNamespaceDeclarations = false; myValue = null; @@ -159,9 +167,24 @@ public class XmlTagImpl extends XmlElementImpl implements XmlTag { super.clearCaches(); } - @Override + /** + * Use {@link #getReferences(PsiReferenceService.Hints)} instead of calling or overriding this method. + */ + @Deprecated @NotNull - public PsiReference[] getReferences() { + @Override + public final PsiReference[] getReferences() { + return getReferences(PsiReferenceService.Hints.NO_HINTS); + } + + @Override + public boolean shouldAskParentForReferences(@NotNull PsiReferenceService.Hints hints) { + return false; + } + + @NotNull + @Override + public PsiReference[] getReferences(@NotNull PsiReferenceService.Hints hints) { ProgressManager.checkCanceled(); final ASTNode startTagName = XmlChildRole.START_TAG_NAME_FINDER.findChild(this); if (startTagName == null) return PsiReference.EMPTY_ARRAY; @@ -169,14 +192,18 @@ public class XmlTagImpl extends XmlElementImpl implements XmlTag { List refs = new ArrayList(); String prefix = getNamespacePrefix(); - TagNameReference startTagRef = TagNameReference.createTagNameReference(this, startTagName, true); - if (startTagRef != null) { - refs.add(startTagRef); + boolean inStartTag = hints.offsetInElement == null || childContainsOffset(startTagName.getPsi(), hints.offsetInElement); + if (inStartTag) { + TagNameReference startTagRef = TagNameReference.createTagNameReference(this, startTagName, true); + if (startTagRef != null) { + refs.add(startTagRef); + } + if (!prefix.isEmpty()) { + refs.add(createPrefixReference(startTagName, prefix, startTagRef)); + } } - if (!prefix.isEmpty()) { - refs.add(createPrefixReference(startTagName, prefix, startTagRef)); - } - if (endTagName != null) { + boolean inEndTag = endTagName != null && (hints.offsetInElement == null || childContainsOffset(endTagName.getPsi(), hints.offsetInElement)); + if (inEndTag) { TagNameReference endTagRef = TagNameReference.createTagNameReference(this, endTagName, false); if (endTagRef != null) { refs.add(endTagRef); @@ -187,16 +214,43 @@ public class XmlTagImpl extends XmlElementImpl implements XmlTag { } } - - // ArrayList.addAll() makes a clone of the collection - //noinspection ManualArrayToCollectionCopy - for (PsiReference ref : ReferenceProvidersRegistry.getReferencesFromProviders(this, XmlTag.class)) { - refs.add(ref); + if (hints.offsetInElement == null || inStartTag || inEndTag || isInsideXmlText(hints.offsetInElement)) { + Collections.addAll(refs, ReferenceProvidersRegistry.getReferencesFromProviders(this, hints)); } return ContainerUtil.toArray(refs, new PsiReference[refs.size()]); } + private static boolean childContainsOffset(PsiElement child, int offsetInTag) { + return child.getStartOffsetInParent() <= offsetInTag && offsetInTag <= child.getStartOffsetInParent() + child.getTextLength(); + } + + private boolean isInsideXmlText(int offsetInTag) { + TextRange[] ranges = getValueTextRanges(); + if (ranges.length == 0) return false; + if (offsetInTag < ranges[0].getStartOffset() || offsetInTag > ranges[ranges.length - 1].getEndOffset()) return false; + + int i = Arrays.binarySearch(ranges, TextRange.from(offsetInTag, 0), RANGE_COMPARATOR); + return i >= 0 || ranges[-i - 2].containsOffset(offsetInTag); + } + + @NotNull + private TextRange[] getValueTextRanges() { + TextRange[] elements = myTextElements; + if (elements == null) { + List list = ContainerUtil.newSmartList(); + // don't use getValue().getXmlElements() because it processes includes & entities, and we only need textual AST here + for (ASTNode child = getFirstChildNode(); child != null; child = child.getTreeNext()) { + PsiElement psi = child.getPsi(); + if (psi instanceof XmlText) { + list.add(TextRange.from(psi.getStartOffsetInParent(), psi.getTextLength())); + } + } + myTextElements = elements = list.toArray(new TextRange[list.size()]); + } + return elements; + } + private SchemaPrefixReference createPrefixReference(ASTNode startTagName, String prefix, TagNameReference tagRef) { return new SchemaPrefixReference(this, TextRange.from(startTagName.getStartOffset() - getStartOffset(), prefix.length()), prefix, tagRef); @@ -428,7 +482,7 @@ public class XmlTagImpl extends XmlElementImpl implements XmlTag { @Override public PsiReference getReference() { - return ArrayUtil.getFirstElement(getReferences()); + return ArrayUtil.getFirstElement(getReferences(PsiReferenceService.Hints.NO_HINTS)); } @Override