From 18520ff38054261b712c5edcffd7d5de8a6dfe95 Mon Sep 17 00:00:00 2001 From: Anton Tarasov Date: Tue, 1 Nov 2016 19:59:02 +0300 Subject: [PATCH 01/15] Add private CachedImageIcon.asDisabledIcon() to have it auto-JBUI-scalable --- .../com/intellij/openapi/util/IconLoader.java | 62 ++++++++++++------- .../src/com/intellij/util/ImageLoader.java | 26 ++++++-- 2 files changed, 63 insertions(+), 25 deletions(-) diff --git a/platform/util/src/com/intellij/openapi/util/IconLoader.java b/platform/util/src/com/intellij/openapi/util/IconLoader.java index ae209f6553b2..900f336b9206 100644 --- a/platform/util/src/com/intellij/openapi/util/IconLoader.java +++ b/platform/util/src/com/intellij/openapi/util/IconLoader.java @@ -298,22 +298,26 @@ public final class IconLoader { LOG.error(icon); // # 22481 return EMPTY_ICON; } - final int scale = UIUtil.isRetina() ? 2 : 1; - @SuppressWarnings("UndesirableClassUsage") - BufferedImage image = new BufferedImage(scale*icon.getIconWidth(), scale*icon.getIconHeight(), BufferedImage.TYPE_INT_ARGB); - final Graphics2D graphics = image.createGraphics(); + if (icon instanceof CachedImageIcon) { + disabledIcon = ((CachedImageIcon)icon).asDisabledIcon(); + } else { + final int scale = UIUtil.isRetina() ? 2 : 1; + @SuppressWarnings("UndesirableClassUsage") + BufferedImage image = new BufferedImage(scale * icon.getIconWidth(), scale * icon.getIconHeight(), BufferedImage.TYPE_INT_ARGB); + final Graphics2D graphics = image.createGraphics(); - graphics.setColor(UIUtil.TRANSPARENT_COLOR); - graphics.fillRect(0, 0, icon.getIconWidth(), icon.getIconHeight()); - graphics.scale(scale, scale); - icon.paintIcon(LabelHolder.ourFakeComponent, graphics, 0, 0); + graphics.setColor(UIUtil.TRANSPARENT_COLOR); + graphics.fillRect(0, 0, icon.getIconWidth(), icon.getIconHeight()); + graphics.scale(scale, scale); + icon.paintIcon(LabelHolder.ourFakeComponent, graphics, 0, 0); - graphics.dispose(); + graphics.dispose(); - Image img = ImageUtil.filter(image, UIUtil.getGrayFilter()); - if (UIUtil.isRetina()) img = RetinaImage.createFrom(img); + Image img = ImageUtil.filter(image, UIUtil.getGrayFilter()); + if (UIUtil.isRetina()) img = RetinaImage.createFrom(img); - disabledIcon = new JBImageIcon(img); + disabledIcon = new JBImageIcon(img); + } ourIcon2DisabledIcon.put(icon, disabledIcon); } return disabledIcon; @@ -376,24 +380,36 @@ public final class IconLoader { private volatile boolean dark; private volatile int numberOfPatchers = ourPatchers.size(); - private volatile ImageFilter filter; + private ImageFilter[] myFilters; private final MyScaledIconsCache myScaledIconsCache = new MyScaledIconsCache(); - public CachedImageIcon(@NotNull URL url) { + private CachedImageIcon(@NotNull URL url, @NotNull ImageFilter[] filters) { myUrl = url; dark = USE_DARK_ICONS; - filter = IMAGE_FILTER; + myFilters = filters; } - @NotNull + public CachedImageIcon(@NotNull URL url) { + this(url, new ImageFilter[] {IMAGE_FILTER}); + } + + private void setGlobalFilter(ImageFilter globalFilter) { + myFilters[0] = globalFilter; + } + + private ImageFilter getGlobalFilter() { + return myFilters[0]; + } + + @NotNull private synchronized ImageIcon getRealIcon() { - if (isLoaderDisabled() && (myRealIcon == null || dark != USE_DARK_ICONS || needUpdateJBUIScale() || filter != IMAGE_FILTER || numberOfPatchers != ourPatchers.size())) return EMPTY_ICON; + if (isLoaderDisabled() && (myRealIcon == null || dark != USE_DARK_ICONS || needUpdateJBUIScale() || getGlobalFilter() != IMAGE_FILTER || numberOfPatchers != ourPatchers.size())) return EMPTY_ICON; if (!isValid()) { myRealIcon = null; dark = USE_DARK_ICONS; updateJBUIScale(); - filter = IMAGE_FILTER; + setGlobalFilter(IMAGE_FILTER); myScaledIconsCache.clear(); if (numberOfPatchers != ourPatchers.size()) { numberOfPatchers = ourPatchers.size(); @@ -420,7 +436,7 @@ public final class IconLoader { if (icon != null) return (ImageIcon)icon; } - Image image = ImageLoader.loadFromUrl(myUrl, true, filter); + Image image = ImageLoader.loadFromUrl(myUrl, true, myFilters); icon = checkIcon(image, myUrl); if (icon != null) { @@ -437,7 +453,7 @@ public final class IconLoader { } private boolean isValid() { - return dark == USE_DARK_ICONS && !needUpdateJBUIScale() && filter == IMAGE_FILTER && numberOfPatchers == ourPatchers.size(); + return dark == USE_DARK_ICONS && !needUpdateJBUIScale() && getGlobalFilter() == IMAGE_FILTER && numberOfPatchers == ourPatchers.size(); } @Override @@ -475,6 +491,10 @@ public final class IconLoader { return (icon != null) ? icon : this; } + private Icon asDisabledIcon() { + return new CachedImageIcon(myUrl, new ImageFilter[] {getGlobalFilter(), UIUtil.getGrayFilter()}); + } + private class MyScaledIconsCache { // Map {false -> image}, {true -> image@2x} private Map> origImagesCache = Collections.synchronizedMap(new HashMap>(2)); @@ -493,7 +513,7 @@ public final class IconLoader { Image img = SoftReference.dereference(origImagesCache.get(retina)); if (img == null) { - img = ImageLoader.loadFromUrl(myUrl, UIUtil.isUnderDarcula(), retina, filter); + img = ImageLoader.loadFromUrl(myUrl, UIUtil.isUnderDarcula(), retina, myFilters); origImagesCache.put(retina, new SoftReference(img)); } return img; diff --git a/platform/util/src/com/intellij/util/ImageLoader.java b/platform/util/src/com/intellij/util/ImageLoader.java index 70e34c6513f8..da9d838cc6ca 100644 --- a/platform/util/src/com/intellij/util/ImageLoader.java +++ b/platform/util/src/com/intellij/util/ImageLoader.java @@ -195,6 +195,14 @@ public class ImageLoader implements Serializable { return new ImageConverterChain(); } + public ImageConverterChain withFilter(final ImageFilter[] filters) { + ImageConverterChain chain = this; + for (ImageFilter filter : filters) { + chain = chain.withFilter(filter); + } + return chain; + } + public ImageConverterChain withFilter(final ImageFilter filter) { return with(new ImageConverter() { @Override @@ -253,11 +261,16 @@ public class ImageLoader implements Serializable { @Nullable public static Image loadFromUrl(@NotNull URL url, boolean allowFloatScaling) { - return loadFromUrl(url, allowFloatScaling, null); + return loadFromUrl(url, allowFloatScaling, (ImageFilter)null); } @Nullable public static Image loadFromUrl(@NotNull URL url, boolean allowFloatScaling, ImageFilter filter) { + return loadFromUrl(url, allowFloatScaling, new ImageFilter[] {filter}); + } + + @Nullable + public static Image loadFromUrl(@NotNull URL url, boolean allowFloatScaling, ImageFilter[] filters) { final float scaleFactor = calcScaleFactor(allowFloatScaling); // We can't check all 3rd party plugins and convince the authors to add @2x icons. @@ -271,7 +284,7 @@ public class ImageLoader implements Serializable { return ImageDescList.create(url.toString(), null, UIUtil.isUnderDarcula(), loadRetinaImages, allowFloatScaling).load( ImageConverterChain.create(). - withFilter(filter). + withFilter(filters). withRetina(). with(new ImageConverter() { public Image convert(Image source, ImageDesc desc) { @@ -309,13 +322,18 @@ public class ImageLoader implements Serializable { @Nullable public static Image loadFromUrl(URL url, boolean dark, boolean retina) { - return loadFromUrl(url, dark, retina, null); + return loadFromUrl(url, dark, retina, (ImageFilter[])null); } @Nullable public static Image loadFromUrl(URL url, boolean dark, boolean retina, ImageFilter filter) { + return loadFromUrl(url, dark, retina, new ImageFilter[] {filter}); + } + + @Nullable + public static Image loadFromUrl(URL url, boolean dark, boolean retina, ImageFilter[] filters) { return ImageDescList.create(url.toString(), null, dark, retina, true). - load(ImageConverterChain.create().withFilter(filter).withRetina()); + load(ImageConverterChain.create().withFilter(filters).withRetina()); } @Nullable From 378c31d0e193328c6d1785556e3ce727351e3a12 Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Tue, 1 Nov 2016 19:58:25 +0300 Subject: [PATCH 02/15] IDEA-158577 Visual artefacts in completion in evaluate dialog --- .../ui/popup/HeavyWeightPopupCache.java | 137 ++++++++++++++++++ .../com/intellij/ui/popup/MovablePopup.java | 48 +----- 2 files changed, 143 insertions(+), 42 deletions(-) create mode 100644 platform/platform-impl/src/com/intellij/ui/popup/HeavyWeightPopupCache.java diff --git a/platform/platform-impl/src/com/intellij/ui/popup/HeavyWeightPopupCache.java b/platform/platform-impl/src/com/intellij/ui/popup/HeavyWeightPopupCache.java new file mode 100644 index 000000000000..7c877a03ddd3 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ui/popup/HeavyWeightPopupCache.java @@ -0,0 +1,137 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.ui.popup; + +import com.intellij.util.ReflectionUtil; + +import javax.swing.JWindow; +import java.awt.Window; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; +import java.util.ArrayDeque; + +/** + * @author Sergey Malenkov + */ +final class HeavyWeightPopupCache implements ComponentListener { + /** + * Returns a cached window or creates a new one if cache is empty. + * + * @param owner the window from which a popup is displayed + * @return a window for the specified owner + */ + public static Window create(Window owner) { + if (owner != null) { + HeavyWeightPopupCache cache = getCache(owner); + if (cache != null) { + Window popup = cache.poll(); + if (popup != null) return popup; + } + } + Window popup = new JWindow(owner); + popup.setType(Window.Type.POPUP); + return popup; + } + + /** + * Caches a window if possible. + * + * @param popup the popup window that is not needed + */ + public static void dispose(Window popup) { + if (popup != null) { + hide(popup); + Window owner = popup.getOwner(); + if (owner == null || !owner.isDisplayable()) { + popup.dispose(); + } + else { + HeavyWeightPopupCache cache = getCache(owner); + if (cache == null) { + cache = new HeavyWeightPopupCache(); + owner.addComponentListener(cache); + } + cache.push(popup); + } + } + } + + private static HeavyWeightPopupCache getCache(Window owner) { + for (ComponentListener listener : owner.getComponentListeners()) { + if (listener instanceof HeavyWeightPopupCache) { + return (HeavyWeightPopupCache)listener; + } + } + return null; + } + + private static Window poll(ArrayDeque windows) { + return windows.poll(); + } + + private static void push(ArrayDeque windows, Window popup) { + windows.push(popup); + } + + private static void hide(ArrayDeque windows) { + for (Window window : windows) hide(window); + } + + /** + * Disables showing a child window with its parent. + * + * @param window the popup window that is not needed + */ + private static void hide(Window window) { + // HACK: do not allow to show a hidden popup on showing its parent + ReflectionUtil.setField(Window.class, window, boolean.class, "showWithParent", false); + } + + private final ArrayDeque myWindows = new ArrayDeque<>(); + + private Window poll() { + return poll(myWindows); + } + + private void push(Window popup) { + push(myWindows, popup); + } + + private void hide() { + hide(myWindows); + } + + @Override + public void componentResized(ComponentEvent event) { + } + + @Override + public void componentMoved(ComponentEvent event) { + } + + @Override + public void componentShown(ComponentEvent event) { + } + + @Override + public void componentHidden(ComponentEvent event) { + Object source = event.getSource(); + if (source instanceof Window) { + HeavyWeightPopupCache cache = getCache((Window)source); + if (cache != null) cache.hide(); + } + } +} diff --git a/platform/platform-impl/src/com/intellij/ui/popup/MovablePopup.java b/platform/platform-impl/src/com/intellij/ui/popup/MovablePopup.java index fb2ee27c02c0..a3adb75c4869 100644 --- a/platform/platform-impl/src/com/intellij/ui/popup/MovablePopup.java +++ b/platform/platform-impl/src/com/intellij/ui/popup/MovablePopup.java @@ -20,13 +20,13 @@ import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.*; -import java.util.ArrayDeque; +import java.awt.event.HierarchyListener; /** * @author Sergey Malenkov */ public class MovablePopup { - private static final Object CACHE = new Object(); + private final HierarchyListener myListener = event -> setVisible(false); private final Component myOwner; private final Component myContent; private Rectangle myViewBounds; @@ -161,11 +161,7 @@ public class MovablePopup { Window owner = UIUtil.getWindow(myOwner); if (owner != null) { if (myHeavyWeight) { - Window view = pop(owner); - if (view == null) { - view = new JWindow(owner); - view.setType(Window.Type.POPUP); - } + Window view = HeavyWeightPopupCache.create(owner); setAlwaysOnTop(view, myAlwaysOnTop); setWindowFocusable(view, myWindowFocusable); setWindowShadow(view, myWindowShadow); @@ -193,6 +189,7 @@ public class MovablePopup { myView.setBounds(myViewBounds); myView.setVisible(true); myViewBounds = null; + myOwner.addHierarchyListener(myListener); } } } @@ -206,6 +203,7 @@ public class MovablePopup { private void disposeAndUpdate(boolean update) { if (myView != null) { + myOwner.removeHierarchyListener(myListener); boolean visible = myView.isVisible(); myView.setVisible(false); Container container = myContent.getParent(); @@ -214,10 +212,7 @@ public class MovablePopup { } if (myView instanceof Window) { myViewBounds = myView.getBounds(); - Window window = (Window)myView; - if (!push(UIUtil.getWindow(myOwner), window)) { - window.dispose(); - } + HeavyWeightPopupCache.dispose((Window)myView); } else { Container parent = myView.getParent(); @@ -270,35 +265,4 @@ public class MovablePopup { } return null; } - - private static Window pop(Window owner) { - JRootPane root = getRootPane(owner); - if (root != null) { - synchronized (CACHE) { - @SuppressWarnings("unchecked") - ArrayDeque cache = (ArrayDeque)root.getClientProperty(CACHE); - if (cache != null && !cache.isEmpty()) { - return cache.pop(); - } - } - } - return null; - } - - private static boolean push(Window owner, Window window) { - JRootPane root = getRootPane(owner); - if (root != null) { - synchronized (CACHE) { - @SuppressWarnings("unchecked") - ArrayDeque cache = (ArrayDeque)root.getClientProperty(CACHE); - if (cache == null) { - cache = new ArrayDeque<>(); - root.putClientProperty(CACHE, cache); - } - cache.push(window); - return true; - } - } - return false; - } } From 06bb9864b053ab2c2992b15d573b1a1121ab9e8f Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Tue, 1 Nov 2016 20:34:46 +0300 Subject: [PATCH 03/15] [vcs-log] do not loose first part of the string that can be displayed with the default font IDEA-162944 --- platform/vcs-impl/src/com/intellij/openapi/vcs/ui/FontUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/ui/FontUtil.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/ui/FontUtil.java index a70f2274ff94..2e471e506c41 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/ui/FontUtil.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/ui/FontUtil.java @@ -39,7 +39,7 @@ public class FontUtil { if (start == -1) return input; Font font = null; - StringBuilder result = new StringBuilder(); + StringBuilder result = new StringBuilder(input.substring(0, start)); for (int i = start; i < input.length(); i++) { char c = input.charAt(i); if (baseFont.canDisplay(c)) { From 3dc1461901b7c8c99deef55483add6cf9a1724d8 Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Tue, 1 Nov 2016 20:40:53 +0300 Subject: [PATCH 04/15] revert com.intellij.openapi.util.text.StringUtil.escapeProperty --- .../util/src/com/intellij/openapi/util/text/StringUtil.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/util/src/com/intellij/openapi/util/text/StringUtil.java b/platform/util/src/com/intellij/openapi/util/text/StringUtil.java index e7cdf92aebd5..e525c567970a 100644 --- a/platform/util/src/com/intellij/openapi/util/text/StringUtil.java +++ b/platform/util/src/com/intellij/openapi/util/text/StringUtil.java @@ -2472,7 +2472,7 @@ public class StringUtil extends StringUtilRt { * Escape property name or key in property file. Unicode characters are escaped as well. * * @param input an input to escape - * @param isKey if true, the rules for key escaping are applied. All spaces are escaped in that case. + * @param isKey if true, the rules for key escaping are applied. The leading space is escaped in that case. * @return an escaped string */ @NotNull @@ -2483,7 +2483,7 @@ public class StringUtil extends StringUtilRt { final char ch = input.charAt(i); switch (ch) { case ' ': - if (isKey) { + if (isKey && i == 0) { // only the leading space has to be escaped escaped.append('\\'); } From 60d1c63e9386df08f8bd0ba72bc9a32e2bb739d7 Mon Sep 17 00:00:00 2001 From: "Vassiliy.Kudryashov" Date: Tue, 1 Nov 2016 20:43:00 +0300 Subject: [PATCH 05/15] Search field: fix for unfocused border --- .../intellij/ide/ui/laf/intellij/MacIntelliJTextFieldUI.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/MacIntelliJTextFieldUI.java b/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/MacIntelliJTextFieldUI.java index b92a216e6259..8a2aeea48bc4 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/MacIntelliJTextFieldUI.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/MacIntelliJTextFieldUI.java @@ -220,7 +220,7 @@ public class MacIntelliJTextFieldUI extends TextFieldWithPopupHandlerUI { g.setStroke(new BasicStroke(.5f)); g.draw(getShape(r, 4, false)); - if (true || c.hasFocus()) { + if (c.hasFocus()) { Color graphiteColor = new Color(0x6f6f72); Color blueColor = ColorUtil.brighter(new Color(0x006de2), 3); g.setColor(ColorUtil.withAlpha(IntelliJLaf.isGraphite() ? graphiteColor : blueColor, .3)); From e65daace432c55aafb53562a082b51648445ac66 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Tue, 1 Nov 2016 20:45:09 +0300 Subject: [PATCH 06/15] cleanup IDEA-CR-15243 --- .../plugins/groovy/lang/psi/GroovyPsiElementFactory.java | 3 --- .../groovy/lang/psi/impl/GroovyPsiElementFactoryImpl.java | 7 ------- .../lang/psi/stubs/elements/GrStubFileElementType.java | 3 +-- 3 files changed, 1 insertion(+), 12 deletions(-) diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/GroovyPsiElementFactory.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/GroovyPsiElementFactory.java index 40fbeef261a1..04e6261c6946 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/GroovyPsiElementFactory.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/GroovyPsiElementFactory.java @@ -178,9 +178,6 @@ public abstract class GroovyPsiElementFactory implements JVMElementFactory { @NotNull public abstract GrVariableDeclaration createVariableDeclaration(@Nullable String[] modifiers, @Nullable String initializer, @Nullable PsiType type, String... identifiers); - @NotNull - public abstract GrVariableDeclaration createVariableDeclarationFromText(@NotNull String text, @Nullable PsiElement context); - @NotNull public abstract GrEnumConstant createEnumConstantFromText(@NotNull String text); diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyPsiElementFactoryImpl.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyPsiElementFactoryImpl.java index 882c0a83cb96..06c4221ebb77 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyPsiElementFactoryImpl.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyPsiElementFactoryImpl.java @@ -296,13 +296,6 @@ public class GroovyPsiElementFactoryImpl extends GroovyPsiElementFactory { return statement; } - @NotNull - @Override - public GrVariableDeclaration createVariableDeclarationFromText(@NotNull String text, @Nullable PsiElement context) { - final GroovyFile file = createGroovyFileChecked(text, false, context); - return ((GrVariableDeclaration)file.getTopStatements()[0]); - } - @NotNull @Override public GrEnumConstant createEnumConstantFromText(@NotNull String text) { diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrStubFileElementType.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrStubFileElementType.java index eccea0754c37..38ff0055b8b2 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrStubFileElementType.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrStubFileElementType.java @@ -106,8 +106,7 @@ public class GrStubFileElementType extends IStubFileElementType { public GrFileStub deserialize(@NotNull final StubInputStream dataStream, final StubElement parentStub) throws IOException { StringRef name = dataStream.readName(); boolean isScript = dataStream.readBoolean(); - String[] annotations = GrStubUtils.readStringArray(dataStream); - return new GrFileStub(name, isScript, annotations); + return new GrFileStub(name, isScript, GrStubUtils.readStringArray(dataStream)); } @Override From 2d0a62b52dc6fb13dc55eab0e0f0a8f72e1afb58 Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Tue, 1 Nov 2016 20:57:58 +0300 Subject: [PATCH 07/15] javac ast indices: cleanup (dead code) --- .../compiler/backwardRefs/CompilerReferenceServiceImpl.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceImpl.java b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceImpl.java index cb7b09226521..625a8eefd9dd 100644 --- a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceImpl.java +++ b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceImpl.java @@ -450,8 +450,4 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceService imple return 31 * mySearchType.hashCode() + mySearchFileType.hashCode(); } } - - private static class RawSearchResult { - - } } From e6db59d0973fc9f49130989aa207f7eca09882cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yann=20C=C3=A9bron?= Date: Tue, 1 Nov 2016 20:03:47 +0100 Subject: [PATCH 08/15] IDEA-75328 GotoRelatedSymbolAction: improvements show "No Related symbols found" popup when nothing is available --- .../ide/actions/GotoRelatedSymbolAction.java | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/ide/actions/GotoRelatedSymbolAction.java b/platform/lang-impl/src/com/intellij/ide/actions/GotoRelatedSymbolAction.java index 4a6deff68d9e..796b4b389541 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/GotoRelatedSymbolAction.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/GotoRelatedSymbolAction.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,7 @@ */ package com.intellij.ide.actions; +import com.intellij.codeInsight.hint.HintUtil; import com.intellij.codeInsight.navigation.NavigationUtil; import com.intellij.navigation.GotoRelatedItem; import com.intellij.openapi.actionSystem.AnAction; @@ -22,12 +23,16 @@ import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.ui.popup.Balloon; +import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; +import com.intellij.ui.IdeBorderFactory; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; +import javax.swing.*; import java.util.List; /** @@ -43,17 +48,28 @@ public class GotoRelatedSymbolAction extends AnAction { @Override public void actionPerformed(@NotNull AnActionEvent e) { - PsiElement element = getContextElement(e.getDataContext()); + final DataContext dataContext = e.getDataContext(); + + final PsiElement element = getContextElement(dataContext); if (element == null) return; - List items = NavigationUtil.collectRelatedItems(element, e.getDataContext()); - if (items.isEmpty()) return; + List items = NavigationUtil.collectRelatedItems(element, dataContext); + if (items.isEmpty()) { + final JComponent label = HintUtil.createErrorLabel("No related symbols"); + label.setBorder(IdeBorderFactory.createEmptyBorder(2, 7, 2, 7)); + JBPopupFactory.getInstance().createBalloonBuilder(label) + .setFadeoutTime(3000) + .setFillColor(HintUtil.ERROR_COLOR) + .createBalloon() + .show(JBPopupFactory.getInstance().guessBestPopupLocation(dataContext), Balloon.Position.above); + return; + } if (items.size() == 1 && items.get(0).getElement() != null) { items.get(0).navigate(); return; } - NavigationUtil.getRelatedItemsPopup(items, "Choose Target").showInBestPositionFor(e.getDataContext()); + NavigationUtil.getRelatedItemsPopup(items, "Choose Target").showInBestPositionFor(dataContext); } @TestOnly From 8d76e1994bd233644d6b45d91c21f77045a7571c Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Tue, 1 Nov 2016 22:01:50 +0300 Subject: [PATCH 09/15] [grails] fix detached criteria closures delegate --- .../groovy/lang/psi/patterns/GroovyClosurePattern.kt | 1 + .../plugins/groovy/lang/psi/patterns/groovyPatterns.kt | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/patterns/GroovyClosurePattern.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/patterns/GroovyClosurePattern.kt index 64b6f68dcd2d..ae1a7b19b114 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/patterns/GroovyClosurePattern.kt +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/patterns/GroovyClosurePattern.kt @@ -41,6 +41,7 @@ class GroovyClosurePattern : GroovyExpressionPattern return false } + context?.put(closureCallKey, call) val method = call.resolveMethod() ?: return false return methodPattern.accepts(method) diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/patterns/groovyPatterns.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/patterns/groovyPatterns.kt index 6178b3ca519a..9cdf0a265bf6 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/patterns/groovyPatterns.kt +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/patterns/groovyPatterns.kt @@ -15,17 +15,21 @@ */ package org.jetbrains.plugins.groovy.lang.psi.patterns +import com.intellij.openapi.util.Key import com.intellij.patterns.PatternCondition import com.intellij.util.ProcessingContext import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrCall import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression +val closureCallKey = Key.create("groovy.pattern.closure.call") + inline fun groovyExpression() = GroovyExpressionPattern.Capture(T::class.java) fun groovyList() = groovyExpression().with(object : PatternCondition("isList") { override fun accepts(t: GrListOrMap, context: ProcessingContext?) = !t.isMap }) -fun psiMethod(containingClass: String, name: String) = GroovyPatterns.psiMethod().withName(name).definedInClass(containingClass) +fun psiMethod(containingClass: String, vararg name: String) = GroovyPatterns.psiMethod().withName(*name).definedInClass(containingClass) fun groovyClosure() = GroovyClosurePattern() From ba5f4cd73c085ab6421011262abe61c396103ee9 Mon Sep 17 00:00:00 2001 From: "Vassiliy.Kudryashov" Date: Tue, 1 Nov 2016 22:04:54 +0300 Subject: [PATCH 10/15] Search field: fix for tab / shift+tab navgation --- .../intellij/find/SearchReplaceComponent.java | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/find/SearchReplaceComponent.java b/platform/lang-impl/src/com/intellij/find/SearchReplaceComponent.java index f558d1b83b58..a8e4d57b34c7 100644 --- a/platform/lang-impl/src/com/intellij/find/SearchReplaceComponent.java +++ b/platform/lang-impl/src/com/intellij/find/SearchReplaceComponent.java @@ -445,7 +445,26 @@ public class SearchReplaceComponent extends EditorHeaderComponent implements Dat KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), NEW_LINE_KEYSTROKE, WHEN_FOCUSED); - wrapper.setContent(textArea); + + textComponent.registerKeyboardAction(e -> { + if (isMultiline(textComponent)) { + if (textComponent.isEditable() && textComponent.isEnabled()) { + textComponent.replaceSelection("\t"); + } + else { + UIManager.getLookAndFeel().provideErrorFeedback(textComponent); + } + } + else { + textComponent.transferFocus(); + } + }, KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0), WHEN_FOCUSED); + + textComponent.registerKeyboardAction(e -> { + textComponent.transferFocusBackward(); + }, KeyStroke.getKeyStroke(KeyEvent.VK_TAB, InputEvent.SHIFT_DOWN_MASK), WHEN_FOCUSED); + + wrapper.setContent(textArea); UIUtil.addUndoRedoActions(textComponent); Utils.setSmallerFont(textComponent); @@ -491,6 +510,10 @@ public class SearchReplaceComponent extends EditorHeaderComponent implements Dat area.setRows(Math.max(2, Math.min(3, StringUtil.countChars(area.getText(), '\n') + 1))); } + private static boolean isMultiline(@NotNull JTextComponent component) { + return component.getText().contains("\n"); + } + private void installCloseOnEscapeAction(@NotNull JTextComponent c) { ActionListener action = new ActionListener() { From e6ca666acbdfcf4c5459c4528052a6c536b3265d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yann=20C=C3=A9bron?= Date: Tue, 1 Nov 2016 21:09:27 +0100 Subject: [PATCH 11/15] [devkit] EP "completion.confidence" supports language with ID "any" --- .../devkit/src/dom/impl/LanguageResolvingUtil.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/plugins/devkit/src/dom/impl/LanguageResolvingUtil.java b/plugins/devkit/src/dom/impl/LanguageResolvingUtil.java index 1749250d5cbe..1b9a80c9a4f2 100644 --- a/plugins/devkit/src/dom/impl/LanguageResolvingUtil.java +++ b/plugins/devkit/src/dom/impl/LanguageResolvingUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,7 @@ */ package org.jetbrains.idea.devkit.dom.impl; +import com.intellij.codeInsight.completion.CompletionConfidenceEP; import com.intellij.codeInsight.completion.CompletionContributorEP; import com.intellij.codeInspection.dataFlow.StringExpressionHelper; import com.intellij.icons.AllIcons; @@ -47,6 +48,7 @@ import javax.swing.*; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Set; class LanguageResolvingUtil { private static final String ANY_LANGUAGE_DEFAULT_ID = Language.ANY.getID(); @@ -209,6 +211,11 @@ class LanguageResolvingUtil { return new LanguageDefinition(anyLanguageId, languageClass, AllIcons.FileTypes.Any_type, ""); } + private static final Set EP_WITH_ANY_LANGUAGE_ID = ContainerUtil.immutableSet( + CompletionContributorEP.class.getName(), + CompletionConfidenceEP.class.getName() + ); + private static String calculateAnyLanguageId(ConvertContext context) { final Extension extension = context.getInvocationElement().getParentOfType(Extension.class, true); if (extension == null) { @@ -220,7 +227,7 @@ class LanguageResolvingUtil { } final GenericAttributeValue epBeanClass = extensionPoint.getBeanClass(); - if (CompletionContributorEP.class.getName().equals(epBeanClass.getStringValue())) { + if (EP_WITH_ANY_LANGUAGE_ID.contains(epBeanClass.getStringValue())) { return "any"; } From db3a8b1102ef077442e0875a57f0dcc07a8afcd3 Mon Sep 17 00:00:00 2001 From: Sergey Ignatov Date: Tue, 1 Nov 2016 23:29:23 +0300 Subject: [PATCH 12/15] [dbe] enable source storage by default --- platform/util/resources/misc/registry.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index e65ffa94a80e..9e3c07f6fdb4 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -747,7 +747,7 @@ database.scrollable.result.sets.description=Enables scrollable result sets, if J database.scrollable.result.sets=true database.src_storage.enabled.description=Enables database sources storage database.src_storage.enabled.restartRequired=true -database.src_storage.enabled=false +database.src_storage.enabled=true git.status.write=true git.blocking.read=true From d70d3360fddc4d42aea94465ea530e31a743ae2f Mon Sep 17 00:00:00 2001 From: "Vassiliy.Kudryashov" Date: Tue, 1 Nov 2016 23:51:28 +0300 Subject: [PATCH 13/15] SearchTextArea: borders and backgrounds fix --- .../src/com/intellij/find/SearchTextArea.java | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/find/SearchTextArea.java b/platform/lang-impl/src/com/intellij/find/SearchTextArea.java index 6f010e7e3deb..0b47384345eb 100644 --- a/platform/lang-impl/src/com/intellij/find/SearchTextArea.java +++ b/platform/lang-impl/src/com/intellij/find/SearchTextArea.java @@ -50,6 +50,7 @@ import javax.swing.event.DocumentEvent; import javax.swing.text.DefaultEditorKit; import java.awt.*; import java.awt.event.*; +import java.awt.geom.RoundRectangle2D; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; @@ -181,6 +182,9 @@ public class SearchTextArea extends NonOpaquePanel implements PropertyChangeList myInfoLabel.setText(info); } + private static Color enabledBorderColor = new JBColor(Gray._196, Gray._100); + private static Color disabledBorderColor = Gray._83; + @Override public void paint(Graphics graphics) { Graphics2D g = (Graphics2D)graphics.create(); @@ -191,18 +195,25 @@ public class SearchTextArea extends NonOpaquePanel implements PropertyChangeList if (r.height % 2 == 1) r.height--; int arcSize = Math.min(25, r.height-1); boolean hasFocus = myTextArea.hasFocus(); - g.setColor(myTextArea.isEnabled() ? Gray._196 : Gray._83); - + Color borderColor = myTextArea.isEnabled() ? enabledBorderColor : disabledBorderColor; if (SystemInfo.isMac && (UIUtil.isUnderIntelliJLaF() || UIUtil.isUnderAquaLookAndFeel())) { + g.setColor(borderColor); MacIntelliJTextFieldUI.paintAquaSearchFocusRing(g, r, myTextArea); } else { - JBInsets.removeFrom(r, new Insets(3, 3, 3, 3)); - if (hasFocus) { + JBInsets.removeFrom(r, new JBInsets(3, 3, 3, 3)); + if (hasFocus && (UIUtil.isUnderIntelliJLaF() || UIUtil.isUnderDarcula())) { DarculaUIUtil.paintSearchFocusRing(g, r, myTextArea, arcSize+6); } else { - g.drawRoundRect(r.x, r.y, r.width, r.height, arcSize, arcSize); + Shape shape = UIUtil.isUnderWindowsLookAndFeel() + ? new Rectangle(r.x, r.y, r.width, r.height) + : new RoundRectangle2D.Double(r.x, r.y, r.width, r.height, arcSize, arcSize); + g.setColor(borderColor); + g.setColor(myTextArea.getBackground()); + g.fill(shape); + g.setColor(borderColor); + g.draw(shape); } } } From f9d0b8dadff3208b90bc1d6aea63d9d601e46498 Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Tue, 1 Nov 2016 21:20:25 +0300 Subject: [PATCH 14/15] [Parameter Name Hints] in context menu show dialog on "Add current method to blacklist" with preselected item --- .../codeInsight/hints/ParameterNameInlays.kt | 9 ++- .../codeInsight/hints/PopupActions.kt | 57 +++++++------- .../ParameterNameHintsConfigurable.java | 76 ++++++++++++++----- .../src/messages/CodeInsightBundle.properties | 4 +- .../src/idea/LangActions.xml | 3 +- 5 files changed, 96 insertions(+), 53 deletions(-) diff --git a/platform/lang-api/src/com/intellij/codeInsight/hints/ParameterNameInlays.kt b/platform/lang-api/src/com/intellij/codeInsight/hints/ParameterNameInlays.kt index aa83001db198..c9e8f73de3fe 100644 --- a/platform/lang-api/src/com/intellij/codeInsight/hints/ParameterNameInlays.kt +++ b/platform/lang-api/src/com/intellij/codeInsight/hints/ParameterNameInlays.kt @@ -17,4 +17,11 @@ package com.intellij.codeInsight.hints class InlayInfo(val text: String, val offset: Int) -class MethodInfo(val fullyQualifiedName: String, val paramNames: List) \ No newline at end of file +open class MethodInfo(val fullyQualifiedName: String, val paramNames: List) { + + open fun getMethodName(): String { + val start = fullyQualifiedName.lastIndexOf('.') + 1 + return fullyQualifiedName.substring(start) + } + +} \ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/codeInsight/hints/PopupActions.kt b/platform/lang-impl/src/com/intellij/codeInsight/hints/PopupActions.kt index aba36f650a9e..1c6d201f64d4 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/hints/PopupActions.kt +++ b/platform/lang-impl/src/com/intellij/codeInsight/hints/PopupActions.kt @@ -40,34 +40,30 @@ import com.intellij.psi.util.PsiTreeUtil private fun String.capitalize() = StringUtil.capitalizeWords(this, true) -class ShowParameterHintsSettings : AnAction() { +class ShowSettingsWithAddedPattern : AnAction() { init { - val presentation = templatePresentation - presentation.text = CodeInsightBundle.message("inlay.hints.show.settings").capitalize() - presentation.description = CodeInsightBundle.message("inlay.hints.show.settings.description") + templatePresentation.description = CodeInsightBundle.message("inlay.hints.show.settings.description") + } + + override fun update(e: AnActionEvent) { + val file = CommonDataKeys.PSI_FILE.getData(e.dataContext) ?: return + val editor = CommonDataKeys.EDITOR.getData(e.dataContext) ?: return + val info = getMethodInfoAtOffset(editor, file) ?: return + + val name = info.getMethodName() + e.presentation.text = CodeInsightBundle.message("inlay.hints.show.settings", name) } override fun actionPerformed(e: AnActionEvent) { val project = CommonDataKeys.PROJECT.getData(e.dataContext) ?: return val file = CommonDataKeys.PSI_FILE.getData(e.dataContext) ?: return - val hintExtension = InlayParameterHintsExtension.forLanguage(file.language) ?: return - val dialog = ParameterNameHintsConfigurable(project, hintExtension.defaultBlackList, file.language) - dialog.show() - } -} - -class BlacklistCurrentMethodAction : AnAction() { - init { - val presentation = templatePresentation - presentation.text = CodeInsightBundle.message("inlay.hints.blacklist.method").capitalize() - presentation.description = CodeInsightBundle.message("inlay.hints.blacklist.method.description") - } - - override fun actionPerformed(e: AnActionEvent) { val editor = CommonDataKeys.EDITOR.getData(e.dataContext) ?: return - val file = CommonDataKeys.PSI_FILE.getData(e.dataContext) ?: return + + val hintExtension = InlayParameterHintsExtension.forLanguage(file.language) ?: return + val info = getMethodInfoAtOffset(editor, file) ?: return + val dialog = ParameterNameHintsConfigurable(project, hintExtension.defaultBlackList, file.language, info.toPattern()) - addMethodAtCaretToBlackList(editor, file) + dialog.show() } } @@ -160,18 +156,19 @@ private fun refreshAllOpenEditors() { } } -private fun addMethodAtCaretToBlackList(editor: Editor, file: PsiFile) { +private fun getMethodInfoAtOffset(editor: Editor, file: PsiFile): MethodInfo? { val offset = editor.caretModel.offset - val element = file.findElementAt(offset) - val hintsProvider = InlayParameterHintsExtension.forLanguage(file.language) ?: return - - val method = PsiTreeUtil.findFirstParent(element, { e -> hintsProvider.getMethodInfo(e) != null }) ?: return - val info = hintsProvider.getMethodInfo(method) ?: return - - val pattern = info.fullyQualifiedName + '(' + info.paramNames.joinToString(",") + ')' - ParameterNameHintsSettings.getInstance().addIgnorePattern(file.language, pattern) + + val hintsProvider = InlayParameterHintsExtension.forLanguage(file.language) ?: return null + + val method = PsiTreeUtil.findFirstParent(element, { e -> hintsProvider.getMethodInfo(e) != null }) ?: return null + return hintsProvider.getMethodInfo(method) +} +private fun addMethodAtCaretToBlackList(editor: Editor, file: PsiFile) { + val info = getMethodInfoAtOffset(editor, file) ?: return + ParameterNameHintsSettings.getInstance().addIgnorePattern(file.language, info.toPattern()) refreshAllOpenEditors() } @@ -189,3 +186,5 @@ fun isPossibleHintNearOffset(file: PsiFile, offset: Int): Boolean { return false } + +fun MethodInfo.toPattern() = this.fullyQualifiedName + '(' + this.paramNames.joinToString(",") + ')' diff --git a/platform/lang-impl/src/com/intellij/codeInsight/hints/settings/ParameterNameHintsConfigurable.java b/platform/lang-impl/src/com/intellij/codeInsight/hints/settings/ParameterNameHintsConfigurable.java index fad2ac7c7039..a6040a30aba8 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/hints/settings/ParameterNameHintsConfigurable.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/hints/settings/ParameterNameHintsConfigurable.java @@ -17,37 +17,49 @@ package com.intellij.codeInsight.hints.settings; import com.intellij.codeInsight.hints.filtering.MatcherConstructor; import com.intellij.lang.Language; -import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.EditorFactory; import com.intellij.openapi.editor.event.DocumentAdapter; import com.intellij.openapi.editor.event.DocumentEvent; -import com.intellij.openapi.fileTypes.PlainTextLanguage; +import com.intellij.openapi.fileTypes.FileTypes; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.EditorTextField; -import com.intellij.ui.EditorTextFieldProvider; -import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import java.awt.*; import java.util.List; import java.util.Set; import java.util.stream.Collectors; public class ParameterNameHintsConfigurable extends DialogWrapper { + public JPanel myConfigurable; + private EditorTextField myEditorTextField; - private final Project myProject; private final Set myDefaultBlackList; private final Language myLanguage; + private final String myNewPreselectedItem; + private final Project myProject; - public ParameterNameHintsConfigurable(@NotNull Project project, + public ParameterNameHintsConfigurable(@NotNull Project project, @NotNull Set defaultBlackList, @NotNull Language language) { + this(project, defaultBlackList, language, null); + } + + public ParameterNameHintsConfigurable(@NotNull Project project, + @NotNull Set defaultBlackList, + @NotNull Language language, + @Nullable String newPreselectedPattern) { super(project); myProject = project; - myDefaultBlackList = defaultBlackList; myLanguage = language; + myDefaultBlackList = defaultBlackList; + myNewPreselectedItem = newPreselectedPattern; setTitle("Configure Parameter Name Hints Blacklist"); init(); } @@ -84,18 +96,11 @@ public class ParameterNameHintsConfigurable extends DialogWrapper { return myConfigurable; } - public JPanel myConfigurable; - private EditorTextField myEditorTextField; - private void createUIComponents() { - EditorTextFieldProvider service = ServiceManager.getService(myProject, EditorTextFieldProvider.class); - myEditorTextField = service.getEditorField(PlainTextLanguage.INSTANCE, myProject, ContainerUtil.emptyIterable()); - Diff diff = ParameterNameHintsSettings.getInstance().getBlackListDiff(myLanguage); Set blacklist = diff.applyOn(myDefaultBlackList); - - String text = StringUtil.join(blacklist, "\n"); - myEditorTextField.setText(text); + + myEditorTextField = createEditor(blacklist, myNewPreselectedItem); myEditorTextField.addDocumentListener(new DocumentAdapter() { @Override public void documentChanged(DocumentEvent e) { @@ -103,7 +108,40 @@ public class ParameterNameHintsConfigurable extends DialogWrapper { } }); } - - - + + private EditorTextField createEditor(@NotNull Set blacklist, @Nullable String newPreselectedItem) { + String text = StringUtil.join(blacklist, "\n"); + + final TextRange range; + if (newPreselectedItem != null) { + text += "\n"; + + final int startOffset = text.length(); + text += newPreselectedItem; + range = new TextRange(startOffset, text.length()); + } + else { + range = null; + } + + return createEditorField(text, range); + } + + @NotNull + private EditorTextField createEditorField(@NotNull String text, @Nullable TextRange rangeToSelect) { + Document document = EditorFactory.getInstance().createDocument(text); + EditorTextField field = new EditorTextField(document, myProject, FileTypes.PLAIN_TEXT, false, false); + field.setPreferredSize(new Dimension(200, 350)); + field.addSettingsProvider(editor -> { + editor.setVerticalScrollbarVisible(true); + editor.setHorizontalScrollbarVisible(true); + editor.getSettings().setAdditionalLinesCount(2); + if (rangeToSelect != null) { + editor.getCaretModel().moveToOffset(rangeToSelect.getStartOffset()); + editor.getScrollingModel().scrollVertically(document.getTextLength() - 1); + editor.getSelectionModel().setSelection(rangeToSelect.getStartOffset(), rangeToSelect.getEndOffset()); + } + }); + return field; + } } diff --git a/platform/platform-resources-en/src/messages/CodeInsightBundle.properties b/platform/platform-resources-en/src/messages/CodeInsightBundle.properties index 622644870ece..c094f99c6f0c 100644 --- a/platform/platform-resources-en/src/messages/CodeInsightBundle.properties +++ b/platform/platform-resources-en/src/messages/CodeInsightBundle.properties @@ -528,8 +528,8 @@ highlight.imported.members.chooser.title=Choose Imported Members to Highlight javadoc.resolved.value=Resolved value\: javadoc.error.resolving.url=Couldn''t resolve URL {0}

Configuring paths to API docs in project settings might help -inlay.hints.show.settings=Show parameter name hints settings... -inlay.hints.show.settings.description=Show dialog with parameter name hints settings +inlay.hints.show.settings=Add Method ''{0}'' to Blacklist... +inlay.hints.show.settings.description=Open parameter name hints settings inlay.hints.blacklist.method=Do not show hints for current method inlay.hints.blacklist.method.description=Adds current method to parameter name hints blacklist inlay.hints.intention.family.name=Parameter Name Hints diff --git a/platform/platform-resources/src/idea/LangActions.xml b/platform/platform-resources/src/idea/LangActions.xml index a0c79c8bff5b..13632b2e51b4 100644 --- a/platform/platform-resources/src/idea/LangActions.xml +++ b/platform/platform-resources/src/idea/LangActions.xml @@ -300,8 +300,7 @@ - - + From 0fb67d4f9d76d8c28f4bcdf478a315bd19091f46 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Wed, 2 Nov 2016 11:41:10 +0700 Subject: [PATCH 15/15] StreamToLoopInspection fixes: toArray intermediate list type fixed; keywords are filtered out from possible var names; context added to expression passed to BoolUtils; supported inside method calls --- .../streamToLoop/StreamToLoopInspection.java | 20 +++++---- .../streamToLoop/TerminalOperation.java | 42 +++++++++++-------- .../streamToLoop/afterAllMatchEqEq.java | 19 +++++++++ .../streamToLoop/afterFilterIdentifier.java | 22 ++++++++++ .../quickFix/streamToLoop/afterInCall.java | 18 ++++++++ .../streamToLoop/afterToArrayGeneric.java | 2 +- .../streamToLoop/afterToArrayObject.java | 2 +- .../afterToArrayTypeMismatch.java | 21 ++++++++++ .../streamToLoop/beforeAllMatchEqEq.java | 14 +++++++ .../streamToLoop/beforeFilterIdentifier.java | 14 +++++++ .../quickFix/streamToLoop/beforeInCall.java | 14 +++++++ .../beforeToArrayTypeMismatch.java | 13 ++++++ 12 files changed, 173 insertions(+), 28 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/afterAllMatchEqEq.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/afterFilterIdentifier.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/afterInCall.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/afterToArrayTypeMismatch.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/beforeAllMatchEqEq.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/beforeFilterIdentifier.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/beforeInCall.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/beforeToArrayTypeMismatch.java diff --git a/java/java-impl/src/com/intellij/codeInspection/streamToLoop/StreamToLoopInspection.java b/java/java-impl/src/com/intellij/codeInspection/streamToLoop/StreamToLoopInspection.java index 24c38ec1027f..411a76282ee6 100644 --- a/java/java-impl/src/com/intellij/codeInspection/streamToLoop/StreamToLoopInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/streamToLoop/StreamToLoopInspection.java @@ -20,8 +20,10 @@ import com.intellij.codeInspection.BaseJavaBatchLocalInspectionTool; import com.intellij.codeInspection.LocalQuickFix; import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.codeInspection.ProblemsHolder; +import com.intellij.lang.java.lexer.JavaLexer; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; +import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.codeStyle.JavaCodeStyleManager; @@ -87,7 +89,7 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool { private static boolean isSupportedCodeLocation(PsiMethodCallExpression call) { PsiElement cur = call; PsiElement parent = cur.getParent(); - while(parent instanceof PsiExpression) { + while(parent instanceof PsiExpression || parent instanceof PsiExpressionList) { // TODO: support in single expression lambdas if(parent instanceof PsiLambdaExpression) { return false; @@ -133,9 +135,10 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool { if(InheritanceUtil.isInheritor(aClass, CommonClassNames.JAVA_UTIL_STREAM_BASE_STREAM)) { PsiExpression qualifier = call.getMethodExpression().getQualifierExpression(); if(qualifier != null) { - Operation op = Operation.createIntermediate(name, args, outVar, getStreamElementType(qualifier.getType())); + PsiType elementType = getStreamElementType(qualifier.getType()); + Operation op = Operation.createIntermediate(name, args, outVar, elementType); if (op != null) return op; - op = TerminalOperation.createTerminal(name, args, callType, className, call.getParent() instanceof PsiExpressionStatement); + op = TerminalOperation.createTerminal(name, args, elementType, callType, call.getParent() instanceof PsiExpressionStatement); if (op != null) return op; } } @@ -232,11 +235,12 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool { } } catch (Exception ex) { + String text = terminalCall.getText(); if(temporaryStreamPlaceholder.isPhysical()) { // Just in case if something went wrong: at least try to restore the original stream code - temporaryStreamPlaceholder.replace(factory.createExpressionFromText(terminalCall.getText(), temporaryStreamPlaceholder)); + temporaryStreamPlaceholder.replace(factory.createExpressionFromText(text, temporaryStreamPlaceholder)); } - throw ex; + throw new RuntimeException("Error converting Stream to loop: "+text, ex); } } @@ -380,9 +384,9 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool { } private boolean isUsed(String varName) { - if(myUsedNames.contains(varName)) return true; - // TODO: cleaner solution - return !varName.equals(JavaCodeStyleManager.getInstance(myStatement.getProject()).suggestUniqueVariableName(varName, myStatement, true)); + return myUsedNames.contains(varName) || JavaLexer.isKeyword(varName, LanguageLevel.HIGHEST) || + // TODO: cleaner solution + !varName.equals(JavaCodeStyleManager.getInstance(myStatement.getProject()).suggestUniqueVariableName(varName, myStatement, true)); } public String declare(String desiredName, String type, String initializer) { diff --git a/java/java-impl/src/com/intellij/codeInspection/streamToLoop/TerminalOperation.java b/java/java-impl/src/com/intellij/codeInspection/streamToLoop/TerminalOperation.java index d5691e3ecb17..416475ebcefb 100644 --- a/java/java-impl/src/com/intellij/codeInspection/streamToLoop/TerminalOperation.java +++ b/java/java-impl/src/com/intellij/codeInspection/streamToLoop/TerminalOperation.java @@ -63,7 +63,7 @@ abstract class TerminalOperation extends Operation { } @Nullable - static TerminalOperation createTerminal(String name, PsiExpression[] args, PsiType callType, String className, boolean isVoid) { + static TerminalOperation createTerminal(String name, PsiExpression[] args, PsiType elementType, PsiType resultType, boolean isVoid) { if(isVoid) { if ((name.equals("forEach") || name.equals("forEachOrdered")) && args.length == 1) { FunctionHelper fn = FunctionHelper.create(args[0], 1, true); @@ -75,23 +75,22 @@ abstract class TerminalOperation extends Operation { return new AccumulatedTerminalOperation("count", "long", "0", "{acc}++;"); } if(name.equals("sum") && args.length == 0) { - return new AccumulatedTerminalOperation("sum", callType.getCanonicalText(), "0", "{acc}+={item};"); + return new AccumulatedTerminalOperation("sum", resultType.getCanonicalText(), "0", "{acc}+={item};"); } if(name.equals("average") && args.length == 0) { - if(className.equals(CommonClassNames.JAVA_UTIL_STREAM_DOUBLE_STREAM)) { + if(elementType.equals(PsiType.DOUBLE)) { return new AverageTerminalOperation(true); } - else if(className.equals(CommonClassNames.JAVA_UTIL_STREAM_INT_STREAM) || - className.equals(CommonClassNames.JAVA_UTIL_STREAM_LONG_STREAM)) { + else if(elementType.equals(PsiType.INT) || elementType.equals(PsiType.LONG)) { return new AverageTerminalOperation(false); } } if(name.equals("summaryStatistics") && args.length == 0) { - return new AccumulatedTerminalOperation("stat", callType.getCanonicalText(), "new " + callType.getCanonicalText() + "()", + return new AccumulatedTerminalOperation("stat", resultType.getCanonicalText(), "new " + resultType.getCanonicalText() + "()", "{acc}.accept({item});"); } if((name.equals("findFirst") || name.equals("findAny")) && args.length == 0) { - return new FindTerminalOperation(callType.getCanonicalText()); + return new FindTerminalOperation(resultType.getCanonicalText()); } if((name.equals("anyMatch") || name.equals("allMatch") || name.equals("noneMatch")) && args.length == 1) { FunctionHelper fn = FunctionHelper.create(args[0], 1, true); @@ -101,11 +100,11 @@ abstract class TerminalOperation extends Operation { if(args.length == 2 || args.length == 3) { FunctionHelper fn = FunctionHelper.create(args[1], 2, true); if(fn != null) { - return new ReduceTerminalOperation(args[0], fn, callType.getCanonicalText()); + return new ReduceTerminalOperation(args[0], fn, resultType.getCanonicalText()); } } if(args.length == 1) { - PsiType optionalElementType = getOptionalElementType(callType); + PsiType optionalElementType = getOptionalElementType(resultType); FunctionHelper fn = FunctionHelper.create(args[0], 2, true); if(fn != null && optionalElementType != null) { return new ReduceToOptionalTerminalOperation(fn, optionalElementType.getCanonicalText()); @@ -113,8 +112,8 @@ abstract class TerminalOperation extends Operation { } } if(name.equals("toArray") && args.length < 2) { - if(!(callType instanceof PsiArrayType)) return null; - PsiType componentType = ((PsiArrayType)callType).getComponentType(); + if(!(resultType instanceof PsiArrayType)) return null; + PsiType componentType = ((PsiArrayType)resultType).getComponentType(); if (componentType instanceof PsiPrimitiveType) { if(args.length == 0) return new ToPrimitiveArrayTerminalOperation(componentType.getCanonicalText()); } @@ -130,7 +129,7 @@ abstract class TerminalOperation extends Operation { if(!(type instanceof PsiArrayType)) return null; arr = "new "+type.getCanonicalText().replaceFirst("\\[]", "[0]"); } - return new AccumulatedTerminalOperation("list", CommonClassNames.JAVA_UTIL_LIST + "<" + componentType.getCanonicalText() + ">", + return new AccumulatedTerminalOperation("list", CommonClassNames.JAVA_UTIL_LIST + "<" + elementType.getCanonicalText() + ">", "new "+ CommonClassNames.JAVA_UTIL_ARRAY_LIST+"<>()", "{acc}.add({item});", "{acc}.toArray("+arr+")"); } @@ -144,25 +143,25 @@ abstract class TerminalOperation extends Operation { PsiClass collectorClass = collector.getContainingClass(); if(collectorClass != null && CommonClassNames.JAVA_UTIL_STREAM_COLLECTORS.equals(collectorClass.getQualifiedName())) { if(collector.getName().equals("toList") && collectorArgs.length == 0) { - return AccumulatedTerminalOperation.toCollection(callType, CommonClassNames.JAVA_UTIL_ARRAY_LIST, "list"); + return AccumulatedTerminalOperation.toCollection(resultType, CommonClassNames.JAVA_UTIL_ARRAY_LIST, "list"); } if(collector.getName().equals("toSet") && collectorArgs.length == 0) { - return AccumulatedTerminalOperation.toCollection(callType, CommonClassNames.JAVA_UTIL_HASH_SET, "set"); + return AccumulatedTerminalOperation.toCollection(resultType, CommonClassNames.JAVA_UTIL_HASH_SET, "set"); } if(collector.getName().equals("toCollection") && collectorArgs.length == 1) { FunctionHelper fn = FunctionHelper.create(collectorArgs[0], 0, true); if(fn != null) { - return new ToCollectionTerminalOperation(fn, callType); + return new ToCollectionTerminalOperation(fn, resultType); } } if(collector.getName().equals("reducing") && collectorArgs.length == 2) { FunctionHelper fn = FunctionHelper.create(collectorArgs[1], 2, true); if(fn != null) { - return new ReduceTerminalOperation(collectorArgs[0], fn, callType.getCanonicalText()); + return new ReduceTerminalOperation(collectorArgs[0], fn, resultType.getCanonicalText()); } } if(collector.getName().equals("reducing") && collectorArgs.length == 1) { - PsiType optionalElementType = getOptionalElementType(callType); + PsiType optionalElementType = getOptionalElementType(resultType); FunctionHelper fn = FunctionHelper.create(collectorArgs[0], 2, true); if(fn != null && optionalElementType != null) { return new ReduceToOptionalTerminalOperation(fn, optionalElementType.getCanonicalText()); @@ -355,7 +354,14 @@ abstract class TerminalOperation extends Operation { @Override String generate(StreamVariable inVar, StreamToLoopReplacementContext context) { myFn.transform(context, inVar.getName()); - String expression = myNegatePredicate ? BoolUtils.getNegatedExpressionText(myFn.getExpression()) : myFn.getText(); + String expression; + if (myNegatePredicate) { + PsiLambdaExpression lambda = (PsiLambdaExpression)context.createExpression("(" + inVar.getDeclaration() + ")->" + myFn.getText()); + expression = BoolUtils.getNegatedExpressionText((PsiExpression)lambda.getBody()); + } + else { + expression = myFn.getText(); + } return "if(" + expression + ") {\n" + context.assignAndBreak(myName, PsiType.BOOLEAN.getCanonicalText(), String.valueOf(!myDefaultValue), String.valueOf(myDefaultValue)) + "}\n"; diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/afterAllMatchEqEq.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/afterAllMatchEqEq.java new file mode 100644 index 000000000000..796856052c76 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/afterAllMatchEqEq.java @@ -0,0 +1,19 @@ +// "Replace Stream API chain with loop" "true" + +import java.util.Arrays; +import java.util.List; + +public class Main { + private static boolean test(List list) { + for (String s : list) { + if (s.trim() != s.toLowerCase()) { + return false; + } + } + return true; + } + + public static void main(String[] args) { + System.out.println(test(Arrays.asList("a", "b", "c"))); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/afterFilterIdentifier.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/afterFilterIdentifier.java new file mode 100644 index 000000000000..ce6cac10de78 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/afterFilterIdentifier.java @@ -0,0 +1,22 @@ +// "Replace Stream API chain with loop" "true" + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +public class Main { + private static test(List packages) { + Optional found = Optional.empty(); + for (String s : packages) { + if (s.startsWith("xyz")) { + found = Optional.of(s); + break; + } + } + return found.filter(pkg -> pkg.endsWith("abc")).isPresent(); + } + + public static void main(String[] args) { + System.out.println(test(Arrays.asList("xyzabc", "xyz123", "123abc", "123"))); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/afterInCall.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/afterInCall.java new file mode 100644 index 000000000000..a4454d5dca83 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/afterInCall.java @@ -0,0 +1,18 @@ +// "Replace Stream API chain with loop" "true" + +import java.util.*; +import java.util.stream.*; + +public class Main { + private static void test(List test) { + StringBuilder sb = new StringBuilder(); + for (String s : test) { + sb.append(s); + } + System.out.println("x"+ sb.toString() +"y"); + } + + public static void main(String[] args) { + System.out.println(test(Arrays.asList("a", "b", "c"))); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/afterToArrayGeneric.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/afterToArrayGeneric.java index 74861fc8bfe5..0c4785829206 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/afterToArrayGeneric.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/afterToArrayGeneric.java @@ -7,7 +7,7 @@ import java.util.List; public class Main { private static List[] test(int[] numbers) { - List> list = new ArrayList<>(); + List> list = new ArrayList<>(); for (int number : numbers) { Integer n = number; List integers = Collections.singletonList(n); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/afterToArrayObject.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/afterToArrayObject.java index 1fc5c218c7f5..a9ee870d3bb4 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/afterToArrayObject.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/afterToArrayObject.java @@ -6,7 +6,7 @@ import java.util.List; public class Main { private static Object[] test(int[] numbers) { - List list = new ArrayList<>(); + List list = new ArrayList<>(); for (int number : numbers) { Integer integer = number; list.add(integer); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/afterToArrayTypeMismatch.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/afterToArrayTypeMismatch.java new file mode 100644 index 000000000000..997c7d4354f5 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/afterToArrayTypeMismatch.java @@ -0,0 +1,21 @@ +// "Replace Stream API chain with loop" "true" + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +public class Main { + private static Number[] test(Object[] objects) { + List list = new ArrayList<>(); + for (Object object : objects) { + if (Number.class.isInstance(object)) { + list.add(object); + } + } + return list.toArray(new Number[0]); + } + + public static void main(String[] args) { + System.out.println(Arrays.asList(test(new Object[]{1, 2, 3, "string", 4}))); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/beforeAllMatchEqEq.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/beforeAllMatchEqEq.java new file mode 100644 index 000000000000..5069a897f65c --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/beforeAllMatchEqEq.java @@ -0,0 +1,14 @@ +// "Replace Stream API chain with loop" "true" + +import java.util.Arrays; +import java.util.List; + +public class Main { + private static boolean test(List list) { + return list.stream().allMatch(s -> s.trim() == s.toLowerCase()); + } + + public static void main(String[] args) { + System.out.println(test(Arrays.asList("a", "b", "c"))); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/beforeFilterIdentifier.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/beforeFilterIdentifier.java new file mode 100644 index 000000000000..aef856b1df38 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/beforeFilterIdentifier.java @@ -0,0 +1,14 @@ +// "Replace Stream API chain with loop" "true" + +import java.util.Arrays; +import java.util.List; + +public class Main { + private static test(List packages) { + return packages.stream().filter(pkg -> pkg.startsWith("xyz")).findAny().filter(pkg -> pkg.endsWith("abc")).isPresent(); + } + + public static void main(String[] args) { + System.out.println(test(Arrays.asList("xyzabc", "xyz123", "123abc", "123"))); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/beforeInCall.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/beforeInCall.java new file mode 100644 index 000000000000..44008779af68 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/beforeInCall.java @@ -0,0 +1,14 @@ +// "Replace Stream API chain with loop" "true" + +import java.util.*; +import java.util.stream.*; + +public class Main { + private static void test(List test) { + System.out.println("x"+test.stream().collect(Collectors.joining())+"y"); + } + + public static void main(String[] args) { + System.out.println(test(Arrays.asList("a", "b", "c"))); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/beforeToArrayTypeMismatch.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/beforeToArrayTypeMismatch.java new file mode 100644 index 000000000000..b095125e6c2f --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/beforeToArrayTypeMismatch.java @@ -0,0 +1,13 @@ +// "Replace Stream API chain with loop" "true" + +import java.util.stream.Stream; + +public class Main { + private static Number[] test(Object[] objects) { + return Stream.of(objects).filter(Number.class::isInstance).toArray(Number[]::new); + } + + public static void main(String[] args) { + System.out.println(Arrays.asList(test(new Object[]{1, 2, 3, "string", 4}))); + } +} \ No newline at end of file