From cca323568f4c9b014ae37309dadd60181a3798a9 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 2 Oct 2015 09:39:20 +0200 Subject: [PATCH 01/38] improve find usages performance in XML: don't query references from tags and attributes that won't be of any use For that, introduce HintedReferenceHost API and pass reference hints into psi.getReferences --- .../com/intellij/psi/HintedReferenceHost.java | 51 +++++++++++ .../intellij/psi/PsiReferenceServiceImpl.java | 5 +- .../psi/impl/SharedPsiElementImplUtil.java | 16 +++- .../psi/impl/source/xml/XmlAttributeImpl.java | 28 +++++- .../psi/impl/source/xml/XmlTagImpl.java | 86 +++++++++++++++---- 5 files changed, 162 insertions(+), 24 deletions(-) create mode 100644 platform/core-api/src/com/intellij/psi/HintedReferenceHost.java 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/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/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 From 5db1e4ef312cc0ced83ef7596c7614f1827a8439 Mon Sep 17 00:00:00 2001 From: Alexander Zolotov Date: Thu, 1 Oct 2015 20:57:21 +0300 Subject: [PATCH 02/38] Non-started template is always valid (EA-73852) --- .../com/intellij/codeInsight/template/impl/TemplateState.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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); } From c3fe8824baf11eae3a3824ac9ee60e5e0c4e6052 Mon Sep 17 00:00:00 2001 From: nik Date: Fri, 2 Oct 2015 11:42:02 +0300 Subject: [PATCH 03/38] temporary commented 'operator' modifier to keep compatibility with M13 --- .../platform-impl/src/com/intellij/openapi/util/extensions.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 From 81da8e42e3fbe19ce7c1c1992d9f8b3e8a5e04ac Mon Sep 17 00:00:00 2001 From: "liana.bakradze" Date: Fri, 2 Oct 2015 11:53:45 +0300 Subject: [PATCH 04/38] fixed after IDEA-CR-5228 --- .../python/module/PyContentEntriesEditor.java | 14 +++++++++----- .../python/module/PyRootTypeProvider.java | 9 ++++++--- 2 files changed, 15 insertions(+), 8 deletions(-) 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) { From 05f845eb38f7efcefdba9c3fd3f86500b2b8310e Mon Sep 17 00:00:00 2001 From: Denis Fokin Date: Fri, 2 Oct 2015 13:24:48 +0400 Subject: [PATCH 05/38] IDEA-141725 Main menus only work one time, then it just stops working (The jayatana agent exclusion is enabled) --- bin/scripts/unix/idea.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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." From 854abe80978f5e495d50548da118080dd657dca9 Mon Sep 17 00:00:00 2001 From: Alexander Kass Date: Wed, 30 Sep 2015 18:10:10 +0300 Subject: [PATCH 06/38] PsiBuilderImpl: fix remapping to whitespaces and comments --- .../com/intellij/lang/impl/PsiBuilderImpl.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) 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(); From 405fef8d699a13ed8f35ce630e11a85bfb3f8d62 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Fri, 2 Oct 2015 11:48:43 +0200 Subject: [PATCH 07/38] fix link to wiki, change title (renamed to "IntelliJ Platform SDK DevGuide") --- .../components/PersistentStateComponent.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) 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) From 197110ff95f1b3528778c0fd5567fd92c69c3b92 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Thu, 1 Oct 2015 21:18:00 +0200 Subject: [PATCH 08/38] overload resolution: provide type parameter bounds with site substitutor to correctly compare types during most specific check (IDEA-144463) --- .../StrictSubtypingConstraint.java | 2 +- .../JavaMethodsConflictResolver.java | 24 +++++++++++-------- ...rameterBoundsWhenCheckForMostSpecific.java | 21 ++++++++++++++++ .../daemon/lambda/OverloadResolutionTest.java | 4 ++++ 4 files changed, 40 insertions(+), 11 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/overloadResolution/SiteSubstituteTypeParameterBoundsWhenCheckForMostSpecific.java 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/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/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); } From 4bd01a1dcf5314255ec66d73fd47d23c16dca4f6 Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Fri, 2 Oct 2015 13:07:47 +0300 Subject: [PATCH 09/38] do not add slot variable names that declared after the current position --- .../debugger/jdi/LocalVariablesUtil.java | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) 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..40e8e52c9cf5 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,36 @@ 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()); + if (!myReached) { + appendName(variable.getName()); + myCurrentSlotIndex += getTypeSlotSize(variable.getType()); + } } public void visitSynchronizedStatement(PsiSynchronizedStatement statement) { From 6a440de2678f6393d71640115be12f5b87830d73 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Fri, 2 Oct 2015 12:09:12 +0200 Subject: [PATCH 10/38] cleanup --- .../intellij/unscramble/UnscrambleDialog.java | 36 +++++++++++-------- .../VcsCheckBoxWithSpinnerConfigurable.java | 4 ++- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/java/java-impl/src/com/intellij/unscramble/UnscrambleDialog.java b/java/java-impl/src/com/intellij/unscramble/UnscrambleDialog.java index 7cca43290ffe..5c1fad11cf37 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,22 +250,21 @@ 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); + PropertiesComponent propertyComponent = PropertiesComponent.getInstance(); + propertyComponent.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); + propertyComponent + .setValue(PROPERTY_UNSCRAMBLER_NAME_USED, selectedUnscrambler == null ? null : selectedUnscrambler.getPresentableName(), null); + propertyComponent.setValue(PROPERTY_LOG_FILE_LAST_URL, StringUtil.nullize(myLogFile.getText()), null); } super.dispose(); } @@ -289,6 +292,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 +370,7 @@ public class UnscrambleDialog extends DialogWrapper { return false; } + @Override protected void doOKAction() { if (myConfigurable != null && myConfigurable.isModified()) { try { @@ -382,6 +387,7 @@ public class UnscrambleDialog extends DialogWrapper { } } + @Override public void doHelpAction() { HelpManager.getInstance().invokeHelp("find.analyzeStackTrace"); } @@ -423,6 +429,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 +481,4 @@ public class UnscrambleDialog extends DialogWrapper { } return builder.toString(); } - } 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); From facca4754bd5d8298e9d34af6f4ad91350f814ce Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Fri, 2 Oct 2015 12:17:39 +0200 Subject: [PATCH 11/38] restore fix "IDEA-125302 The Analyze Stacktrace menu option remembers only one log file across multiple projects" --- .../com/intellij/unscramble/UnscrambleDialog.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/java/java-impl/src/com/intellij/unscramble/UnscrambleDialog.java b/java/java-impl/src/com/intellij/unscramble/UnscrambleDialog.java index 5c1fad11cf37..152d084258ae 100644 --- a/java/java-impl/src/com/intellij/unscramble/UnscrambleDialog.java +++ b/java/java-impl/src/com/intellij/unscramble/UnscrambleDialog.java @@ -259,16 +259,20 @@ public class UnscrambleDialog extends DialogWrapper { public void dispose() { if (isOK()){ final List list = myLogFile.getHistory(); - PropertiesComponent propertyComponent = PropertiesComponent.getInstance(); - propertyComponent.setValue(PROPERTY_LOG_FILE_HISTORY_URLS, list.isEmpty() ? null : StringUtil.join(list, ":::"), null); + PropertiesComponent.getInstance().setValue(PROPERTY_LOG_FILE_HISTORY_URLS, list.isEmpty() ? null : StringUtil.join(list, ":::"), null); UnscrambleSupport selectedUnscrambler = getSelectedUnscrambler(); - propertyComponent - .setValue(PROPERTY_UNSCRAMBLER_NAME_USED, selectedUnscrambler == null ? null : selectedUnscrambler.getPresentableName(), null); - propertyComponent.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); From c1491d8ba5ffc56e6d3da6f70ae34fa0c35fb6b7 Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Fri, 2 Oct 2015 13:58:30 +0300 Subject: [PATCH 12/38] do not add slot variable names that declared after the current position --- .../impl/src/com/intellij/debugger/jdi/LocalVariablesUtil.java | 1 + 1 file changed, 1 insertion(+) 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 40e8e52c9cf5..b204903ca751 100644 --- a/java/debugger/impl/src/com/intellij/debugger/jdi/LocalVariablesUtil.java +++ b/java/debugger/impl/src/com/intellij/debugger/jdi/LocalVariablesUtil.java @@ -335,6 +335,7 @@ public class LocalVariablesUtil { @Override public void visitLocalVariable(PsiLocalVariable variable) { + super.visitLocalVariable(variable); if (!myReached) { appendName(variable.getName()); myCurrentSlotIndex += getTypeSlotSize(variable.getType()); From f3d8c17ae1875a7058b33c10486a2f82bd34e0f7 Mon Sep 17 00:00:00 2001 From: Alexander Zolotov Date: Fri, 2 Oct 2015 13:56:23 +0300 Subject: [PATCH 13/38] Validate template variable name while renaming (EA-73914) --- .../template/impl/EditVariableDialog.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) 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); From ef28417ae1cb31b85cf643788eb01c98d915746c Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Fri, 2 Oct 2015 14:26:00 +0300 Subject: [PATCH 14/38] error deleting directory --- .../test-framework-java8/path.kt | 20 +++++-------------- 1 file changed, 5 insertions(+), 15 deletions(-) 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) From 06a5774528f3fce6550cac950fac89d1a51c53db Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Fri, 2 Oct 2015 14:31:11 +0300 Subject: [PATCH 15/38] removed unused --- .../impl/TransferToPooledThreadQueue.java | 48 ------------------- 1 file changed, 48 deletions(-) delete mode 100644 platform/platform-impl/src/com/intellij/openapi/application/impl/TransferToPooledThreadQueue.java diff --git a/platform/platform-impl/src/com/intellij/openapi/application/impl/TransferToPooledThreadQueue.java b/platform/platform-impl/src/com/intellij/openapi/application/impl/TransferToPooledThreadQueue.java deleted file mode 100644 index d07aeb2b6e6e..000000000000 --- a/platform/platform-impl/src/com/intellij/openapi/application/impl/TransferToPooledThreadQueue.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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.application.impl; - -import com.intellij.openapi.util.Condition; -import com.intellij.util.Processor; -import com.intellij.util.concurrency.BoundedTaskExecutor; -import com.intellij.util.containers.TransferToEDTQueue; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.ide.PooledThreadExecutor; - -import java.util.concurrent.Executor; - -/** - * Passes elements for processing in the dedicated thread. - * Processes elements in batches, no longer than {maxUnitOfWorkThresholdMs}ms per batch, and reschedules processing later for longer batches. - * Usage: {@link #offer(Object)}} : schedules element for processing in a pooled thread - */ -public class TransferToPooledThreadQueue extends TransferToEDTQueue { - private final Executor myExecutor; - - public TransferToPooledThreadQueue(@NonNls @NotNull String name, - @NotNull Condition shutUpCondition, - int maxUnitOfWorkThresholdMs, - @NotNull Processor processor) { - super(name, processor, shutUpCondition, maxUnitOfWorkThresholdMs); - myExecutor = new BoundedTaskExecutor(PooledThreadExecutor.INSTANCE, 1); - } - - @Override - protected void schedule(@NotNull Runnable updateRunnable) { - myExecutor.execute(updateRunnable); - } -} From 3e9361a4ded7e93015d62b97f72363e209175d6e Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Fri, 2 Oct 2015 14:34:41 +0300 Subject: [PATCH 16/38] fewer scheduled alarms --- .../daemon/impl/DaemonCodeAnalyzerImpl.java | 21 +++++++++++++--- .../util/io/storage/HeavyProcessLatch.java | 24 +++++++++++-------- 2 files changed, 32 insertions(+), 13 deletions(-) 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/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) { From c0f0c6b9e88dbccfc515137806f7afe50c5a6e13 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Fri, 2 Oct 2015 14:39:35 +0300 Subject: [PATCH 17/38] warning about too wrapped indicators --- .../openapi/progress/util/ProgressWrapper.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) 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(); From 3f797908d31753f641ff6fab7a4de2b7639b9d2f Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Fri, 2 Oct 2015 14:40:28 +0300 Subject: [PATCH 18/38] dispose properly --- .../src/com/intellij/internal/DebugAttachDetector.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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(); } } From 382d4f6faf86096ff2e9a17f9a065a8b737cca0a Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Fri, 2 Oct 2015 14:50:31 +0300 Subject: [PATCH 19/38] Revert: removed unused (06a5774528f3fce6550cac950fac89d1a51c53db) --- .../impl/TransferToPooledThreadQueue.java | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 platform/platform-impl/src/com/intellij/openapi/application/impl/TransferToPooledThreadQueue.java diff --git a/platform/platform-impl/src/com/intellij/openapi/application/impl/TransferToPooledThreadQueue.java b/platform/platform-impl/src/com/intellij/openapi/application/impl/TransferToPooledThreadQueue.java new file mode 100644 index 000000000000..d07aeb2b6e6e --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/application/impl/TransferToPooledThreadQueue.java @@ -0,0 +1,48 @@ +/* + * 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.application.impl; + +import com.intellij.openapi.util.Condition; +import com.intellij.util.Processor; +import com.intellij.util.concurrency.BoundedTaskExecutor; +import com.intellij.util.containers.TransferToEDTQueue; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.ide.PooledThreadExecutor; + +import java.util.concurrent.Executor; + +/** + * Passes elements for processing in the dedicated thread. + * Processes elements in batches, no longer than {maxUnitOfWorkThresholdMs}ms per batch, and reschedules processing later for longer batches. + * Usage: {@link #offer(Object)}} : schedules element for processing in a pooled thread + */ +public class TransferToPooledThreadQueue extends TransferToEDTQueue { + private final Executor myExecutor; + + public TransferToPooledThreadQueue(@NonNls @NotNull String name, + @NotNull Condition shutUpCondition, + int maxUnitOfWorkThresholdMs, + @NotNull Processor processor) { + super(name, processor, shutUpCondition, maxUnitOfWorkThresholdMs); + myExecutor = new BoundedTaskExecutor(PooledThreadExecutor.INSTANCE, 1); + } + + @Override + protected void schedule(@NotNull Runnable updateRunnable) { + myExecutor.execute(updateRunnable); + } +} From 813741f79a9903f72e41792f932f3499c7cfd981 Mon Sep 17 00:00:00 2001 From: nik Date: Fri, 2 Oct 2015 15:07:54 +0300 Subject: [PATCH 20/38] jre selector: NPE fixed --- .../impl/src/com/intellij/execution/ui/JrePathEditor.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 From b7b3d67ccae4ab118e29e3b4f3d0db4ac2a3272f Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Fri, 2 Oct 2015 10:47:26 +0200 Subject: [PATCH 21/38] fix isBundled if output dir not under idea home --- .../com/intellij/ide/plugins/IdeaPluginDescriptorImpl.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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()); From 71c6c775788d12eab77ef079c82f66982a5fc04e Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Fri, 2 Oct 2015 15:23:14 +0300 Subject: [PATCH 22/38] guava type migration: type migration usages sorting algorithm changed --- .../typeMigration/TypeEvaluator.java | 1 + .../typeMigration/TypeMigrationLabeler.java | 120 +++++++++++++++--- .../typeMigration/ui/MigrationNode.java | 4 +- .../usageInfo/TypeMigrationUsageInfo.java | 14 ++ .../inspections/GuavaInspectionTest.java | 10 +- .../typeMigration/t100/after/test.java | 2 +- .../typeMigration/t33/after/test.java | 2 +- .../typeMigration/t54/after/test.java | 2 +- .../typeMigration/t99/after/test.java | 2 +- 9 files changed, 125 insertions(+), 32 deletions(-) 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..a157daa9946a 100644 --- a/java/java-impl/src/com/intellij/refactoring/typeMigration/TypeMigrationLabeler.java +++ b/java/java-impl/src/com/intellij/refactoring/typeMigration/TypeMigrationLabeler.java @@ -36,13 +36,19 @@ 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.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.Nullable; import org.jetbrains.annotations.TestOnly; import javax.swing.*; import java.util.*; +import java.util.HashMap; +import java.util.HashSet; /** * @author db @@ -132,15 +138,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 +156,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,9 +176,75 @@ public class TypeMigrationLabeler { for (final TypeMigrationUsageInfo expr : myNewExpressionTypeChange.keySet()) { usages[j++] = expr; } - return usages; + return sortMigratedUsages(usages); } + 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) { final PsiElement element = usageInfo.getElement(); if (element == null) return; @@ -310,7 +380,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 +394,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 +458,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 +681,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 +803,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 +836,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/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/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/java/typeMigration/testData/refactoring/typeMigration/t100/after/test.java b/java/typeMigration/testData/refactoring/typeMigration/t100/after/test.java index b2917c86149e..5204fd518622 100644 --- a/java/typeMigration/testData/refactoring/typeMigration/t100/after/test.java +++ b/java/typeMigration/testData/refactoring/typeMigration/t100/after/test.java @@ -1,6 +1,6 @@ import java.util.*; public class Test { - Map f = new HashMap<>(); + Map f = new HashMap(); } diff --git a/java/typeMigration/testData/refactoring/typeMigration/t33/after/test.java b/java/typeMigration/testData/refactoring/typeMigration/t33/after/test.java index 126292ad9322..34eed3867745 100644 --- a/java/typeMigration/testData/refactoring/typeMigration/t33/after/test.java +++ b/java/typeMigration/testData/refactoring/typeMigration/t33/after/test.java @@ -2,6 +2,6 @@ import java.util.*; class Test { List l; void foo() { - l = new ArrayList<>(); + l = new ArrayList(); } } \ No newline at end of file diff --git a/java/typeMigration/testData/refactoring/typeMigration/t54/after/test.java b/java/typeMigration/testData/refactoring/typeMigration/t54/after/test.java index 11cbc3fe7520..b6edbd7a5eb2 100644 --- a/java/typeMigration/testData/refactoring/typeMigration/t54/after/test.java +++ b/java/typeMigration/testData/refactoring/typeMigration/t54/after/test.java @@ -1,4 +1,4 @@ import java.util.*; class Test { - Set f = new Set<>(); + Set f = new Set(); } \ No newline at end of file diff --git a/java/typeMigration/testData/refactoring/typeMigration/t99/after/test.java b/java/typeMigration/testData/refactoring/typeMigration/t99/after/test.java index 1f7b49a490d9..dea674d39645 100644 --- a/java/typeMigration/testData/refactoring/typeMigration/t99/after/test.java +++ b/java/typeMigration/testData/refactoring/typeMigration/t99/after/test.java @@ -1,6 +1,6 @@ import java.util.*; public class Test { - Set> f = new Set<>(); + Set> f = new Set>(); } From 0d1adb345b73ed0bd18ff7ef02c7879eb37c2967 Mon Sep 17 00:00:00 2001 From: Rustam Vishnyakov Date: Fri, 2 Oct 2015 15:13:00 +0300 Subject: [PATCH 23/38] Scheme exporter API and IDEA-90075 (Export Codestyle as XML File) --- .../codeStyle/CodeStyleSchemeExporterUI.java | 119 ++++++++++++++++++ .../ManageCodeStyleSchemesDialog.form | 2 +- .../ManageCodeStyleSchemesDialog.java | 26 +++- .../CodeStyleSchemeCopyExporter.java | 82 ++++++++++++ .../openapi/options/SchemeExporter.java | 41 ++++++ .../options/SchemeExporterException.java | 25 ++++ .../openapi/options/SchemeExporterEP.java | 90 +++++++++++++ .../src/messages/ApplicationBundle.properties | 5 + .../src/META-INF/LangExtensions.xml | 5 + .../src/META-INF/PlatformExtensionPoints.xml | 5 + 10 files changed, 395 insertions(+), 5 deletions(-) create mode 100644 platform/lang-impl/src/com/intellij/application/options/codeStyle/CodeStyleSchemeExporterUI.java create mode 100644 platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleSchemeCopyExporter.java create mode 100644 platform/platform-api/src/com/intellij/openapi/options/SchemeExporter.java create mode 100644 platform/platform-api/src/com/intellij/openapi/options/SchemeExporterException.java create mode 100644 platform/platform-impl/src/com/intellij/openapi/options/SchemeExporterEP.java 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/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-impl/src/com/intellij/openapi/options/SchemeExporterEP.java b/platform/platform-impl/src/com/intellij/openapi/options/SchemeExporterEP.java new file mode 100644 index 000000000000..b41738d8d7ea --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/options/SchemeExporterEP.java @@ -0,0 +1,90 @@ +/* + * 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.extensions.AbstractExtensionPointBean; +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.openapi.util.LazyInstance; +import com.intellij.util.xmlb.annotations.Attribute; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * Extension point for schemes exporters. + * + * @author Rustam Vishnyakov + */ +public class SchemeExporterEP 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-resources-en/src/messages/ApplicationBundle.properties b/platform/platform-resources-en/src/messages/ApplicationBundle.properties index 663253c49fdc..f2d869de7904 100644 --- a/platform/platform-resources-en/src/messages/ApplicationBundle.properties +++ b/platform/platform-resources-en/src/messages/ApplicationBundle.properties @@ -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 @@ + + + + + From a3d6c1d09340e0793a2355adabec33c15304ad5c Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Fri, 2 Oct 2015 14:29:19 +0200 Subject: [PATCH 24/38] [project] 64-bit file watcher for Windows (IDEA-133292) --- bin/win/fsnotifier64.exe | Bin 0 -> 113768 bytes native/fileWatcher/fileWatcher3.cpp | 2 +- native/fileWatcher/fileWatcher3.sln | 6 ++ native/fileWatcher/fileWatcher3.vcxproj | 75 ++++++++++++++++++ .../vfs/impl/local/NativeFileWatcherImpl.java | 10 +-- 5 files changed, 87 insertions(+), 6 deletions(-) create mode 100644 bin/win/fsnotifier64.exe diff --git a/bin/win/fsnotifier64.exe b/bin/win/fsnotifier64.exe new file mode 100644 index 0000000000000000000000000000000000000000..5324b9a3715c34467dcfae1f0175bb34e7d1981d GIT binary patch literal 113768 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 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/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; } From feaf208a2cba57f29d0c4fa12f8b693e62313f83 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 2 Oct 2015 13:14:09 +0200 Subject: [PATCH 25/38] when considering changelists to delete, use always the latest version of ChangeListManager's mutable state (IDEA-145846) --- .../vcs/changes/ChangeListManagerImpl.java | 5 ++-- .../actions/ChangeListRemoveConfirmation.kt | 28 ++++++++++--------- 2 files changed, 18 insertions(+), 15 deletions(-) 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) } } } From 0eb643a725b9e4bb8d324dfa4c9c3d4d9fc9b5cf Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 2 Oct 2015 13:41:01 +0200 Subject: [PATCH 26/38] IDEA-145768 MagicConstant completion in incomplete code --- .../MagicCompletionContributor.java | 18 ++++++-------- .../MagicConstantCompletionTest.groovy | 24 ++++++++++++++++++- 2 files changed, 30 insertions(+), 12 deletions(-) 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-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() From 1154b6f0965227365dcf6c5b4a28a21af019593b Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 2 Oct 2015 14:22:03 +0200 Subject: [PATCH 27/38] IDEA-145752 Annotation attribute value: irrelevant completion proposals for boolean attribute (basic completion) --- .../completion/JavaCompletionContributor.java | 1 + .../completion/JavaKeywordCompletion.java | 17 +++++++++++++++-- .../JavaSmartCompletionContributor.java | 5 ++--- ...oPrimitivesInBooleanAnnotationAttribute.java | 5 +++++ .../noPrimitivesInEnumAnnotationAttribute.java | 6 ++++++ ...PrimitivesInIntAnnotationValueAttribute.java | 5 +++++ .../primitivesInClassAnnotationAttribute.java | 5 +++++ ...imitivesInClassAnnotationValueAttribute.java | 5 +++++ .../completion/KeywordCompletionTest.java | 8 +++++++- 9 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/completion/keywords/noPrimitivesInBooleanAnnotationAttribute.java create mode 100644 java/java-tests/testData/codeInsight/completion/keywords/noPrimitivesInEnumAnnotationAttribute.java create mode 100644 java/java-tests/testData/codeInsight/completion/keywords/noPrimitivesInIntAnnotationValueAttribute.java create mode 100644 java/java-tests/testData/codeInsight/completion/keywords/primitivesInClassAnnotationAttribute.java create mode 100644 java/java-tests/testData/codeInsight/completion/keywords/primitivesInClassAnnotationValueAttribute.java 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-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/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"); } From fdcb182e8a04f41aacea9caafb3b444afb8ba45f Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Fri, 2 Oct 2015 15:53:19 +0300 Subject: [PATCH 28/38] testData reverted --- .../testData/refactoring/typeMigration/t100/after/test.java | 2 +- .../testData/refactoring/typeMigration/t33/after/test.java | 2 +- .../testData/refactoring/typeMigration/t54/after/test.java | 2 +- .../testData/refactoring/typeMigration/t99/after/test.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/java/typeMigration/testData/refactoring/typeMigration/t100/after/test.java b/java/typeMigration/testData/refactoring/typeMigration/t100/after/test.java index 5204fd518622..b2917c86149e 100644 --- a/java/typeMigration/testData/refactoring/typeMigration/t100/after/test.java +++ b/java/typeMigration/testData/refactoring/typeMigration/t100/after/test.java @@ -1,6 +1,6 @@ import java.util.*; public class Test { - Map f = new HashMap(); + Map f = new HashMap<>(); } diff --git a/java/typeMigration/testData/refactoring/typeMigration/t33/after/test.java b/java/typeMigration/testData/refactoring/typeMigration/t33/after/test.java index 34eed3867745..126292ad9322 100644 --- a/java/typeMigration/testData/refactoring/typeMigration/t33/after/test.java +++ b/java/typeMigration/testData/refactoring/typeMigration/t33/after/test.java @@ -2,6 +2,6 @@ import java.util.*; class Test { List l; void foo() { - l = new ArrayList(); + l = new ArrayList<>(); } } \ No newline at end of file diff --git a/java/typeMigration/testData/refactoring/typeMigration/t54/after/test.java b/java/typeMigration/testData/refactoring/typeMigration/t54/after/test.java index b6edbd7a5eb2..11cbc3fe7520 100644 --- a/java/typeMigration/testData/refactoring/typeMigration/t54/after/test.java +++ b/java/typeMigration/testData/refactoring/typeMigration/t54/after/test.java @@ -1,4 +1,4 @@ import java.util.*; class Test { - Set f = new Set(); + Set f = new Set<>(); } \ No newline at end of file diff --git a/java/typeMigration/testData/refactoring/typeMigration/t99/after/test.java b/java/typeMigration/testData/refactoring/typeMigration/t99/after/test.java index dea674d39645..1f7b49a490d9 100644 --- a/java/typeMigration/testData/refactoring/typeMigration/t99/after/test.java +++ b/java/typeMigration/testData/refactoring/typeMigration/t99/after/test.java @@ -1,6 +1,6 @@ import java.util.*; public class Test { - Set> f = new Set>(); + Set> f = new Set<>(); } From 15955db1b2000f30d84e5144a21e9ff65622c1d6 Mon Sep 17 00:00:00 2001 From: Denis Fokin Date: Fri, 2 Oct 2015 15:54:02 +0300 Subject: [PATCH 29/38] 1. Propagate antialiasing settings to more Swing components 2. Use default aa contrast value or system value for non-mac platforms --- .../com/intellij/ide/ui/AntialiasingType.java | 23 +++++++++++-------- .../src/com/intellij/ide/ui/UISettings.java | 4 +++- .../lookup/impl/LookupCellRenderer.java | 2 +- .../ExpandedItemListCellRendererWrapper.java | 3 +++ .../intellij/ui/SimpleColoredComponent.java | 2 +- .../JBComboBoxTableCellEditorComponent.java | 3 +++ .../com/intellij/ui/tabs/impl/TabLabel.java | 5 ---- .../ide/ui/AppearanceConfigurable.java | 11 +++++++-- .../ui/impl/DialogWrapperPeerImpl.java | 7 +++++- .../util/src/com/intellij/util/ui/UIUtil.java | 13 ++++++++++- 10 files changed, 51 insertions(+), 22 deletions(-) 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/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/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/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/util/src/com/intellij/util/ui/UIUtil.java b/platform/util/src/com/intellij/util/ui/UIUtil.java index daed73559738..92776e23b813 100644 --- a/platform/util/src/com/intellij/util/ui/UIUtil.java +++ b/platform/util/src/com/intellij/util/ui/UIUtil.java @@ -3077,13 +3077,24 @@ 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 { + lcdContrastValue = ((Integer)map.get(RenderingHints.KEY_TEXT_LCD_CONTRAST)); + } + } } if (lcdContrastValue < 100 || lcdContrastValue > 250) { // the default value lcdContrastValue = 140; } + return lcdContrastValue; } From 2f73226bfeff95cb710a4dc5ccbd656533abe5b5 Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Fri, 2 Oct 2015 16:24:00 +0300 Subject: [PATCH 30/38] type migration tries to insert diamond for new expressions at the end of changing --- .../typeMigration/TypeMigrationLabeler.java | 15 +++++++++++-- .../typeMigration/TypeMigrationProcessor.java | 16 +++++++++++++- .../TypeMigrationReplacementUtil.java | 22 ++++++++++++++----- 3 files changed, 44 insertions(+), 9 deletions(-) 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 a157daa9946a..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,12 +37,14 @@ 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; @@ -245,7 +248,7 @@ public class TypeMigrationLabeler { } - public void change(final TypeMigrationUsageInfo usageInfo) { + public void change(final TypeMigrationUsageInfo usageInfo, @NotNull Consumer consumer) { final PsiElement element = usageInfo.getElement(); if (element == null) return; final Project project = element.getProject(); @@ -255,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); + } } } } @@ -282,6 +289,10 @@ public class TypeMigrationLabeler { } } + void postProcessNewExpression(@NotNull PsiNewExpression expression) { + TypeMigrationReplacementUtil.tryToReplaceWithDiamond(expression, null); + } + @Nullable Object getConversion(PsiElement element) { return myConversions.get(element); 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) { From 8736db12bc1153f53f630f80b6cf52fcb73ee98f Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Fri, 2 Oct 2015 16:29:03 +0300 Subject: [PATCH 31/38] Replace empty lines with

instead of

(IDEA-145373), however preserve existing

. According to documentation "If you have more than one paragraph in the doc comment, separate the paragraphs with a

paragraph tag, as shown." and it has not changed since 2010, also javadoc tool from jdk8 fails with error "error: self-closing element not allowed", that is why we now generate

--- .../codeStyle/javadoc/CommentFormatter.java | 2 +- .../source/codeStyle/javadoc/JDParser.java | 23 ++++--- .../formatter/java/JavadocFormatterTest.java | 60 +++++++++++-------- .../src/messages/ApplicationBundle.properties | 2 +- 4 files changed, 48 insertions(+), 39 deletions(-) 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-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/platform/platform-resources-en/src/messages/ApplicationBundle.properties b/platform/platform-resources-en/src/messages/ApplicationBundle.properties index f2d869de7904..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 From a602a66bc0abebd70124373c04255101405b0ea6 Mon Sep 17 00:00:00 2001 From: Denis Fokin Date: Fri, 2 Oct 2015 16:55:27 +0300 Subject: [PATCH 32/38] NPE fix --- platform/util/src/com/intellij/util/ui/UIUtil.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/platform/util/src/com/intellij/util/ui/UIUtil.java b/platform/util/src/com/intellij/util/ui/UIUtil.java index 92776e23b813..b5bca86ac6e9 100644 --- a/platform/util/src/com/intellij/util/ui/UIUtil.java +++ b/platform/util/src/com/intellij/util/ui/UIUtil.java @@ -3085,7 +3085,8 @@ public class UIUtil { if (map == null) { lcdContrastValue = 140; } else { - lcdContrastValue = ((Integer)map.get(RenderingHints.KEY_TEXT_LCD_CONTRAST)); + Object o = map.get(RenderingHints.KEY_TEXT_LCD_CONTRAST); + lcdContrastValue = (o == null) ? 140 : ((Integer)o); } } } From 357ecea4282edcd2230c6e8685f7d133ea4fa355 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Thu, 1 Oct 2015 10:55:38 +0200 Subject: [PATCH 33/38] IG: cleanup --- .../migration/TryFinallyCanBeTryWithResourcesInspection.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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; } } From 0dff08b0c3c5e30db8eb075cd6c10c0c24bf4774 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Fri, 2 Oct 2015 08:41:12 +0200 Subject: [PATCH 34/38] SSR: cleanup --- .../LiteralWithSubstitutionHandler.java | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) 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; } } From eb4b2613f76380c222069ed90fd7fb28bc0bf3eb Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Fri, 2 Oct 2015 11:52:56 +0200 Subject: [PATCH 35/38] SSR: cleanup java type matching --- .../impl/matcher/JavaMatchingVisitor.java | 108 +++++++++--------- 1 file changed, 54 insertions(+), 54 deletions(-) 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..7f6a55d6f2b0 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 @@ -717,93 +717,93 @@ public class JavaMatchingVisitor extends JavaElementVisitor { } } - private boolean matchType(final PsiElement _type, final PsiElement _type2) { - PsiElement el = _type; - PsiElement el2 = _type2; + private boolean matchType(final PsiElement patternType, final PsiElement matchedType) { + PsiElement patternElement = patternType; + PsiElement matchedElement = matchedType; PsiType type1 = null; PsiType type2 = null; // check for generics - if (_type instanceof PsiTypeElement && - ((PsiTypeElement)_type).getInnermostComponentReferenceElement() != null + if (patternType instanceof PsiTypeElement && + ((PsiTypeElement)patternType).getInnermostComponentReferenceElement() != null ) { - el = ((PsiTypeElement)_type).getInnermostComponentReferenceElement(); - type1 = ((PsiTypeElement)_type).getType(); + patternElement = ((PsiTypeElement)patternType).getInnermostComponentReferenceElement(); + type1 = ((PsiTypeElement)patternType).getType(); } - if (_type2 instanceof PsiTypeElement && - ((PsiTypeElement)_type2).getInnermostComponentReferenceElement() != null + if (matchedType instanceof PsiTypeElement && + ((PsiTypeElement)matchedType).getInnermostComponentReferenceElement() != null ) { - el2 = ((PsiTypeElement)_type2).getInnermostComponentReferenceElement(); - type2 = ((PsiTypeElement)_type2).getType(); + matchedElement = ((PsiTypeElement)matchedType).getInnermostComponentReferenceElement(); + type2 = ((PsiTypeElement)matchedType).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 (patternElement instanceof PsiJavaCodeReferenceElement) { + list = ((PsiJavaCodeReferenceElement)patternElement).getParameterList(); + patternElement = ((PsiJavaCodeReferenceElement)patternElement).getReferenceNameElement(); } if (list != null && list.getTypeParameterElements().length > 0) { - boolean result = typeparams != null && myMatchingVisitor.matchSequentially(list.getTypeParameterElements(), typeparams); + boolean result = typeParameters != null && myMatchingVisitor.matchSequentially(list.getTypeParameterElements(), typeParameters); if (!result) return false; } else { - if (_type2 instanceof PsiTypeElement) { - type2 = ((PsiTypeElement)_type2).getType(); + if (matchedType instanceof PsiTypeElement) { + type2 = ((PsiTypeElement)matchedType).getType(); - if (typeparams == null || typeparams.length == 0) { + if (typeParameters == null || typeParameters.length == 0) { final PsiJavaCodeReferenceElement innermostComponentReferenceElement = - ((PsiTypeElement)_type2).getInnermostComponentReferenceElement(); - if (innermostComponentReferenceElement != null) el2 = innermostComponentReferenceElement; + ((PsiTypeElement)matchedType).getInnermostComponentReferenceElement(); + if (innermostComponentReferenceElement != null) matchedElement = innermostComponentReferenceElement; } else { - el2 = _type2; + matchedElement = matchedType; } } } - final int array2Dims = (type2 != null ? type2.getArrayDimensions() : 0) + countCStyleArrayDeclarationDims(_type2); - final int arrayDims = (type1 != null ? type1.getArrayDimensions() : 0) + countCStyleArrayDeclarationDims(_type); + final int patternArrayDimensions = (type1 != null ? type1.getArrayDimensions() : 0) + countCStyleArrayDeclarationDims(patternType); + final int matchedArrayDimensions = (type2 != null ? type2.getArrayDimensions() : 0) + countCStyleArrayDeclarationDims(matchedType); - if (myMatchingVisitor.getMatchContext().getPattern().isTypedVar(el)) { - final SubstitutionHandler handler = (SubstitutionHandler)myMatchingVisitor.getMatchContext().getPattern().getHandler(el); + if (myMatchingVisitor.getMatchContext().getPattern().isTypedVar(patternElement)) { + final SubstitutionHandler handler = (SubstitutionHandler)myMatchingVisitor.getMatchContext().getPattern().getHandler(patternElement); RegExpPredicate regExpPredicate = null; - if (arrayDims != 0) { - if (arrayDims != array2Dims) { + if (patternArrayDimensions != 0) { + if (patternArrayDimensions != matchedArrayDimensions) { return false; } } - else if (array2Dims != 0) { + 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 +812,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 +823,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(); From 247b885ce9d9d42f6956be704170fb2862ee356d Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Fri, 2 Oct 2015 12:07:55 +0200 Subject: [PATCH 36/38] SRR: improve java array declaration matching --- .../impl/matcher/JavaMatchingVisitor.java | 111 ++++++++---------- .../StructuralSearchTest.java | 40 ++++++- 2 files changed, 82 insertions(+), 69 deletions(-) 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 7f6a55d6f2b0..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()) { @@ -718,25 +734,8 @@ public class JavaMatchingVisitor extends JavaElementVisitor { } private boolean matchType(final PsiElement patternType, final PsiElement matchedType) { - PsiElement patternElement = patternType; - PsiElement matchedElement = matchedType; - PsiType type1 = null; - PsiType type2 = null; - - // check for generics - if (patternType instanceof PsiTypeElement && - ((PsiTypeElement)patternType).getInnermostComponentReferenceElement() != null - ) { - patternElement = ((PsiTypeElement)patternType).getInnermostComponentReferenceElement(); - type1 = ((PsiTypeElement)patternType).getType(); - } - - if (matchedType instanceof PsiTypeElement && - ((PsiTypeElement)matchedType).getInnermostComponentReferenceElement() != null - ) { - matchedElement = ((PsiTypeElement)matchedType).getInnermostComponentReferenceElement(); - type2 = ((PsiTypeElement)matchedType).getType(); - } + PsiElement patternElement = getInnermostComponent(patternType); + PsiElement matchedElement = getInnermostComponent(matchedType); PsiElement[] typeParameters = null; if (matchedElement instanceof PsiJavaCodeReferenceElement) { @@ -757,34 +756,20 @@ public class JavaMatchingVisitor extends JavaElementVisitor { matchedElement = ((PsiMethod)matchedType).getNameIdentifier(); } - PsiReferenceParameterList list = null; if (patternElement instanceof PsiJavaCodeReferenceElement) { - list = ((PsiJavaCodeReferenceElement)patternElement).getParameterList(); - patternElement = ((PsiJavaCodeReferenceElement)patternElement).getReferenceNameElement(); - } - - if (list != null && list.getTypeParameterElements().length > 0) { - boolean result = typeParameters != null && myMatchingVisitor.matchSequentially(list.getTypeParameterElements(), typeParameters); - - if (!result) return false; - } - else { - if (matchedType instanceof PsiTypeElement) { - type2 = ((PsiTypeElement)matchedType).getType(); - - if (typeParameters == null || typeParameters.length == 0) { - final PsiJavaCodeReferenceElement innermostComponentReferenceElement = - ((PsiTypeElement)matchedType).getInnermostComponentReferenceElement(); - if (innermostComponentReferenceElement != null) matchedElement = innermostComponentReferenceElement; - } - else { - matchedElement = matchedType; + 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; } } + patternElement = referenceElement.getReferenceNameElement(); } - final int patternArrayDimensions = (type1 != null ? type1.getArrayDimensions() : 0) + countCStyleArrayDeclarationDims(patternType); - final int matchedArrayDimensions = (type2 != null ? type2.getArrayDimensions() : 0) + countCStyleArrayDeclarationDims(matchedType); + 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); 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) From b1f772eb3187bef89c74bfb04d3c44515f1bfc05 Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Fri, 2 Oct 2015 17:34:25 +0300 Subject: [PATCH 37/38] embedded live templates: HTML templates --- .../src/fileTemplates/internal/HTML4 File.html.ft | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From df3694f0255049a9c6ada940d3f0a1b0fb86de2f Mon Sep 17 00:00:00 2001 From: Dmitry Batrak Date: Fri, 2 Oct 2015 17:42:24 +0300 Subject: [PATCH 38/38] editor new rendering: speed up opening and editing for documents with long lines --- .../editor/impl/EditorDocumentPriorities.java | 3 +- .../openapi/editor/impl/EditorImpl.java | 6 +- .../impl/view/ApproximationFragment.java | 88 ++++ .../impl/view/EditorCoordinateMapper.java | 64 +-- .../editor/impl/view/EditorPainter.java | 13 +- .../editor/impl/view/EditorSizeManager.java | 80 ++-- .../openapi/editor/impl/view/EditorView.java | 37 +- .../editor/impl/view/LineFragment.java | 4 - .../openapi/editor/impl/view/LineLayout.java | 442 +++++++----------- .../impl/view/LogicalPositionCache.java | 205 ++++++++ .../openapi/editor/impl/view/TabFragment.java | 10 - .../editor/impl/view/TextFragment.java | 20 - .../editor/impl/view/TextLayoutCache.java | 57 +-- .../view/VisualLineFragmentsIterator.java | 14 +- 14 files changed, 575 insertions(+), 468 deletions(-) create mode 100644 platform/platform-impl/src/com/intellij/openapi/editor/impl/view/ApproximationFragment.java create mode 100644 platform/platform-impl/src/com/intellij/openapi/editor/impl/view/LogicalPositionCache.java 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/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