From 144de773397e74ee6a9366dd9fad5c7d55cc30d3 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Mon, 22 May 2017 18:27:02 +0300 Subject: [PATCH 001/136] [tw] add ability to retrieve selected index directly from tabbed content --- .../intellij/ui/content/TabbedContent.java | 21 ++++++++++++++++++- .../ui/content/impl/TabbedContentImpl.java | 6 +++--- .../src/com/intellij/util/ContentUtilEx.java | 5 ++++- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/platform/platform-api/src/com/intellij/ui/content/TabbedContent.java b/platform/platform-api/src/com/intellij/ui/content/TabbedContent.java index f3606dac8238..1735d2dde9ae 100644 --- a/platform/platform-api/src/com/intellij/ui/content/TabbedContent.java +++ b/platform/platform-api/src/com/intellij/ui/content/TabbedContent.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -29,10 +29,29 @@ public interface TabbedContent extends Content { String SPLIT_PROPERTY_PREFIX = "tabbed.toolwindow.expanded."; void addContent(@NotNull JComponent content, @NotNull String name, boolean selectTab); + void removeContent(@NotNull JComponent content); + + /** + * This method is used for preselecting popup menu items + * + * @return index of selected tab + * @see #selectContent(int) + */ + default int getSelectedIndex() { return -1; } + + /** + * This method is invoked before content is selected with {@link ContentManager#setSelectedContent(Content)} + * + * @param index index of tab in {@link #getTabs()} + */ void selectContent(int index); + List> getTabs(); + String getTitlePrefix(); + void setTitlePrefix(String titlePrefix); + void split(); } diff --git a/platform/platform-impl/src/com/intellij/ui/content/impl/TabbedContentImpl.java b/platform/platform-impl/src/com/intellij/ui/content/impl/TabbedContentImpl.java index cd52c0383806..8e8910480bd6 100644 --- a/platform/platform-impl/src/com/intellij/ui/content/impl/TabbedContentImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/content/impl/TabbedContentImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -95,8 +95,8 @@ public class TabbedContentImpl extends ContentImpl implements TabbedContent { setDisplayName(tab.first); setComponent(tab.second); } - - public int getSelectedContent() { + + public int getSelectedIndex() { JComponent selected = getComponent(); for (int i = 0; i < myTabs.size(); i++) { if (myTabs.get(i).second == selected) return i; diff --git a/platform/platform-impl/src/com/intellij/util/ContentUtilEx.java b/platform/platform-impl/src/com/intellij/util/ContentUtilEx.java index e699fbc8dd81..3eb528ad9524 100644 --- a/platform/platform-impl/src/com/intellij/util/ContentUtilEx.java +++ b/platform/platform-impl/src/com/intellij/util/ContentUtilEx.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -185,6 +185,9 @@ public class ContentUtilEx extends ContentsUtil { } public static int getSelectedTab(@NotNull TabbedContent content) { + int selectedIndex = content.getSelectedIndex(); + if (selectedIndex != -1) return selectedIndex; + final JComponent current = content.getComponent(); int index = 0; for (Pair tab : content.getTabs()) { From 4bd73669fe946245531433c06d023cad1949da40 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Fri, 26 May 2017 16:38:10 +0300 Subject: [PATCH 002/136] [tw] add TabbedContent.hasMultipleTabs() --- .../execution/dashboard/RunDashboardManagerImpl.java | 5 +++++ .../intellij/openapi/ui/PanelWithActionsAndCloseButton.java | 2 +- .../src/com/intellij/ui/content/TabbedContent.java | 4 ++++ .../platform-api/src/com/intellij/util/ContentsUtil.java | 2 +- .../openapi/wm/impl/content/ToolWindowContentUi.java | 2 +- 5 files changed, 12 insertions(+), 3 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/execution/dashboard/RunDashboardManagerImpl.java b/platform/lang-impl/src/com/intellij/execution/dashboard/RunDashboardManagerImpl.java index d9678af2cb4f..05fa639418ad 100644 --- a/platform/lang-impl/src/com/intellij/execution/dashboard/RunDashboardManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/dashboard/RunDashboardManagerImpl.java @@ -372,6 +372,11 @@ public class RunDashboardManagerImpl implements RunDashboardManager, PersistentS .map(content -> Pair.create(content.getDisplayName(), content.getComponent())).collect(Collectors.toList()); } + @Override + public boolean hasMultipleTabs() { + return myDashboardContentManager.getContents().length > 1; + } + @Override public String getTitlePrefix() { return myTitlePrefix; diff --git a/platform/platform-api/src/com/intellij/openapi/ui/PanelWithActionsAndCloseButton.java b/platform/platform-api/src/com/intellij/openapi/ui/PanelWithActionsAndCloseButton.java index 62fca11394a4..167a6dd85b98 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/PanelWithActionsAndCloseButton.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/PanelWithActionsAndCloseButton.java @@ -110,7 +110,7 @@ public abstract class PanelWithActionsAndCloseButton extends JPanel implements D Content content = myContentManager.getContent(PanelWithActionsAndCloseButton.this); if (content != null) { ContentsUtil.closeContentTab(myContentManager, content); - if (content instanceof TabbedContent && ((TabbedContent)content).getTabs().size() > 1) { + if (content instanceof TabbedContent && ((TabbedContent)content).hasMultipleTabs()) { final TabbedContent tabbedContent = (TabbedContent)content; final JComponent component = content.getComponent(); tabbedContent.removeContent(component); diff --git a/platform/platform-api/src/com/intellij/ui/content/TabbedContent.java b/platform/platform-api/src/com/intellij/ui/content/TabbedContent.java index 1735d2dde9ae..4d5ad49911e6 100644 --- a/platform/platform-api/src/com/intellij/ui/content/TabbedContent.java +++ b/platform/platform-api/src/com/intellij/ui/content/TabbedContent.java @@ -49,6 +49,10 @@ public interface TabbedContent extends Content { List> getTabs(); + default boolean hasMultipleTabs() { + return getTabs().size() > 1; + } + String getTitlePrefix(); void setTitlePrefix(String titlePrefix); diff --git a/platform/platform-api/src/com/intellij/util/ContentsUtil.java b/platform/platform-api/src/com/intellij/util/ContentsUtil.java index 0595becc14eb..550c980344a7 100644 --- a/platform/platform-api/src/com/intellij/util/ContentsUtil.java +++ b/platform/platform-api/src/com/intellij/util/ContentsUtil.java @@ -58,7 +58,7 @@ public class ContentsUtil { public static void closeContentTab(@NotNull ContentManager contentManager, @NotNull Content content) { if (content instanceof TabbedContent) { TabbedContent tabbedContent = (TabbedContent)content; - if (tabbedContent.getTabs().size() > 1) { + if (tabbedContent.hasMultipleTabs()) { JComponent component = tabbedContent.getComponent(); tabbedContent.removeContent(component); contentManager.setSelectedContent(tabbedContent, true, true); diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ToolWindowContentUi.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ToolWindowContentUi.java index ea0a2be5580b..57765b246e34 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ToolWindowContentUi.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ToolWindowContentUi.java @@ -354,7 +354,7 @@ public class ToolWindowContentUi extends JPanel implements ContentUI, PropertyCh group.add(myPreviousTabAction); group.add(myShowContent); - if (content instanceof TabbedContent && ((TabbedContent)content).getTabs().size() > 1) { + if (content instanceof TabbedContent && ((TabbedContent)content).hasMultipleTabs()) { group.addAction(createSplitTabsAction((TabbedContent)content)); } From ff227c23efa8ebf487530fd6b1f1757bec1cfbd5 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Fri, 19 May 2017 19:41:17 +0300 Subject: [PATCH 003/136] [tw] tabbed content updates - Select tab before selecting content in ContentManager - Do not draw the icon when the tabbed content has only one tab - Do not show the popup when the tabbed content has only one tab: Before the change content was selected every time user clicks on a label. There was no way to simply view the list of available tabs. - Add reusable list popup steps for selecting content and subcontent. Before this change dummy DumbAwareAction-s was used to create the list popup via ActionGroup. Also this addes icons as a side effect of using custom list popup steps. - Do not show visible tabs in More action --- .../wm/impl/content/ContentTabLabel.java | 29 +++---- .../wm/impl/content/SelectContentStep.kt | 51 +++++++++++++ .../wm/impl/content/SelectContentTabStep.kt | 42 ++++++++++ .../wm/impl/content/TabContentLayout.java | 70 ++++------------- .../impl/content/TabbedContentTabLabel.java | 76 ++++++++----------- .../wm/impl/content/ToolWindowContentUi.java | 65 ++-------------- 6 files changed, 159 insertions(+), 174 deletions(-) create mode 100644 platform/platform-impl/src/com/intellij/openapi/wm/impl/content/SelectContentStep.kt create mode 100644 platform/platform-impl/src/com/intellij/openapi/wm/impl/content/SelectContentTabStep.kt diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ContentTabLabel.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ContentTabLabel.java index 4c4f7598b1ef..13abb7baaff1 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ContentTabLabel.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ContentTabLabel.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -23,6 +23,7 @@ import com.intellij.util.ui.BaseButtonBehavior; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.TimedDeadzone; import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.*; @@ -30,26 +31,27 @@ import java.awt.event.MouseEvent; class ContentTabLabel extends BaseLabel { - Content myContent; - private final BaseButtonBehavior myBehavior; + private final Content myContent; private final TabContentLayout myLayout; - public ContentTabLabel(final Content content, TabContentLayout layout) { + public ContentTabLabel(@NotNull Content content, @NotNull TabContentLayout layout) { super(layout.myUi, true); myLayout = layout; myContent = content; - update(); - - myBehavior = new BaseButtonBehavior(this) { + BaseButtonBehavior behavior = new BaseButtonBehavior(this) { protected void execute(final MouseEvent e) { - final ContentManager mgr = contentManager(); - if (mgr.getIndexOfContent(myContent) >= 0) { - mgr.setSelectedContent(myContent, true); - } + selectContent(); } }; - myBehavior.setActionTrigger(MouseEvent.MOUSE_PRESSED); - myBehavior.setMouseDeadzone(TimedDeadzone.NULL); + behavior.setActionTrigger(MouseEvent.MOUSE_PRESSED); + behavior.setMouseDeadzone(TimedDeadzone.NULL); + } + + protected void selectContent() { + final ContentManager mgr = contentManager(); + if (mgr.getIndexOfContent(myContent) >= 0) { + mgr.setSelectedContent(myContent, true); + } } public void update() { @@ -106,6 +108,7 @@ class ContentTabLabel extends BaseLabel { return myUi.myWindow.getContentManager(); } + @NotNull @Override public Content getContent() { return myContent; diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/SelectContentStep.kt b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/SelectContentStep.kt new file mode 100644 index 000000000000..7c4e82093e41 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/SelectContentStep.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2000-2017 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.wm.impl.content + +import com.intellij.openapi.ui.popup.PopupStep +import com.intellij.openapi.ui.popup.util.BaseListPopupStep +import com.intellij.ui.content.Content +import com.intellij.ui.content.TabbedContent +import javax.swing.Icon + +class SelectContentStep : BaseListPopupStep { + + constructor(contents: Array) : super(null, *contents) + constructor(contents: List) : super(null, contents) + + override fun isSpeedSearchEnabled(): Boolean = true + + override fun getIconFor(value: Content): Icon? = value.icon + + override fun getTextFor(value: Content): String { + return value.asMultiTabbed()?.titlePrefix ?: value.displayName ?: super.getTextFor(value) + } + + override fun hasSubstep(value: Content): Boolean = value.asMultiTabbed() != null + + override fun onChosen(value: Content, finalChoice: Boolean): PopupStep<*>? { + val tabbed = value.asMultiTabbed() + if (tabbed == null) { + value.manager?.setSelectedContentCB(value, true, true) + return PopupStep.FINAL_CHOICE + } + else { + return SelectContentTabStep(tabbed) + } + } + + private fun Content.asMultiTabbed(): TabbedContent? = if (this is TabbedContent && hasMultipleTabs()) this else null +} diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/SelectContentTabStep.kt b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/SelectContentTabStep.kt new file mode 100644 index 000000000000..762f3cbb519e --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/SelectContentTabStep.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2000-2017 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.wm.impl.content + +import com.intellij.openapi.ui.popup.PopupStep +import com.intellij.openapi.ui.popup.util.BaseListPopupStep +import com.intellij.ui.content.TabbedContent + +class SelectContentTabStep(val content: TabbedContent) : BaseListPopupStep(null) { + + private val myTabs = content.tabs + + init { + val indexes = (0 until myTabs.size).toList() + init(null, indexes, null) + defaultOptionIndex = content.selectedIndex + } + + override fun isSpeedSearchEnabled(): Boolean = true + + override fun getTextFor(value: Int): String = myTabs[value].first + + override fun onChosen(selectedValue: Int, finalChoice: Boolean): PopupStep<*>? { + val manager = content.manager ?: return FINAL_CHOICE + content.selectContent(selectedValue) + manager.setSelectedContent(content) + return FINAL_CHOICE + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/TabContentLayout.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/TabContentLayout.java index ca060eead771..b0b68e87e382 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/TabContentLayout.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/TabContentLayout.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 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,38 +17,31 @@ package com.intellij.openapi.wm.impl.content; import com.intellij.ide.dnd.DnDSupport; import com.intellij.ide.dnd.DnDTarget; -import com.intellij.openapi.ui.JBPopupMenu; +import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.ui.popup.ListPopup; import com.intellij.ui.UIBundle; +import com.intellij.ui.awt.RelativePoint; import com.intellij.ui.awt.RelativeRectangle; import com.intellij.ui.content.Content; import com.intellij.ui.content.ContentManager; import com.intellij.ui.content.ContentManagerEvent; import com.intellij.ui.content.TabbedContent; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.BaseButtonBehavior; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.Nullable; -import javax.swing.*; -import javax.swing.event.PopupMenuEvent; -import javax.swing.event.PopupMenuListener; import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; +import java.util.*; +import java.util.List; class TabContentLayout extends ContentLayout { static final int MORE_ICON_BORDER = 6; LayoutData myLastLayout; - JPopupMenu myPopup; - final PopupMenuListener myPopupListener; - ArrayList myTabs = new ArrayList<>(); final Map myContent2Tabs = new HashMap<>(); @@ -71,8 +64,6 @@ class TabContentLayout extends ContentLayout { TabContentLayout(ToolWindowContentUi ui) { super(ui); - myPopupListener = new MyPopupListener(); - new BaseButtonBehavior(myUi) { protected void execute(final MouseEvent e) { if (!myUi.isCurrent(TabContentLayout.this)) return; @@ -80,7 +71,7 @@ class TabContentLayout extends ContentLayout { if (myLastLayout != null) { final Rectangle moreRect = myLastLayout.moreRect; if (moreRect != null && moreRect.contains(e.getPoint())) { - showPopup(); + showPopup(e, ContainerUtil.filter(myTabs, myLastLayout.toDrop::contains)); } } } @@ -109,41 +100,10 @@ class TabContentLayout extends ContentLayout { myIdLabel = null; } - private void showPopup() { - myPopup = new JBPopupMenu(); - myPopup.addPopupMenuListener(myPopupListener); - - ArrayList tabs = myTabs; - - for (final ContentTabLabel each : tabs) { - final JCheckBoxMenuItem item = new JCheckBoxMenuItem(each.getText()); - if (myUi.myManager.isSelected(each.myContent)) { - item.setSelected(true); - } - item.addActionListener(new ActionListener() { - public void actionPerformed(final ActionEvent e) { - myUi.myManager.setSelectedContent(each.myContent, true); - } - }); - myPopup.add(item); - } - myPopup.show(myUi, myLastLayout.moreRect.x, myLastLayout.moreRect.y); - } - - - private class MyPopupListener implements PopupMenuListener { - public void popupMenuWillBecomeVisible(final PopupMenuEvent e) { - } - - public void popupMenuWillBecomeInvisible(final PopupMenuEvent e) { - if (myPopup != null) { - myPopup.removePopupMenuListener(this); - } - myPopup = null; - } - - public void popupMenuCanceled(final PopupMenuEvent e) { - } + private static void showPopup(MouseEvent e, List tabs) { + final List contentsToShow = ContainerUtil.map(tabs, ContentTabLabel::getContent); + final SelectContentStep step = new SelectContentStep(contentsToShow); + JBPopupFactory.getInstance().createListPopup(step).show(new RelativePoint(e)); } @Override @@ -173,7 +133,7 @@ class TabContentLayout extends ContentLayout { myLastLayout.contentCount == manager.getContentCount()) { for (ContentTabLabel each : myTabs) { if (!each.isValid()) break; - if (each.myContent == selected && each.getBounds().width != 0) { + if (each.getContent() == selected && each.getBounds().width != 0) { data = myLastLayout; data.fullLayout = false; } @@ -294,7 +254,7 @@ class TabContentLayout extends ContentLayout { int moreRectWidth; ArrayList toLayout = new ArrayList<>(); - ArrayList toDrop = new ArrayList<>(); + Collection toDrop = new HashSet<>(); Rectangle moreRect; @@ -429,11 +389,11 @@ class TabContentLayout extends ContentLayout { myUi.removeAll(); myUi.add(myIdLabel); - myUi.initMouseListeners(myIdLabel, myUi); + ToolWindowContentUi.initMouseListeners(myIdLabel, myUi); for (ContentTabLabel each : myTabs) { myUi.add(each); - myUi.initMouseListeners(each, myUi); + ToolWindowContentUi.initMouseListeners(each, myUi); } myCached.clear(); diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/TabbedContentTabLabel.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/TabbedContentTabLabel.java index 7537e337e5ba..84967d1318fc 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/TabbedContentTabLabel.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/TabbedContentTabLabel.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -18,23 +18,15 @@ package com.intellij.openapi.wm.impl.content; import com.intellij.ide.IdeEventQueue; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.ui.popup.ListPopup; import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.util.Pair; import com.intellij.reference.SoftReference; -import com.intellij.ui.ClickListener; -import com.intellij.ui.components.JBList; import com.intellij.ui.content.TabbedContent; -import com.intellij.util.NotNullFunction; -import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; -import javax.swing.*; -import javax.swing.border.EmptyBorder; import java.awt.*; -import java.awt.event.MouseEvent; import java.lang.ref.Reference; import java.lang.ref.WeakReference; -import java.util.ArrayList; /** * @author Konstantin Bulenkov @@ -54,45 +46,23 @@ public class TabbedContentTabLabel extends ContentTabLabel { private final TabbedContent myContent; private Reference myPopupReference = null; - public TabbedContentTabLabel(TabbedContent content, TabContentLayout layout) { + public TabbedContentTabLabel(@NotNull TabbedContent content, @NotNull TabContentLayout layout) { super(content, layout); myContent = content; - new ClickListener() { - @Override - public boolean onClick(@NotNull MouseEvent event, int clickCount) { - showPopup(); - return true; - } - }.installOn(this); } - private void showPopup() { + @Override + protected void selectContent() { IdeEventQueue.getInstance().getPopupManager().closeAllPopups(); - ArrayList names = new ArrayList(); - for (Pair tab : myContent.getTabs()) { - names.add(tab.first); + + if (!hasMultipleTabs()) { + super.selectContent(); + return; } - final JBList list = new JBList(names); - list.installCellRenderer(new NotNullFunction() { - private final JLabel label = new JLabel(); - { - label.setBorder(new EmptyBorder(UIUtil.getListCellPadding())); - } - @NotNull - @Override - public JComponent fun(Object dom) { - label.setText(dom.toString()); - return label; - } - }); - final JBPopup popup = JBPopupFactory.getInstance().createListPopupBuilder(list) - .setItemChoosenCallback(() -> { - int index = list.getSelectedIndex(); - if (index != -1) { - myContent.selectContent(index); - } - }).createPopup(); - myPopupReference = new WeakReference(popup); + + final SelectContentTabStep step = new SelectContentTabStep(getContent()); + final ListPopup popup = JBPopupFactory.getInstance().createListPopup(step); + myPopupReference = new WeakReference<>(popup); popup.showUnderneathOf(this); } @@ -102,19 +72,23 @@ public class TabbedContentTabLabel extends ContentTabLabel { if (myContent != null) { setText(myContent.getTabName()); } - setHorizontalAlignment(LEFT); + if (hasMultipleTabs()) { + setHorizontalAlignment(LEFT); + } } @Override public Dimension getPreferredSize() { final Dimension size = super.getPreferredSize(); - return new Dimension(size.width + 12, size.height); + return hasMultipleTabs() ? new Dimension(size.width + 12, size.height) : size; } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); - myComboIcon.paintIcon(this, g); + if (hasMultipleTabs()) { + myComboIcon.paintIcon(this, g); + } } @Override @@ -126,4 +100,14 @@ public class TabbedContentTabLabel extends ContentTabLabel { myPopupReference = null; } } + + @NotNull + @Override + public TabbedContent getContent() { + return myContent; + } + + private boolean hasMultipleTabs() { + return myContent != null && myContent.hasMultipleTabs(); + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ToolWindowContentUi.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ToolWindowContentUi.java index 57765b246e34..992bbb9c64c8 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ToolWindowContentUi.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ToolWindowContentUi.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2017 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,7 +15,6 @@ */ package com.intellij.openapi.wm.impl.content; -import com.intellij.ide.DataManager; import com.intellij.ide.IdeEventQueue; import com.intellij.ide.actions.CloseAction; import com.intellij.ide.actions.ShowContentAction; @@ -28,7 +27,6 @@ import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.ui.popup.ListPopup; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.Ref; import com.intellij.openapi.wm.IdeFrame; import com.intellij.openapi.wm.ToolWindowContentUiType; import com.intellij.openapi.wm.impl.ToolWindowImpl; @@ -76,7 +74,6 @@ public class ToolWindowContentUi extends JPanel implements ContentUI, PropertyCh ContentLayout myComboLayout = new ComboContentLayout(this); private ToolWindowContentUiType myType = ToolWindowContentUiType.TABBED; - private boolean myShouldNotShowPopup; public ToolWindowContentUi(ToolWindowImpl window) { myWindow = window; @@ -509,69 +506,17 @@ public class ToolWindowContentUi extends JPanel implements ContentUI, PropertyCh } public void toggleContentPopup() { - if (myShouldNotShowPopup) { - myShouldNotShowPopup = false; - return; - } - final Ref selected = Ref.create(); - final Ref selectedTab = Ref.create(); final Content[] contents = myManager.getContents(); final Content selectedContent = myManager.getSelectedContent(); - final AnAction[] actions = new AnAction[contents.length]; - for (int i = 0; i < actions.length; i++) { - final Content content = contents[i]; - if (content instanceof TabbedContent) { - final TabbedContent tabbedContent = (TabbedContent)content; - final List> tabs = ((TabbedContent)content).getTabs(); - final AnAction[] tabActions = new AnAction[tabs.size()]; - for (int j = 0; j < tabActions.length; j++) { - final int index = j; - tabActions[j] = new DumbAwareAction(tabs.get(index).first) { - @Override - public void actionPerformed(@NotNull AnActionEvent e) { - myManager.setSelectedContent(tabbedContent); - tabbedContent.selectContent(index); - } - }; - } - final DefaultActionGroup group = new DefaultActionGroup(tabActions); - group.getTemplatePresentation().setText(((TabbedContent)content).getTitlePrefix()); - group.setPopup(true); - actions[i] = group; - if (content == selectedContent) { - selected.set(group); - final int selectedIndex = ContentUtilEx.getSelectedTab(tabbedContent); - if (selectedIndex != -1) { - selectedTab.set(tabActions[selectedIndex]); - } - } - } else { - actions[i] = new DumbAwareAction() { - { - getTemplatePresentation().setText(content.getTabName(), false); - } - - @Override - public void actionPerformed(@NotNull AnActionEvent e) { - myManager.setSelectedContent(content, true, true); - } - }; - if (content == selectedContent) { - selected.set(actions[i]); - } - } - } - - final ListPopup popup = JBPopupFactory.getInstance().createActionGroupPopup(null, new DefaultActionGroup(actions), - DataManager.getInstance() - .getDataContext(myManager.getComponent()), false, true, - true, null, -1, action -> action == selected.get() || action == selectedTab.get()); + final SelectContentStep step = new SelectContentStep(contents); + step.setDefaultOptionIndex(myManager.getIndexOfContent(selectedContent)); + final ListPopup popup = JBPopupFactory.getInstance().createListPopup(step); getCurrentLayout().showContentPopup(popup); if (selectedContent instanceof TabbedContent) { - new Alarm(Alarm.ThreadToUse.SWING_THREAD, popup).addRequest(() -> popup.handleSelect(true), 30); + new Alarm(Alarm.ThreadToUse.SWING_THREAD, popup).addRequest(() -> popup.handleSelect(false), 50); } } } From fe00c0f376dc43cd3f3fd0b41e04a46ff4aaef17 Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Tue, 30 May 2017 17:05:33 +0300 Subject: [PATCH 004/136] inspection view: remove redundant inheritance on OccurenceNavigator --- .../ui/InspectionResultsView.java | 35 +------------------ 1 file changed, 1 insertion(+), 34 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionResultsView.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionResultsView.java index ef815a16983b..5b2677bddcbf 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionResultsView.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionResultsView.java @@ -93,10 +93,7 @@ import java.util.List; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutorService; -/** - * @author max - */ -public class InspectionResultsView extends JPanel implements Disposable, OccurenceNavigator, DataProvider { +public class InspectionResultsView extends JPanel implements Disposable, DataProvider { private static final Logger LOG = Logger.getInstance(InspectionResultsView.class); public static final DataKey DATA_KEY = DataKey.create("inspectionView"); @@ -865,36 +862,6 @@ public class InspectionResultsView extends JPanel implements Disposable, Occuren return myOccurenceNavigator; } - @Override - public boolean hasNextOccurence() { - return myOccurenceNavigator != null && myOccurenceNavigator.hasNextOccurence(); - } - - @Override - public boolean hasPreviousOccurence() { - return myOccurenceNavigator != null && myOccurenceNavigator.hasPreviousOccurence(); - } - - @Override - public OccurenceInfo goNextOccurence() { - return myOccurenceNavigator != null ? myOccurenceNavigator.goNextOccurence() : null; - } - - @Override - public OccurenceInfo goPreviousOccurence() { - return myOccurenceNavigator != null ? myOccurenceNavigator.goPreviousOccurence() : null; - } - - @Override - public String getNextOccurenceActionName() { - return myOccurenceNavigator != null ? myOccurenceNavigator.getNextOccurenceActionName() : ""; - } - - @Override - public String getPreviousOccurenceActionName() { - return myOccurenceNavigator != null ? myOccurenceNavigator.getPreviousOccurenceActionName() : ""; - } - @NotNull public Project getProject() { return myProject; From cca2aef6ec38849dc0af5ec5969b4b88c2cf5dd5 Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Tue, 30 May 2017 17:09:58 +0300 Subject: [PATCH 005/136] inspection view: decompose InspectionResultView --- .../codeInspection/ui/CloseAction.java | 37 ++++++++++++++ .../ui/InspectionResultsView.java | 40 +-------------- .../codeInspection/ui/RerunAction.java | 49 +++++++++++++++++++ 3 files changed, 88 insertions(+), 38 deletions(-) create mode 100644 platform/lang-impl/src/com/intellij/codeInspection/ui/CloseAction.java create mode 100644 platform/lang-impl/src/com/intellij/codeInspection/ui/RerunAction.java diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/CloseAction.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/CloseAction.java new file mode 100644 index 000000000000..2e7280024136 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/CloseAction.java @@ -0,0 +1,37 @@ +/* + * Copyright 2000-2017 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.codeInspection.ui; + +import com.intellij.CommonBundle; +import com.intellij.codeInspection.ex.GlobalInspectionContextImpl; +import com.intellij.icons.AllIcons; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.project.DumbAware; + +class CloseAction extends AnAction implements DumbAware { + private GlobalInspectionContextImpl myContext; + + CloseAction(GlobalInspectionContextImpl context) { + super(CommonBundle.message("action.close"), null, AllIcons.Actions.Cancel); + myContext = context; + } + + @Override + public void actionPerformed(AnActionEvent e) { + myContext.close(true); + } +} diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionResultsView.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionResultsView.java index 5b2677bddcbf..8b0a0c650c06 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionResultsView.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionResultsView.java @@ -16,7 +16,6 @@ package com.intellij.codeInspection.ui; -import com.intellij.CommonBundle; import com.intellij.ReviseWhenPortedToJDK; import com.intellij.analysis.AnalysisScope; import com.intellij.analysis.AnalysisUIOptions; @@ -30,7 +29,6 @@ import com.intellij.codeInspection.reference.RefEntity; import com.intellij.codeInspection.ui.actions.ExportHTMLAction; import com.intellij.codeInspection.ui.actions.InvokeQuickFixAction; import com.intellij.diff.util.DiffUtil; -import com.intellij.icons.AllIcons; import com.intellij.ide.*; import com.intellij.ide.actions.ContextHelpAction; import com.intellij.ide.actions.exclusion.ExclusionHandler; @@ -46,7 +44,6 @@ import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.colors.EditorColors; import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.fileEditor.OpenFileDescriptor; -import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Splitter; import com.intellij.openapi.ui.popup.JBPopup; @@ -386,8 +383,8 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro private JComponent createLeftActionsToolbar() { final CommonActionsManager actionsManager = CommonActionsManager.getInstance(); DefaultActionGroup group = new DefaultActionGroup(); - group.add(new RerunAction(this)); - group.add(new CloseAction()); + group.add(new RerunAction(this, this)); + group.add(new CloseAction(myGlobalInspectionContext)); final TreeExpander treeExpander = new DefaultTreeExpander(myTree); group.add(actionsManager.createExpandAllAction(treeExpander, myTree)); group.add(actionsManager.createCollapseAllAction(treeExpander, myTree)); @@ -1074,44 +1071,11 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro return myDisposed; } - private class CloseAction extends AnAction implements DumbAware { - private CloseAction() { - super(CommonBundle.message("action.close"), null, AllIcons.Actions.Cancel); - } - - @Override - public void actionPerformed(AnActionEvent e) { - myGlobalInspectionContext.close(true); - } - } - public void updateCurrentProfile() { final String name = myInspectionProfile.getName(); myInspectionProfile = myInspectionProfile.getProfileManager().getProfile(name); } - private class RerunAction extends AnAction { - RerunAction(JComponent comp) { - super(InspectionsBundle.message("inspection.action.rerun"), InspectionsBundle.message("inspection.action.rerun"), - AllIcons.Actions.Rerun); - registerCustomShortcutSet(CommonShortcuts.getRerun(), comp); - } - - @Override - public void update(AnActionEvent e) { - e.getPresentation().setEnabled(isRerunAvailable()); - } - - @Override - public void actionPerformed(AnActionEvent e) { - rerun(); - } - - private void rerun() { - InspectionResultsView.this.rerun(); - } - } - public boolean isRerunAvailable() { return !(myProvider instanceof OfflineInspectionRVContentProvider) && myScope.isValid(); } diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/RerunAction.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/RerunAction.java new file mode 100644 index 000000000000..2224c6a19cbd --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/RerunAction.java @@ -0,0 +1,49 @@ +/* + * Copyright 2000-2017 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.codeInspection.ui; + +import com.intellij.codeInspection.InspectionsBundle; +import com.intellij.icons.AllIcons; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonShortcuts; + +import javax.swing.*; + +class RerunAction extends AnAction { + private final InspectionResultsView myView; + + RerunAction(InspectionResultsView view, JComponent comp) { + super(InspectionsBundle.message("inspection.action.rerun"), InspectionsBundle.message("inspection.action.rerun"), + AllIcons.Actions.Rerun); + myView = view; + registerCustomShortcutSet(CommonShortcuts.getRerun(), comp); + } + + @Override + public void update(AnActionEvent e) { + e.getPresentation().setEnabled(myView.isRerunAvailable()); + } + + @Override + public void actionPerformed(AnActionEvent e) { + rerun(); + } + + private void rerun() { + myView.rerun(); + } +} From dfe1b5e47f89986c8bcc95c03797a411d1251827 Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Tue, 30 May 2017 17:29:11 +0300 Subject: [PATCH 006/136] inspection view: inline project field in InspectionResultView --- .../ui/InspectionResultsView.java | 65 +++++++------------ .../codeInspection/ui/InspectionTree.java | 4 +- 2 files changed, 24 insertions(+), 45 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionResultsView.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionResultsView.java index 8b0a0c650c06..66bc2ec0133a 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionResultsView.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionResultsView.java @@ -96,10 +96,8 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro public static final DataKey DATA_KEY = DataKey.create("inspectionView"); private static final Key PREVIEW_EDITOR_IS_REUSED_KEY = Key.create("inspection.tool.window.preview.editor.is.reused"); - private final Project myProject; private final InspectionTree myTree; - private final ConcurrentMap> myGroups = - ContainerUtil.newConcurrentMap(); + private final ConcurrentMap> myGroups = ContainerUtil.newConcurrentMap(); private final OccurenceNavigator myOccurenceNavigator; private volatile InspectionProfileImpl myInspectionProfile; private final boolean mySettingsEnabled; @@ -138,7 +136,6 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro public InspectionResultsView(@NotNull GlobalInspectionContextImpl globalInspectionContext, @NotNull InspectionRVContentProvider provider) { setLayout(new BorderLayout()); - myProject = globalInspectionContext.getProject(); myInspectionProfile = globalInspectionContext.getCurrentProfile(); myScope = globalInspectionContext.getCurrentScope(); myGlobalInspectionContext = globalInspectionContext; @@ -146,12 +143,12 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro myExcludedInspectionTreeNodesManager = new ExcludedInspectionTreeNodesManager(provider instanceof OfflineInspectionRVContentProvider, isSingleInspectionRun()); - myTree = new InspectionTree(myProject, globalInspectionContext, this); + myTree = new InspectionTree(globalInspectionContext, this); initTreeListeners(); myOccurenceNavigator = initOccurenceNavigator(); - mySplitter = new OnePixelSplitter(false, AnalysisUIOptions.getInstance(myProject).SPLITTER_PROPORTION); + mySplitter = new OnePixelSplitter(false, AnalysisUIOptions.getInstance(globalInspectionContext.getProject()).SPLITTER_PROPORTION); mySplitter.setFirstComponent(ScrollPaneFactory.createScrollPane(myTree, SideBorder.LEFT)); mySplitter.setHonorComponentsMinimumSize(false); @@ -245,9 +242,9 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro } }; createActionsToolbar(); - PsiManager.getInstance(myProject).addPsiTreeChangeListener(new InspectionViewPsiTreeChangeAdapter(this), this); + PsiManager.getInstance(getProject()).addPsiTreeChangeListener(new InspectionViewPsiTreeChangeAdapter(this), this); - ProjectInspectionProfileManager profileManager = ProjectInspectionProfileManager.getInstance(myProject); + ProjectInspectionProfileManager profileManager = ProjectInspectionProfileManager.getInstance(getProject()); profileManager.addProfileChangeListener(new ProfileChangeAdapter() { @Override public void profileChanged(InspectionProfile profile) { @@ -358,12 +355,9 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro } private void createActionsToolbar() { - final JComponent leftActionsToolbar = createLeftActionsToolbar(); - final JComponent rightActionsToolbar = createRightActionsToolbar(); - JPanel westPanel = new JPanel(new BorderLayout()); - westPanel.add(leftActionsToolbar, BorderLayout.WEST); - westPanel.add(rightActionsToolbar, BorderLayout.EAST); + westPanel.add(createLeftActionsToolbar(), BorderLayout.WEST); + westPanel.add(createRightActionsToolbar(), BorderLayout.EAST); add(westPanel, BorderLayout.WEST); } @@ -413,9 +407,8 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro } } - private boolean isAutoScrollMode() { - String activeToolWindowId = ToolWindowManager.getInstance(myProject).getActiveToolWindowId(); + String activeToolWindowId = ToolWindowManager.getInstance(getProject()).getActiveToolWindowId(); return myGlobalInspectionContext.getUIOptions().AUTOSCROLL_TO_SOURCE && (activeToolWindowId == null || activeToolWindowId.equals(ToolWindowId.INSPECTION)); } @@ -430,27 +423,13 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro @Nullable private static OpenFileDescriptor getOpenFileDescriptor(final RefElement refElement) { - final VirtualFile[] file = new VirtualFile[1]; - final int[] offset = new int[1]; - - ApplicationManager.getApplication().runReadAction(() -> { - PsiElement psiElement = refElement.getElement(); - if (psiElement != null) { - final PsiFile containingFile = psiElement.getContainingFile(); - if (containingFile != null) { - file[0] = containingFile.getVirtualFile(); - offset[0] = psiElement.getTextOffset(); - } - } - else { - file[0] = null; - } - }); - - if (file[0] != null && file[0].isValid()) { - return new OpenFileDescriptor(refElement.getRefManager().getProject(), file[0], offset[0]); - } - return null; + PsiElement psiElement = refElement.getElement(); + if (psiElement == null) return null; + final PsiFile containingFile = psiElement.getContainingFile(); + if (containingFile == null) return null; + VirtualFile file = containingFile.getVirtualFile(); + if (file == null) return null; + return new OpenFileDescriptor(refElement.getRefManager().getProject(), file, psiElement.getTextOffset()); } public void setApplyingFix(boolean applyingFix) { @@ -595,7 +574,7 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro myPreviewEditor.getMarkupModel().removeAllHighlighters(); } else { - myPreviewEditor = (EditorEx)EditorFactory.getInstance().createEditor(document, myProject, file.getVirtualFile(), true); + myPreviewEditor = (EditorEx)EditorFactory.getInstance().createEditor(document, getProject(), file.getVirtualFile(), true); DiffUtil.setFoldingModelSupport(myPreviewEditor); final EditorSettings settings = myPreviewEditor.getSettings(); settings.setLineNumbersShown(false); @@ -615,7 +594,7 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro return Pair.create(myPreviewEditor.getComponent(), myPreviewEditor); } if (selectedEntity == null) { - return Pair.create(new InspectionNodeInfo(myTree, myProject), null); + return Pair.create(new InspectionNodeInfo(myTree, getProject()), null); } if (selectedEntity.isValid()) { return Pair.create(InspectionResultsViewUtil.getPreviewIsNotAvailable(selectedEntity), null); @@ -784,7 +763,7 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro InspectionToolWrapper toolWrapper = state.getTool(); if (ReadAction.compute(() -> myProvider.checkReportedProblems(myGlobalInspectionContext, toolWrapper))) { addTool(toolWrapper, - profile.getErrorLevel(key, state.getScope(myProject), myProject), + profile.getErrorLevel(key, state.getScope(getProject()), getProject()), isGroupedBySeverity, singleInspectionRun); } @@ -843,7 +822,7 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro if (isGroupedBySeverity) { InspectionSeverityGroupNode severityGroupNode = mySeverityGroupNodes.get(level); if (severityGroupNode == null) { - InspectionSeverityGroupNode newNode = new InspectionSeverityGroupNode(myProject, level); + InspectionSeverityGroupNode newNode = new InspectionSeverityGroupNode(getProject(), level); severityGroupNode = ConcurrencyUtil.cacheOrGet(mySeverityGroupNodes, level, newNode); if (severityGroupNode == newNode) { InspectionTreeNode root = myTree.getRoot(); @@ -861,7 +840,7 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro @NotNull public Project getProject() { - return myProject; + return myGlobalInspectionContext.getProject(); } @Override @@ -972,7 +951,7 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro startOffset = textRange.getStartOffset(); } } - return new OpenFileDescriptor(myProject, virtualFile, startOffset); + return new OpenFileDescriptor(getProject(), virtualFile, startOffset); } return null; } @@ -1083,7 +1062,7 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro public void rerun() { myRerun = true; if (myScope.isValid()) { - AnalysisUIOptions.getInstance(myProject).save(myGlobalInspectionContext.getUIOptions()); + AnalysisUIOptions.getInstance(getProject()).save(myGlobalInspectionContext.getUIOptions()); myGlobalInspectionContext.setTreeState(getTree().getTreeState()); myGlobalInspectionContext.doInspections(myScope); } diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionTree.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionTree.java index a08fffb23132..90dccde763d4 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionTree.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionTree.java @@ -56,9 +56,9 @@ public class InspectionTree extends Tree { @NotNull private InspectionTreeState myState = new InspectionTreeState(); private boolean myQueueUpdate; - public InspectionTree(@NotNull Project project, - @NotNull GlobalInspectionContextImpl context, + public InspectionTree(@NotNull GlobalInspectionContextImpl context, @NotNull InspectionResultsView view) { + Project project = context.getProject(); setModel(new DefaultTreeModel(new InspectionRootNode(project, new InspectionTreeUpdater(view)))); myContext = context; myExcludedManager = view.getExcludedManager(); From a270531ae09531faaf476577742ba1b782e22b03 Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Tue, 30 May 2017 17:33:28 +0300 Subject: [PATCH 007/136] inspection view: fix typo --- .../codeInspection/ui/InspectionTreeState.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionTreeState.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionTreeState.java index 70a850707b5e..bf420dfb96a2 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionTreeState.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionTreeState.java @@ -63,14 +63,14 @@ public class InspectionTreeState { private static class InspectionTreeSelectionPath { private final Object[] myPath; - private final int[] myIndicies; + private final int[] myIndices; public InspectionTreeSelectionPath(TreePath path) { myPath = path.getPath(); - myIndicies = new int[myPath.length]; + myIndices = new int[myPath.length]; for (int i = 0; i < myPath.length - 1; i++) { InspectionTreeNode node = (InspectionTreeNode)myPath[i]; - myIndicies[i + 1] = getChildIndex(node, (InspectionTreeNode)myPath[i + 1]); + myIndices[i + 1] = getChildIndex(node, (InspectionTreeNode)myPath[i + 1]); } } @@ -116,8 +116,8 @@ public class InspectionTreeState { // Exactly same element not found. Trying to select somewhat near. int count = newRoot.getChildCount(); if (count > 0) { - if (myIndicies[idx] < count) { - newPath.add(newRoot.getChildAt(myIndicies[idx])); + if (myIndices[idx] < count) { + newPath.add(newRoot.getChildAt(myIndices[idx])); } else { newPath.add(newRoot.getChildAt(count - 1)); From e74959a9910f562a5a905b4260f92453bc8802cf Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Mon, 5 Jun 2017 12:07:55 +0300 Subject: [PATCH 008/136] IDEA-173906 shelve: highlight syntax in diff --- .../vcs/changes/shelf/DiffShelvedChangesActionProvider.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesActionProvider.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesActionProvider.java index bad6e597a4d3..772fc2514639 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesActionProvider.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesActionProvider.java @@ -245,7 +245,7 @@ public class DiffShelvedChangesActionProvider implements AnActionExtensionProvid DiffContentFactory contentFactory = DiffContentFactory.getInstance(); DiffContent leftContent = withLocal ? contentFactory.create(project, file) - : contentFactory.create(project, patch.getSingleHunkPatchText()); + : contentFactory.create(project, patch.getSingleHunkPatchText(), file); return new SimpleDiffRequest(getName(), leftContent, contentFactory.createEmpty(), withLocal ? CURRENT_VERSION : SHELVED_VERSION, null); @@ -269,8 +269,8 @@ public class DiffShelvedChangesActionProvider implements AnActionExtensionProvid DiffContentFactory contentFactory = DiffContentFactory.getInstance(); DiffContent leftContent = withLocal ? contentFactory.create(project, file) - : contentFactory.create(project, assertNotNull(texts.getBase())); - return new SimpleDiffRequest(getName(), leftContent, contentFactory.create(project, texts.getPatched()), + : contentFactory.create(project, assertNotNull(texts.getBase()), file); + return new SimpleDiffRequest(getName(), leftContent, contentFactory.create(project, texts.getPatched(), file), withLocal ? CURRENT_VERSION : BASE_VERSION, SHELVED_VERSION); } else { From 1ffac15ddf23f01c3e414332937e4c7a7fa7b061 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Mon, 5 Jun 2017 12:17:29 +0300 Subject: [PATCH 009/136] vcs: fix "Preview Diff" state in Local Changes * remove unnecessary inner state, that could differ from the real one --- .../vcs/changes/ChangesViewManager.java | 11 ++++--- .../actions/ShowDiffPreviewAction.java | 13 -------- .../shelf/ShelvedChangesViewManager.java | 30 +++++++++++-------- 3 files changed, 24 insertions(+), 30 deletions(-) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesViewManager.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesViewManager.java index a43c2189bb97..103acc7c0821 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesViewManager.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesViewManager.java @@ -73,7 +73,6 @@ import java.awt.event.KeyEvent; import java.util.Collection; import java.util.List; -import static com.intellij.util.ObjectUtils.assertNotNull; import static java.util.stream.Collectors.toList; @State( @@ -83,7 +82,7 @@ import static java.util.stream.Collectors.toList; public class ChangesViewManager implements ChangesViewI, ProjectComponent, PersistentStateComponent { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.changes.ChangesViewManager"); - public static final String CHANGES_VIEW_PREVIEW_SPLITTER_PROPORTION = "ChangesViewManager.DETAILS_SPLITTER_PROPORTION"; + private static final String CHANGES_VIEW_PREVIEW_SPLITTER_PROPORTION = "ChangesViewManager.DETAILS_SPLITTER_PROPORTION"; @NotNull private final ChangesListView myView; private JPanel myProgressLabel; @@ -478,10 +477,14 @@ public class ChangesViewManager implements ChangesViewI, ProjectComponent, Persi private class ToggleDetailsAction extends ShowDiffPreviewAction { @Override public void setSelected(AnActionEvent e, boolean state) { - super.setSelected(e, state); - assertNotNull(mySplitterComponent).setDetailsOn(state); + mySplitterComponent.setDetailsOn(state); VcsConfiguration.getInstance(myProject).LOCAL_CHANGES_DETAILS_PREVIEW_SHOWN = state; } + + @Override + public boolean isSelected(AnActionEvent e) { + return VcsConfiguration.getInstance(myProject).LOCAL_CHANGES_DETAILS_PREVIEW_SHOWN; + } } private class MyChangeProcessor extends ChangeViewDiffRequestProcessor { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/ShowDiffPreviewAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/ShowDiffPreviewAction.java index 068a97b8ea16..56528d7d599b 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/ShowDiffPreviewAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/ShowDiffPreviewAction.java @@ -16,24 +16,11 @@ package com.intellij.openapi.vcs.changes.actions; import com.intellij.icons.AllIcons; -import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.ToggleAction; import com.intellij.openapi.project.DumbAware; public abstract class ShowDiffPreviewAction extends ToggleAction implements DumbAware { - private boolean myState; - public ShowDiffPreviewAction() { super("Preview Diff", null, AllIcons.Actions.DiffPreview); } - - @Override - public boolean isSelected(AnActionEvent e) { - return myState; - } - - @Override - public void setSelected(AnActionEvent e, boolean state) { - myState = state; - } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedChangesViewManager.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedChangesViewManager.java index a811a1a7c1ab..76c6bc03fd89 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedChangesViewManager.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedChangesViewManager.java @@ -108,6 +108,7 @@ public class ShelvedChangesViewManager implements ProjectComponent { private static final Logger LOG = Logger.getInstance(ShelvedChangesViewManager.class); @NonNls static final String SHELF_CONTEXT_MENU = "Vcs.Shelf.ContextMenu"; + private static final String SHELVE_PREVIEW_SPLITTER_PROPORTION = "ShelvedChangesViewManager.DETAILS_SPLITTER_PROPORTION"; private final ChangesViewContentManager myContentManager; private final ShelveChangesManager myShelveChangesManager; @@ -247,21 +248,11 @@ public class ShelvedChangesViewManager implements ProjectComponent { DefaultActionGroup actionGroup = new DefaultActionGroup(); actionGroup.addAll((ActionGroup)ActionManager.getInstance().getAction("ShelvedChangesToolbar")); - ShowDiffPreviewAction diffPreviewAction = new ShowDiffPreviewAction() { - @Override - public void setSelected(AnActionEvent e, boolean state) { - super.setSelected(e, state); - assertNotNull(mySplitterComponent).setDetailsOn(state); - VcsConfiguration.getInstance(myProject).SHELVE_DETAILS_PREVIEW_SHOWN = state; - } - }; - actionGroup.add(diffPreviewAction, new Constraints(AFTER, "ShelvedChanges.ShowHideDeleted")); + actionGroup.add(new MyToggleDetailsAction(), new Constraints(AFTER, "ShelvedChanges.ShowHideDeleted")); MyShelvedPreviewProcessor changeProcessor = new MyShelvedPreviewProcessor(myProject); - mySplitterComponent = - new PreviewDiffSplitterComponent(pane, changeProcessor, "ShelvedChangesViewManager.DETAILS_SPLITTER_PROPORTION", - VcsConfiguration.getInstance(myProject).SHELVE_DETAILS_PREVIEW_SHOWN); - diffPreviewAction.setSelected(null, mySplitterComponent.isDetailsOn()); + mySplitterComponent = new PreviewDiffSplitterComponent(pane, changeProcessor, SHELVE_PREVIEW_SPLITTER_PROPORTION, + VcsConfiguration.getInstance(myProject).SHELVE_DETAILS_PREVIEW_SHOWN); ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar("ShelvedChanges", actionGroup, false); JPanel rootPanel = new JPanel(new BorderLayout()); @@ -742,6 +733,19 @@ public class ShelvedChangesViewManager implements ProjectComponent { return new DnDImage(image, new Point(-image.getWidth(null), -image.getHeight(null))); } + private class MyToggleDetailsAction extends ShowDiffPreviewAction { + @Override + public void setSelected(AnActionEvent e, boolean state) { + mySplitterComponent.setDetailsOn(state); + VcsConfiguration.getInstance(myProject).SHELVE_DETAILS_PREVIEW_SHOWN = state; + } + + @Override + public boolean isSelected(AnActionEvent e) { + return VcsConfiguration.getInstance(myProject).SHELVE_DETAILS_PREVIEW_SHOWN; + } + } + private class MyShelvedPreviewProcessor extends CacheDiffRequestProcessor implements DiffPreviewUpdateProcessor { @NotNull private final DiffShelvedChangesActionProvider.PatchesPreloader myPreloader; From 4de0110a00902609c6e54a6bdb38088135551543 Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Mon, 5 Jun 2017 12:20:27 +0300 Subject: [PATCH 010/136] show errors in a separate node in filtered arrays --- .../intellij/debugger/engine/JavaValue.java | 37 +++++++++++++++--- .../debugger/ui/impl/watch/DebuggerTree.java | 39 +++++++++++++++++-- .../ui/tree/render/ArrayRenderer.java | 20 +++++++++- .../ui/tree/render/ChildrenBuilder.java | 22 +++++------ 4 files changed, 94 insertions(+), 24 deletions(-) diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java b/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java index 40cb2be97daa..b5fa6b017b1a 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java @@ -92,7 +92,7 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV myContextSet = contextSet; } - static JavaValue create(JavaValue parent, + public static JavaValue create(JavaValue parent, @NotNull ValueDescriptorImpl valueDescriptor, @NotNull EvaluationContextImpl evaluationContext, NodeManagerImpl nodeManager, @@ -364,11 +364,6 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV return myValueDescriptor; } - @Override - public void setRemaining(int remaining) { - node.tooManyChildren(remaining); - } - @Override public void initChildrenArrayRenderer(ArrayRenderer renderer) { renderer.START_INDEX = myCurrentChildrenStart; @@ -408,6 +403,36 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV @Nullable XDebuggerTreeNodeHyperlink link) { node.setMessage(message, icon, attributes, link); } + + @Override + public void addChildren(@NotNull XValueChildrenList children, boolean last) { + node.addChildren(children, last); + } + + @Override + public void tooManyChildren(int remaining) { + node.tooManyChildren(remaining); + } + + @Override + public void setAlreadySorted(boolean alreadySorted) { + node.setAlreadySorted(alreadySorted); + } + + @Override + public void setErrorMessage(@NotNull String errorMessage) { + node.setErrorMessage(errorMessage); + } + + @Override + public void setErrorMessage(@NotNull String errorMessage, @Nullable XDebuggerTreeNodeHyperlink link) { + node.setErrorMessage(errorMessage, link); + } + + @Override + public boolean isObsolete() { + return node.isObsolete(); + } }, myEvaluationContext); } }); diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java index 4114fc9b8ffe..2ae707d0f12c 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java @@ -56,13 +56,17 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.SpeedSearchComparator; import com.intellij.ui.TreeSpeedSearch; +import com.intellij.xdebugger.frame.XDebuggerTreeNodeHyperlink; +import com.intellij.xdebugger.frame.XValueChildrenList; import com.intellij.xdebugger.settings.XDebuggerSettingsManager; import com.sun.jdi.*; import com.sun.jdi.event.Event; import com.sun.jdi.event.ExceptionEvent; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.event.TreeModelEvent; @@ -576,9 +580,6 @@ public abstract class DebuggerTree extends DebuggerTreeBase implements DataProvi return (ValueDescriptorImpl)getNode().getDescriptor(); } - @Override - public void setRemaining(int remaining) {} - @Override public void initChildrenArrayRenderer(ArrayRenderer renderer) {} @@ -591,6 +592,38 @@ public abstract class DebuggerTree extends DebuggerTreeBase implements DataProvi } updateUI(false); } + + @Override + public void addChildren(@NotNull XValueChildrenList children, boolean last) { + } + + @Override + public void tooManyChildren(int remaining) { + } + + @Override + public void setAlreadySorted(boolean alreadySorted) { + } + + @Override + public void setErrorMessage(@NotNull String errorMessage) { + } + + @Override + public void setErrorMessage(@NotNull String errorMessage, @Nullable XDebuggerTreeNodeHyperlink link) { + } + + @Override + public void setMessage(@NotNull String message, + @Nullable Icon icon, + @NotNull SimpleTextAttributes attributes, + @Nullable XDebuggerTreeNodeHyperlink link) { + } + + @Override + public boolean isObsolete() { + return false; + } } private class BuildStaticNodeCommand extends BuildNodeCommand { diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ArrayRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ArrayRenderer.java index f246c3785e38..7f01e744dec8 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ArrayRenderer.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ArrayRenderer.java @@ -21,21 +21,25 @@ import com.intellij.debugger.DebuggerManagerEx; import com.intellij.debugger.actions.ArrayAction; import com.intellij.debugger.engine.ContextUtil; import com.intellij.debugger.engine.DebuggerManagerThreadImpl; +import com.intellij.debugger.engine.JavaValue; import com.intellij.debugger.engine.evaluation.EvaluateException; import com.intellij.debugger.engine.evaluation.EvaluationContext; import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; import com.intellij.debugger.engine.evaluation.TextWithImportsImpl; import com.intellij.debugger.impl.DebuggerUtilsEx; +import com.intellij.debugger.memory.utils.ErrorsValueGroup; import com.intellij.debugger.settings.NodeRendererSettings; import com.intellij.debugger.settings.ViewsGeneralSettings; import com.intellij.debugger.ui.impl.watch.ArrayElementDescriptorImpl; import com.intellij.debugger.ui.impl.watch.NodeManagerImpl; +import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl; import com.intellij.debugger.ui.tree.DebuggerTreeNode; import com.intellij.debugger.ui.tree.NodeDescriptor; import com.intellij.debugger.ui.tree.NodeDescriptorFactory; import com.intellij.debugger.ui.tree.ValueDescriptor; import com.intellij.icons.AllIcons; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.LanguageLevelProjectExtension; import com.intellij.openapi.util.DefaultJDOMExternalizer; import com.intellij.openapi.util.InvalidDataException; @@ -49,6 +53,7 @@ import com.intellij.ui.SimpleTextAttributes; import com.intellij.util.IncorrectOperationException; import com.intellij.xdebugger.XExpression; import com.intellij.xdebugger.frame.XDebuggerTreeNodeHyperlink; +import com.intellij.xdebugger.frame.XValueChildrenList; import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree; import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; import com.sun.jdi.ArrayReference; @@ -159,7 +164,7 @@ public class ArrayRenderer extends NodeRendererImpl{ builder.setMessage(DebuggerBundle.message("message.node.elements.null.hidden"), null, SimpleTextAttributes.REGULAR_ATTRIBUTES, null); } if (!myForced && END_INDEX < arrayLength - 1) { - builder.setRemaining(arrayLength - 1 - END_INDEX); + builder.tooManyChildren(arrayLength - 1 - END_INDEX); } } } @@ -252,6 +257,7 @@ public class ArrayRenderer extends NodeRendererImpl{ int added = 0; if (arrayLength - 1 >= START_INDEX) { + ErrorsValueGroup errorsGroup = null; for (int idx = START_INDEX; idx < arrayLength; idx++) { try { if (DebuggerUtilsEx.evaluateBoolean(cachedEvaluator.getEvaluator(evaluationContext.getProject()), @@ -269,7 +275,17 @@ public class ArrayRenderer extends NodeRendererImpl{ } } catch (EvaluateException e) { - builder.addChildren(Collections.singletonList(nodeManager.createMessageNode(e.getMessage())), false); + if (errorsGroup == null) { + errorsGroup = new ErrorsValueGroup(); + builder.addChildren(XValueChildrenList.bottomGroup(errorsGroup), false); + } + JavaValue childValue = JavaValue + .create(null, + (ValueDescriptorImpl)descriptorFactory.getArrayItemDescriptor(builder.getParentDescriptor(), array, idx), + ((EvaluationContextImpl)evaluationContext), + nodeManager, + false); + errorsGroup.addErrorValue(e.getMessage(), childValue); } } } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ChildrenBuilder.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ChildrenBuilder.java index 416089ed0de7..1e4b406add12 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ChildrenBuilder.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ChildrenBuilder.java @@ -15,16 +15,15 @@ */ package com.intellij.debugger.ui.tree.render; -import com.intellij.debugger.ui.tree.*; -import com.intellij.ui.SimpleTextAttributes; -import com.intellij.xdebugger.frame.XDebuggerTreeNodeHyperlink; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import com.intellij.debugger.ui.tree.DebuggerTreeNode; +import com.intellij.debugger.ui.tree.NodeDescriptorFactory; +import com.intellij.debugger.ui.tree.NodeManager; +import com.intellij.debugger.ui.tree.ValueDescriptor; +import com.intellij.xdebugger.frame.XCompositeNode; -import javax.swing.*; import java.util.List; -public interface ChildrenBuilder { +public interface ChildrenBuilder extends XCompositeNode { NodeDescriptorFactory getDescriptorManager(); NodeManager getNodeManager(); @@ -37,13 +36,10 @@ public interface ChildrenBuilder { setChildren(children); } - default void setMessage(@NotNull String message, - @Nullable Icon icon, - @NotNull SimpleTextAttributes attributes, - @Nullable XDebuggerTreeNodeHyperlink link) { + @Deprecated + default void setRemaining(int remaining) { + tooManyChildren(remaining); } - void setRemaining(int remaining); - void initChildrenArrayRenderer(ArrayRenderer renderer); } From f236ad72348d82cae5ccdd6c5515a3010483e37c Mon Sep 17 00:00:00 2001 From: Sergey Simonchik Date: Mon, 5 Jun 2017 13:06:38 +0300 Subject: [PATCH 011/136] semver: deprecate UNKNOWN as it's an invalid SemVer instance (use null for unknown semvers instead) --- platform/util/src/com/intellij/util/text/SemVer.java | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/util/src/com/intellij/util/text/SemVer.java b/platform/util/src/com/intellij/util/text/SemVer.java index 664aa224ab7d..347a1b07b11a 100644 --- a/platform/util/src/com/intellij/util/text/SemVer.java +++ b/platform/util/src/com/intellij/util/text/SemVer.java @@ -23,6 +23,7 @@ import org.jetbrains.annotations.Nullable; * Holds Semantic Version. */ public final class SemVer implements Comparable { + /** @deprecated */ public static final SemVer UNKNOWN = new SemVer("?", 0, 0, 0); private final String myRawVersion; From 32e3b8b73a1db66b169eb9939f52478aff5d5b3f Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Mon, 5 Jun 2017 13:42:30 +0300 Subject: [PATCH 012/136] convert to threadlocal: do not create initializer if variable was final (IDEA-173748) --- .../ConvertFieldToThreadLocalIntention.java | 4 +++- .../intentions/threadLocal/afterFinalField.java | 13 +++++++++++++ .../intentions/threadLocal/beforeFinalField.java | 8 ++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 java/typeMigration/testData/intentions/threadLocal/afterFinalField.java create mode 100644 java/typeMigration/testData/intentions/threadLocal/beforeFinalField.java diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ConvertFieldToThreadLocalIntention.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ConvertFieldToThreadLocalIntention.java index 6161a42af635..0e03d91dd617 100644 --- a/java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ConvertFieldToThreadLocalIntention.java +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ConvertFieldToThreadLocalIntention.java @@ -115,7 +115,9 @@ public class ConvertFieldToThreadLocalIntention extends PsiElementBaseIntentionA } PsiExpression initializer = psiField.getInitializer(); - if (initializer == null) { + + if (initializer == null && + !psiField.hasModifierProperty(PsiModifier.FINAL)) { final PsiType type = psiField.getType(); String initializerText = null; if (PsiType.BOOLEAN.equals(type)) { diff --git a/java/typeMigration/testData/intentions/threadLocal/afterFinalField.java b/java/typeMigration/testData/intentions/threadLocal/afterFinalField.java new file mode 100644 index 000000000000..9575f8c84842 --- /dev/null +++ b/java/typeMigration/testData/intentions/threadLocal/afterFinalField.java @@ -0,0 +1,13 @@ +// "Convert to ThreadLocal" "true" +class Foo { + private final ThreadLocal property; + + Foo(boolean property) { + this.property = new ThreadLocal() { + @Override + protected Boolean initialValue() { + return property; + } + }; + } +} \ No newline at end of file diff --git a/java/typeMigration/testData/intentions/threadLocal/beforeFinalField.java b/java/typeMigration/testData/intentions/threadLocal/beforeFinalField.java new file mode 100644 index 000000000000..1cffae690828 --- /dev/null +++ b/java/typeMigration/testData/intentions/threadLocal/beforeFinalField.java @@ -0,0 +1,8 @@ +// "Convert to ThreadLocal" "true" +class Foo { + private final boolean property; + + Foo(boolean property) { + this.property = property; + } +} \ No newline at end of file From 90ab867bad789fa307f8fa613e95a1c808a2b64b Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Mon, 5 Jun 2017 13:41:44 +0300 Subject: [PATCH 013/136] ui: fix fonts in "Usages Detected" dialog under HiDPI Linux --- .../com/intellij/refactoring/safeDelete/UnsafeUsagesDialog.java | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/lang-impl/src/com/intellij/refactoring/safeDelete/UnsafeUsagesDialog.java b/platform/lang-impl/src/com/intellij/refactoring/safeDelete/UnsafeUsagesDialog.java index 709ad66242e6..c1472c420881 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/safeDelete/UnsafeUsagesDialog.java +++ b/platform/lang-impl/src/com/intellij/refactoring/safeDelete/UnsafeUsagesDialog.java @@ -58,6 +58,7 @@ public class UnsafeUsagesDialog extends DialogWrapper { JPanel panel = new JPanel(new BorderLayout()); myMessagePane = new JEditorPane(UIUtil.HTML_MIME, ""); myMessagePane.setEditable(false); + myMessagePane.setEditorKit(UIUtil.getHTMLEditorKit()); JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(myMessagePane); scrollPane.setPreferredSize(JBUI.size(500, 400)); panel.add(new JLabel(RefactoringBundle.message("the.following.problems.were.found")), BorderLayout.NORTH); From 92c3daa01474c6693f0c23a6133ee0e12a4916f2 Mon Sep 17 00:00:00 2001 From: "Liana.Bakradze" Date: Mon, 5 Jun 2017 14:09:02 +0300 Subject: [PATCH 014/136] convert line separators in edu tests --- .../testSrc/com/jetbrains/edu/coursecreator/CCTestCase.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/educational-core/testSrc/com/jetbrains/edu/coursecreator/CCTestCase.java b/python/educational-core/testSrc/com/jetbrains/edu/coursecreator/CCTestCase.java index b9eed80db896..95be2c1581d0 100644 --- a/python/educational-core/testSrc/com/jetbrains/edu/coursecreator/CCTestCase.java +++ b/python/educational-core/testSrc/com/jetbrains/edu/coursecreator/CCTestCase.java @@ -12,6 +12,7 @@ import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.testFramework.EditorTestUtil; import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase; @@ -187,7 +188,7 @@ public abstract class CCTestCase extends LightPlatformCodeInsightFixtureTestCase public Pair> getPlaceholders(String name, boolean useLength, boolean removeMarkers) { try { - String text = FileUtil.loadFile(new File(getBasePath(), name)); + String text = StringUtil.convertLineSeparators(FileUtil.loadFile(new File(getBasePath(), name))); Document tempDocument = EditorFactory.getInstance().createDocument(text); if (removeMarkers) { EditorTestUtil.extractCaretAndSelectionMarkers(tempDocument); From 5b962a018fa623688e6ce29ef2a14736a84f43c8 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Mon, 5 Jun 2017 12:31:23 +0300 Subject: [PATCH 015/136] method return always constants (IDEA-173896) ensure external derivatives are not ignored, do not report on derivatives - report on api method, don't visit same methods multiple times --- .../siyeh/InspectionGadgetsBundle.properties | 2 +- .../MethodReturnAlwaysConstantInspection.java | 68 ++++++++++--------- .../MethodReturnAlwaysConstant.html | 2 +- 3 files changed, 38 insertions(+), 34 deletions(-) diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties index 3828297a94dd..d067787d0e95 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties @@ -1448,7 +1448,7 @@ integer.multiplication.implicit.cast.to.long.option=Ignore compile time co wait.or.await.without.timeout.display.name='wait()' or 'await()' without timeout wait.or.await.without.timeout.problem.descriptor=#ref without timeout #loc method.return.always.constant.display.name=Method returns per-class constant -method.return.always.constant.problem.descriptor=Method #ref() returns a per-class constant +method.return.always.constant.problem.descriptor=Method #ref() and all it's derivables always return constants class.with.too.many.dependencies.display.name=Class with too many dependencies class.with.too.many.dependencies.problem.descriptor=Class ''{0}'' has too many dependencies ({1} > {2}) class.with.too.many.transitive.dependencies.display.name=Class with too many transitive dependencies diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/MethodReturnAlwaysConstantInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/MethodReturnAlwaysConstantInspection.java index fbced3b0f8c0..b475ee5d422f 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/MethodReturnAlwaysConstantInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/MethodReturnAlwaysConstantInspection.java @@ -17,9 +17,7 @@ package com.siyeh.ig.classlayout; import com.intellij.analysis.AnalysisScope; import com.intellij.codeInspection.*; -import com.intellij.codeInspection.reference.RefEntity; -import com.intellij.codeInspection.reference.RefMethod; -import com.intellij.openapi.util.Key; +import com.intellij.codeInspection.reference.*; import com.intellij.psi.*; import com.intellij.psi.util.PsiUtil; import com.siyeh.InspectionGadgetsBundle; @@ -34,14 +32,10 @@ import java.util.Set; public class MethodReturnAlwaysConstantInspection extends BaseGlobalInspection { - private static final Key ALWAYS_CONSTANT = - Key.create("ALWAYS_CONSTANT"); - @NotNull @Override public String getDisplayName() { - return InspectionGadgetsBundle.message( - "method.return.always.constant.display.name"); + return InspectionGadgetsBundle.message("method.return.always.constant.display.name"); } @Override @@ -51,35 +45,24 @@ public class MethodReturnAlwaysConstantInspection extends BaseGlobalInspection { if (!(refEntity instanceof RefMethod)) { return null; } - final RefMethod refMethod = (RefMethod)refEntity; - final Boolean alreadyProcessed = refMethod.getUserData(ALWAYS_CONSTANT); - if (alreadyProcessed != null && alreadyProcessed.booleanValue()) { + //don't warn on overriders + if (((RefMethod)refEntity).hasSuperMethods()) { return null; } + final RefMethod refMethod = (RefMethod)refEntity; if (!(refMethod.getElement() instanceof PsiMethod)) { return null; } - final PsiMethod method = (PsiMethod)refMethod.getElement(); - if (method.getBody() == null) { - return null; //we'll catch it on another method - } - if (!alwaysReturnsConstant(method)) { - return null; - } - final Set siblingMethods = - MethodInheritanceUtils.calculateSiblingMethods(refMethod); - for (RefMethod siblingMethod : siblingMethods) { - final PsiMethod siblingPsiMethod = - (PsiMethod)siblingMethod.getElement(); - if (method.getBody() != null && - !alwaysReturnsConstant(siblingPsiMethod)) { + final Set allScopeInheritors = MethodInheritanceUtils.calculateSiblingMethods(refMethod); + for (RefMethod siblingMethod : allScopeInheritors) { + final PsiMethod siblingPsiMethod = (PsiMethod)siblingMethod.getElement(); + if (siblingPsiMethod.getBody() != null && !alwaysReturnsConstant(siblingPsiMethod)) { return null; } } final List out = new ArrayList<>(); - for (RefMethod siblingRefMethod : siblingMethods) { - final PsiMethod siblingMethod = - (PsiMethod)siblingRefMethod.getElement(); + for (RefMethod siblingRefMethod : allScopeInheritors) { + final PsiMethod siblingMethod = (PsiMethod)siblingRefMethod.getElement(); final PsiIdentifier identifier = siblingMethod.getNameIdentifier(); if (identifier == null) { continue; @@ -88,8 +71,6 @@ public class MethodReturnAlwaysConstantInspection extends BaseGlobalInspection { InspectionGadgetsBundle.message( "method.return.always.constant.problem.descriptor"), false, null, ProblemHighlightType.GENERIC_ERROR_OR_WARNING)); - siblingRefMethod.putUserData(ALWAYS_CONSTANT, - Boolean.valueOf(true)); } return out.toArray(new ProblemDescriptor[out.size()]); } @@ -100,9 +81,32 @@ public class MethodReturnAlwaysConstantInspection extends BaseGlobalInspection { if (!(statement instanceof PsiReturnStatement)) { return false; } - final PsiReturnStatement returnStatement = - (PsiReturnStatement)statement; + final PsiReturnStatement returnStatement = (PsiReturnStatement)statement; final PsiExpression value = returnStatement.getReturnValue(); return value != null && PsiUtil.isConstantExpression(value); } + + @Override + protected boolean queryExternalUsagesRequests(@NotNull final RefManager manager, @NotNull final GlobalJavaInspectionContext globalContext, + @NotNull final ProblemDescriptionsProcessor processor) { + manager.iterate(new RefJavaVisitor() { + @Override public void visitElement(@NotNull RefEntity refEntity) { + if (refEntity instanceof RefElement && processor.getDescriptions(refEntity) != null) { + refEntity.accept(new RefJavaVisitor() { + @Override public void visitMethod(@NotNull final RefMethod refMethod) { + globalContext.enqueueDerivedMethodsProcessor(refMethod, new GlobalJavaInspectionContext.DerivedMethodsProcessor() { + @Override + public boolean process(PsiMethod derivedMethod) { + processor.ignoreElement(refMethod); + return false; + } + }); + } + }); + } + } + }); + + return false; + } } diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/MethodReturnAlwaysConstant.html b/plugins/InspectionGadgets/src/inspectionDescriptions/MethodReturnAlwaysConstant.html index 05c7d7cbdbcc..a48b19716916 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/MethodReturnAlwaysConstant.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/MethodReturnAlwaysConstant.html @@ -1,6 +1,6 @@ -Reports methods which only ever return a constant. +Reports methods which only ever return a constant, probably different for different inheritors. Because this inspection requires global code analysis it is only available for Analyze|Inspect Code or Analyze|Run Inspection by Name and it will not report in the editor. From 5e2ab00618697ff312f1f6fc3d47485c6a17fa40 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Mon, 5 Jun 2017 12:57:34 +0300 Subject: [PATCH 016/136] code reuse --- .../com/intellij/codeInspection/MakeVoidQuickFix.java | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInspection/MakeVoidQuickFix.java b/java/java-impl/src/com/intellij/codeInspection/MakeVoidQuickFix.java index 95c2f9ab09b0..3d867c9174dd 100644 --- a/java/java-impl/src/com/intellij/codeInspection/MakeVoidQuickFix.java +++ b/java/java-impl/src/com/intellij/codeInspection/MakeVoidQuickFix.java @@ -83,18 +83,12 @@ public class MakeVoidQuickFix implements LocalQuickFix { for (final PsiMethod oMethod : OverridingMethodsSearch.search(psiMethod)) { replaceReturnStatements(oMethod); } - final PsiParameter[] params = psiMethod.getParameterList().getParameters(); - final ParameterInfoImpl[] infos = new ParameterInfoImpl[params.length]; - for (int i = 0; i < params.length; i++) { - PsiParameter param = params[i]; - infos[i] = new ParameterInfoImpl(i, param.getName(), param.getType()); - } - + final ChangeSignatureProcessor csp = new ChangeSignatureProcessor(project, psiMethod, false, null, psiMethod.getName(), PsiType.VOID, - infos); + ParameterInfoImpl.fromMethod(psiMethod)); csp.run(); } From 377520ade5cc4deaae2a3b9272578833ac199644 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Mon, 5 Jun 2017 13:08:40 +0300 Subject: [PATCH 017/136] intersection type validation fixed (IDEA-173900) take type parameter bounds into account --- .../resolve/graphInference/InferenceSession.java | 4 ++-- .../ValidIntersectionTypeWithCapturedBounds.java | 11 +++++++++++ .../daemon/lambda/GraphInferenceHighlightingTest.java | 1 + 3 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/ValidIntersectionTypeWithCapturedBounds.java diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java index ba58f4e48a3d..1addc14dfc38 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java @@ -1984,8 +1984,8 @@ public class InferenceSession { final PsiSubstitutor sSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(gClass, (PsiClassType)sBound); final PsiSubstitutor tSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(gClass, (PsiClassType)tBound); for (PsiTypeParameter typeParameter : gClass.getTypeParameters()) { - final PsiType sType = sSubstitutor.substitute(typeParameter); - final PsiType tType = tSubstitutor.substitute(typeParameter); + final PsiType sType = sSubstitutor.substituteWithBoundsPromotion(typeParameter); + final PsiType tType = tSubstitutor.substituteWithBoundsPromotion(typeParameter); final Pair typePair = Pair.create(sType, tType); if (!processor.process(typePair)) { return gClass; diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/ValidIntersectionTypeWithCapturedBounds.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/ValidIntersectionTypeWithCapturedBounds.java new file mode 100644 index 000000000000..d66c2f810a19 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/ValidIntersectionTypeWithCapturedBounds.java @@ -0,0 +1,11 @@ + +abstract class Bug { + { + D _m = m(); + } + + abstract > J m(); +} + +abstract class C { } +abstract class D extends C { } diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/lambda/GraphInferenceHighlightingTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/lambda/GraphInferenceHighlightingTest.java index 2c09c1102be5..645a9442f600 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/lambda/GraphInferenceHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/lambda/GraphInferenceHighlightingTest.java @@ -115,6 +115,7 @@ public class GraphInferenceHighlightingTest extends LightDaemonAnalyzerTestCase public void testPullUncheckedWarningNotionThroughNestedCalls() { doTest(); } public void testIDEA149774() { doTest(); } public void testDisjunctionTypes() { doTest(); } + public void testValidIntersectionTypeWithCapturedBounds() { doTest(); } public void testPushErasedStateToArguments() { doTest(); } public void testStopAtStandaloneConditional() { doTest(); } public void testTransitiveInferenceVariableDependencies() { doTest(); } From 893f16622644d3c44bcbd63ba069c949f32bceac Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Mon, 5 Jun 2017 13:18:52 +0300 Subject: [PATCH 018/136] log warning for invalid text e.g. with spaces --- .../daemon/impl/quickfix/QualifySuperArgumentFix.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/QualifySuperArgumentFix.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/QualifySuperArgumentFix.java index 6cfc3fc07868..b7ff8c8954f5 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/QualifySuperArgumentFix.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/QualifySuperArgumentFix.java @@ -20,6 +20,7 @@ import com.intellij.codeInsight.daemon.impl.HighlightInfo; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.refactoring.util.RefactoringChangeUtil; +import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; public class QualifySuperArgumentFix extends QualifyThisOrSuperArgumentFix { @@ -50,7 +51,14 @@ public class QualifySuperArgumentFix extends QualifyThisOrSuperArgumentFix { final PsiExpression superQualifierCopy = copy.getMethodExpression().getQualifierExpression(); LOG.assertTrue(superQualifierCopy != null); superQualifierCopy.delete(); - PsiMethod method = ((PsiMethodCallExpression)elementFactory.createExpressionFromText(copy.getText(), superClass)).resolveMethod(); + PsiMethod method; + try { + method = ((PsiMethodCallExpression)elementFactory.createExpressionFromText(copy.getText(), superClass)).resolveMethod(); + } + catch (IncorrectOperationException e) { + LOG.info(e); + return; + } if (method != null && !method.hasModifierProperty(PsiModifier.ABSTRACT)) { QuickFixAction.registerQuickFixAction(highlightInfo, new QualifySuperArgumentFix(expr, superClass)); } From 12c350758d2f4c2e198a563224f380cdc9388510 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Mon, 5 Jun 2017 14:00:14 +0300 Subject: [PATCH 019/136] Interface.super method calls: ensure valid qualifier (IDEA-173888) forbid calls to super methods which are overridden in direct superinterfaces --- .../daemon/impl/analysis/HighlightUtil.java | 8 ++++--- ...faceSuperMethodReferenceApplicability.java | 24 +++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java index af791a1cf50a..d58fe33d0cfd 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java @@ -1579,11 +1579,13 @@ public class HighlightUtil extends HighlightUtilBase { final PsiElement parent = expr.getParent(); final PsiElement resolved = parent instanceof PsiReferenceExpression ? ((PsiReferenceExpression)parent).resolve() : null; + PsiClass containingClass = + ObjectUtils.notNull(resolved instanceof PsiMethod ? ((PsiMethod)resolved).getContainingClass() : null, aClass); for (PsiClass superClass : classT.getSupers()) { - if (superClass.isInheritor(aClass, true)) { + if (superClass.isInheritor(containingClass, true)) { String cause = null; - if (superClass.isInterface()) { - cause = "redundant interface " + format(aClass) + " is extended by "; + if (superClass.isInheritor(aClass, true) && superClass.isInterface()) { + cause = "redundant interface " + format(containingClass) + " is extended by "; } else if (resolved instanceof PsiMethod && MethodSignatureUtil.findMethodBySuperMethod(superClass, (PsiMethod)resolved, true) != resolved) { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newMethodRef/TypeNameInterfaceSuperMethodReferenceApplicability.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newMethodRef/TypeNameInterfaceSuperMethodReferenceApplicability.java index 5fc439fd75e8..0a030b150e1e 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newMethodRef/TypeNameInterfaceSuperMethodReferenceApplicability.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newMethodRef/TypeNameInterfaceSuperMethodReferenceApplicability.java @@ -81,3 +81,27 @@ class InsideThisRxpression { public interface Bar extends Foo {} } +class SameDefaultMethodDifferentInheritors { + interface A { default void a() {} } + interface B extends A { default void a() {} } + interface B1 extends A { } + interface C extends A {} + + class Clazz implements B, C { + { + C.super.a(); + } + } + + class Clazz1 implements B1, C { + { + C.super.a(); + } + } + + class Clazz2 implements C { + { + C.super.a(); + } + } +} From 3e7cfef9d6cb6b4e5b934bba98dc41a2a8ad9b9b Mon Sep 17 00:00:00 2001 From: "Vladimir.Orlov" Date: Mon, 5 Jun 2017 14:43:05 +0300 Subject: [PATCH 020/136] IDEA-172677 MacOS: idea community: incorrect idea installer window opens on .dmg --- .../tools/mac/scripts/makedmg.sh | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/platform/build-scripts/tools/mac/scripts/makedmg.sh b/platform/build-scripts/tools/mac/scripts/makedmg.sh index ae3b2aad4b14..c525b8bf6f52 100644 --- a/platform/build-scripts/tools/mac/scripts/makedmg.sh +++ b/platform/build-scripts/tools/mac/scripts/makedmg.sh @@ -36,6 +36,26 @@ stat ${EXPLODED}/DSStorePlaceHolder echo "Creating unpacked r/w disk image ${VOLNAME}..." hdiutil create -srcfolder ./${EXPLODED} -volname "$VOLNAME" -anyowners -nospotlight -quiet -fs HFS+ -fsargs "-c c=64,a=16,e=16" -format UDRW $2.temp.dmg +# check if the image already mounted +if [ -d "/Volumes/$VOLNAME" ]; then + attempt=1 + limit=5 + while [ $attempt -le $limit ] + do + echo "/Volumes/$VOLNAME - the image is already mounted. This build will wait for unmount for 1 min (up to 5 times)." + sleep 60; + if [ -d "/Volumes/$VOLNAME" ]; then + let "attempt += 1" + if [ $attempt -eq $limit ]; then + echo "/Volumes/$VOLNAME - the image is still mounted. By the reason the build will be stopped." + rm -rf ${EXPLODED} + rm -f $2.temp.dmg + exit 1 + fi + fi + done +fi + # mount this image echo "Mounting unpacked r/w disk image..." device=$(hdiutil attach -readwrite -noverify -noautoopen $2.temp.dmg | egrep '^/dev/' | sed 1q | awk '{print $1.dmg}') From 17b1b6ae0f07dbbb150a278eda4d89e14529c797 Mon Sep 17 00:00:00 2001 From: Elizaveta Shashkova Date: Mon, 5 Jun 2017 15:48:49 +0300 Subject: [PATCH 021/136] Use soft wrapping for both editors (PY-14446) --- .../python/console/PydevConsoleRunnerImpl.java | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/python/src/com/jetbrains/python/console/PydevConsoleRunnerImpl.java b/python/src/com/jetbrains/python/console/PydevConsoleRunnerImpl.java index 39c85123ceb5..353a5a72c265 100644 --- a/python/src/com/jetbrains/python/console/PydevConsoleRunnerImpl.java +++ b/python/src/com/jetbrains/python/console/PydevConsoleRunnerImpl.java @@ -48,7 +48,6 @@ import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Caret; import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.EditorSettings; import com.intellij.openapi.editor.actionSystem.EditorAction; import com.intellij.openapi.editor.actionSystem.EditorWriteActionHandler; import com.intellij.openapi.editor.actions.SplitLineAction; @@ -784,7 +783,7 @@ public class PydevConsoleRunnerImpl implements PydevConsoleRunner { SoftWrapAction() { super(ActionsBundle.actionText("EditorToggleUseSoftWraps"), ActionsBundle.actionDescription("EditorToggleUseSoftWraps"), AllIcons.Actions.ToggleSoftWrap); - myConsoleView.getEditor().getSettings().setUseSoftWraps(isSelected); + updateEditors(); } @Override @@ -792,16 +791,15 @@ public class PydevConsoleRunnerImpl implements PydevConsoleRunner { return isSelected; } + private void updateEditors() { + myConsoleView.getEditor().getSettings().setUseSoftWraps(isSelected); + myConsoleView.getConsoleEditor().getSettings().setUseSoftWraps(isSelected); + } + @Override public void setSelected(AnActionEvent e, boolean state) { isSelected = state; - EditorSettings editorSettings = getConsoleView().getEditor().getSettings(); - if (isSelected) { - editorSettings.setUseSoftWraps(true); - } - else { - editorSettings.setUseSoftWraps(false); - } + updateEditors(); myConsoleSettings.setUseSoftWraps(isSelected); } } From 9be054ea9b69d74c355b4bc2c964fb1b643c504f Mon Sep 17 00:00:00 2001 From: "Gregory.Shrago" Date: Mon, 5 Jun 2017 16:10:49 +0300 Subject: [PATCH 022/136] EA-92305 - IOE: LocalFileSystemBase.createChildFile --- .../src/com/intellij/ide/scratch/ScratchFileServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/ide/scratch/ScratchFileServiceImpl.java b/platform/lang-impl/src/com/intellij/ide/scratch/ScratchFileServiceImpl.java index c46db97f7546..a991bb85fafe 100644 --- a/platform/lang-impl/src/com/intellij/ide/scratch/ScratchFileServiceImpl.java +++ b/platform/lang-impl/src/com/intellij/ide/scratch/ScratchFileServiceImpl.java @@ -289,7 +289,7 @@ public class ScratchFileServiceImpl extends ScratchFileService implements Persis return VfsUtil.createChildSequent(LocalFileSystem.getInstance(), dir, fileName, StringUtil.notNullize(ext)); } else { - return dir.createChildData(LocalFileSystem.getInstance(), fileNameExt); + return dir.findOrCreateChildData(LocalFileSystem.getInstance(), fileNameExt); } }); } From 071634d77eff05eead4266c466d974e6a3ce05b6 Mon Sep 17 00:00:00 2001 From: Pavel Dolgov Date: Mon, 5 Jun 2017 15:04:40 +0300 Subject: [PATCH 023/136] Java: Navigate to proper overload of a method being accessed via reflection (IDEA-172319) --- ...vaLangInvokeHandleSignatureInspection.java | 2 +- .../JavaReflectionInvocationInspection.java | 35 ++++++--- .../JavaReflectionMemberAccessInspection.java | 5 ++ .../impl/JavaLangClassMemberReference.java | 47 ++++++++---- .../impl/JavaLangInvokeHandleReference.java | 25 ++++++- .../JavaLangInvokeHandleNavigationTest.kt | 64 ++++++++++++++++- .../JavaReflectionNavigationTest.java | 71 +++++++++++++++++-- 7 files changed, 217 insertions(+), 32 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaLangInvokeHandleSignatureInspection.java b/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaLangInvokeHandleSignatureInspection.java index 4db61720ca88..e71c07de7568 100644 --- a/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaLangInvokeHandleSignatureInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaLangInvokeHandleSignatureInspection.java @@ -309,7 +309,7 @@ public class JavaLangInvokeHandleSignatureInspection extends BaseJavaBatchLocalI * from arguments of MethodType.methodType(Class...) and MethodType.genericMethodType(int, boolean?) */ @Nullable - private static ReflectiveSignature composeMethodSignature(@Nullable PsiExpression methodTypeExpression) { + public static ReflectiveSignature composeMethodSignature(@Nullable PsiExpression methodTypeExpression) { final PsiExpression typeDefinition = findDefinition(methodTypeExpression); if (typeDefinition instanceof PsiMethodCallExpression) { final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)typeDefinition; diff --git a/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaReflectionInvocationInspection.java b/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaReflectionInvocationInspection.java index 04bd9a3dca84..6f3515fceafe 100644 --- a/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaReflectionInvocationInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaReflectionInvocationInspection.java @@ -20,6 +20,7 @@ import com.intellij.codeInspection.InspectionsBundle; import com.intellij.codeInspection.ProblemsHolder; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.containers.ContainerUtil; import com.siyeh.ig.psiutils.ParenthesesUtils; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; @@ -131,22 +132,34 @@ public class JavaReflectionInvocationInspection extends BaseJavaBatchLocalInspec if (definition instanceof PsiMethodCallExpression) { final PsiMethodCallExpression definitionCall = (PsiMethodCallExpression)definition; if (methodPredicate.test(definitionCall)) { - final PsiExpression[] arguments = definitionCall.getArgumentList().getExpressions(); - - if (arguments.length == argumentOffset + 1) { - final PsiExpression[] arrayElements = getVarargAsArray(arguments[argumentOffset]); - if (arrayElements != null) { - return Arrays.asList(arrayElements); - } - } - if (arguments.length >= argumentOffset) { - return Arrays.asList(arguments).subList(argumentOffset, arguments.length); - } + return getRequiredMethodArguments(definitionCall, argumentOffset); } } return null; } + private static List getRequiredMethodArguments(@NotNull PsiMethodCallExpression definitionCall, int argumentOffset) { + final PsiExpression[] arguments = definitionCall.getArgumentList().getExpressions(); + + if (arguments.length == argumentOffset + 1) { + final PsiExpression[] arrayElements = getVarargAsArray(arguments[argumentOffset]); + if (arrayElements != null) { + return Arrays.asList(arrayElements); + } + } + if (arguments.length >= argumentOffset) { + return Arrays.asList(arguments).subList(argumentOffset, arguments.length); + } + return null; + } + + @Nullable + public static List getReflectionMethodParameterTypes(@NotNull PsiMethodCallExpression definitionCall, + int argumentOffset) { + List arguments = getRequiredMethodArguments(definitionCall, argumentOffset); + return arguments != null ? ContainerUtil.map(arguments, type -> getReflectiveType(type)) : null; + } + @Nullable static Arguments getActualMethodArguments(PsiExpression[] arguments, int argumentOffset, boolean allowVarargAsArray) { if (allowVarargAsArray && arguments.length == argumentOffset + 1) { diff --git a/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaReflectionMemberAccessInspection.java b/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaReflectionMemberAccessInspection.java index 951770eb6d56..90213c251cfa 100644 --- a/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaReflectionMemberAccessInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaReflectionMemberAccessInspection.java @@ -279,6 +279,11 @@ public class JavaReflectionMemberAccessInspection extends BaseJavaBatchLocalInsp final List argumentTypes = ContainerUtil.map(methodArguments.expressions, JavaReflectionReferenceUtil::getReflectiveType); + return matchMethod(methods, argumentTypes); + } + + @Nullable + public static PsiMethod matchMethod(@NotNull PsiMethod[] methods, @NotNull List argumentTypes) { int mismatchCount = Integer.MAX_VALUE; PsiMethod bestGuess = null; for (PsiMethod method : methods) { diff --git a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaLangClassMemberReference.java b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaLangClassMemberReference.java index 67629f036bce..00ba95c77149 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaLangClassMemberReference.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaLangClassMemberReference.java @@ -18,9 +18,11 @@ package com.intellij.psi.impl.source.resolve.reference.impl; import com.intellij.codeInsight.completion.InsertHandler; import com.intellij.codeInsight.completion.InsertionContext; import com.intellij.codeInsight.lookup.LookupElement; -import com.intellij.codeInsight.lookup.LookupElementBuilder; +import com.intellij.codeInspection.reflectiveAccess.JavaReflectionInvocationInspection; +import com.intellij.codeInspection.reflectiveAccess.JavaReflectionMemberAccessInspection; import com.intellij.psi.*; import com.intellij.psi.util.MethodSignatureBackedByPsiMethod; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.ContainerUtil; import gnu.trove.THashSet; @@ -28,10 +30,7 @@ import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Arrays; -import java.util.Comparator; -import java.util.Objects; -import java.util.Set; +import java.util.*; import static com.intellij.psi.impl.source.resolve.reference.impl.JavaReflectionReferenceUtil.*; @@ -73,17 +72,29 @@ public class JavaLangClassMemberReference extends PsiReferenceBase isRegularMethod(method) && isPublic(method)); - if (publicMethod != null) { - return publicMethod; + PsiMethod[] methods = psiClass.findMethodsByName(name, true); + if (methods.length > 1) { + methods = + ContainerUtil.filter(methods, method -> isRegularMethod(method) && isPublic(method)) + .toArray(PsiMethod.EMPTY_ARRAY); + if (methods.length > 1) { + return findOverloadedMethod(methods); + } } - return ContainerUtil.find(methods, method -> isRegularMethod(method)); + return methods.length != 0 ? methods[0] : null; } case GET_DECLARED_METHOD: { - final PsiMethod[] methods = psiClass.findMethodsByName(name, false); - return ContainerUtil.find(methods, method -> isRegularMethod(method) && isPotentiallyAccessible(method, psiClass)); + PsiMethod[] methods = psiClass.findMethodsByName(name, false); + if (methods.length > 1) { + methods = + ContainerUtil.filter(methods, method -> isRegularMethod(method) && isPotentiallyAccessible(method, psiClass)) + .toArray(PsiMethod.EMPTY_ARRAY); + if (methods.length > 1) { + return findOverloadedMethod(methods); + } + } + return methods.length != 0 ? methods[0] : null; } } } @@ -155,6 +166,18 @@ public class JavaLangClassMemberReference extends PsiReferenceBase parameterTypes = JavaReflectionInvocationInspection.getReflectionMethodParameterTypes(definitionCall, 1); + if (parameterTypes != null) { + return JavaReflectionMemberAccessInspection.matchMethod(methods, parameterTypes); + } + } + return null; + } + @Override public void handleInsert(InsertionContext context, LookupElement item) { final Object object = item.getObject(); diff --git a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaLangInvokeHandleReference.java b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaLangInvokeHandleReference.java index 838ee3abe01e..2574d768a80e 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaLangInvokeHandleReference.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaLangInvokeHandleReference.java @@ -19,13 +19,16 @@ import com.intellij.codeInsight.completion.InsertHandler; import com.intellij.codeInsight.completion.InsertionContext; import com.intellij.codeInsight.completion.JavaLookupElementBuilder; import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.codeInspection.reflectiveAccess.JavaLangInvokeHandleSignatureInspection; import com.intellij.openapi.util.Condition; import com.intellij.psi.*; import com.intellij.psi.util.MethodSignatureBackedByPsiMethod; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; import com.intellij.util.ProcessingContext; import com.intellij.util.containers.ContainerUtil; +import com.siyeh.ig.psiutils.ParenthesesUtils; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -97,9 +100,25 @@ public class JavaLangInvokeHandleReference extends PsiReferenceBase filter) { - final PsiMethod[] methods = psiClass.findMethodsByName(name, true); - return ContainerUtil.find(methods, filter); + private PsiElement resolveMethod(@NotNull String name, @NotNull PsiClass psiClass, Condition filter) { + PsiMethod[] methods = psiClass.findMethodsByName(name, true); + if (methods.length != 0) { + methods = ContainerUtil.filter(methods, filter).toArray(PsiMethod.EMPTY_ARRAY); + if (methods.length > 1) { + final PsiMethodCallExpression definitionCall = PsiTreeUtil.getParentOfType(myElement, PsiMethodCallExpression.class); + if (definitionCall != null) { + final PsiExpression[] arguments = definitionCall.getArgumentList().getExpressions(); + if (arguments.length > 2) { + final PsiExpression typeExpression = ParenthesesUtils.stripParentheses(arguments[2]); + final ReflectiveSignature expectedSignature = JavaLangInvokeHandleSignatureInspection.composeMethodSignature(typeExpression); + if (expectedSignature != null) { + return ContainerUtil.find(methods, method -> expectedSignature.equals(getMethodSignature(method))); + } + } + } + } + } + return methods.length != 0 ? methods[0] : null; } @NotNull diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/navigation/JavaLangInvokeHandleNavigationTest.kt b/java/java-tests/testSrc/com/intellij/java/codeInsight/navigation/JavaLangInvokeHandleNavigationTest.kt index 73e7aa0e2f4c..b8f0b87796bb 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/navigation/JavaLangInvokeHandleNavigationTest.kt +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/navigation/JavaLangInvokeHandleNavigationTest.kt @@ -16,10 +16,13 @@ package com.intellij.java.codeInsight.navigation import com.intellij.psi.PsiMember +import com.intellij.psi.PsiMethod import com.intellij.psi.PsiReference import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase import junit.framework.TestCase +import org.intellij.lang.annotations.Language import org.intellij.lang.annotations.MagicConstant +import org.jetbrains.annotations.NonNls /** * @author Pavel.Dolgov @@ -74,6 +77,64 @@ class JavaLangInvokeHandleNavigationTest : LightCodeInsightFixtureTestCase() { fun testStaticSetter6() = doNegativeTest("pf1", STATIC_SETTER) fun testStaticSetter7() = doNegativeTest("m1", STATIC_SETTER) + fun testOverloadedBothPublic() = doTestOverloaded( + """public class Overloaded { + public void foo(int n) {} + public void foo(String s) {} +}""", VIRTUAL, "java.lang.String") + + fun testOverloadedFirstPublic() = doTestOverloaded( + """public class Overloaded { + public void foo(int n) {} + void foo(String s) {} +}""", VIRTUAL, "int") + + fun testOverloadedSecondPublic() = doTestOverloaded( + """public class Overloaded { + void foo(int n) {} + public void foo(String s) {} +}""", VIRTUAL, "java.lang.String") + + fun testOverloadedInherited() { + myFixture.addClass("""public class OverloadedParent { + public static void foo(String s) {} +}""") + + doTestOverloaded( + """public class Overloaded extends OverloadedParent { + public static void foo(int n) {} +}""", STATIC, "java.lang.String") + } + + fun testOverloadedStatic() = doTestOverloaded( + """public class Overloaded { + public static void foo(int n) {} + public static void foo(String s) {} +}""", STATIC, "java.lang.String") + + + private fun doTestOverloaded(@NonNls @Language("JAVA") classText: String, function: String, vararg expectedParameterTypes: String) { + myFixture.addClass(classText) + + val methodType = arrayOf("void", *expectedParameterTypes).map { "$it.class" }.joinToString(", ") + val member = doTestImpl("foo", """ +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +class Main { + void foo() throws ReflectiveOperationException { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + lookup.$function(Overloaded.class, "foo", MethodType.methodType($methodType)); + } +}""") + + TestCase.assertTrue("Is method", member is PsiMethod) + val parameters = (member as PsiMethod).parameterList.parameters + TestCase.assertEquals("Parameter count", expectedParameterTypes.size, parameters.size) + for (i in 0 until expectedParameterTypes.size) { + TestCase.assertEquals("Parameter $i", expectedParameterTypes[i], parameters[i].type.canonicalText) + } + } + private fun doTest(name: String, @MagicConstant(stringValues = arrayOf(VIRTUAL, STATIC, SPECIAL, @@ -83,7 +144,7 @@ class JavaLangInvokeHandleNavigationTest : LightCodeInsightFixtureTestCase() { doTestImpl(name, getMainClassText(name, function)) } - private fun doTestImpl(name: String, mainClassText: String) { + private fun doTestImpl(name: String, mainClassText: String): PsiMember { val reference = getReference(mainClassText) TestCase.assertEquals("Reference text", name, reference.canonicalText) val resolved = reference.resolve() @@ -91,6 +152,7 @@ class JavaLangInvokeHandleNavigationTest : LightCodeInsightFixtureTestCase() { TestCase.assertTrue("Target is a member", resolved is PsiMember) val member = resolved as PsiMember? TestCase.assertEquals("Target name", name, member!!.name) + return member } private fun doNegativeTest(name: String, diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/navigation/JavaReflectionNavigationTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/navigation/JavaReflectionNavigationTest.java index e904b9b9deb1..1102cb973937 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/navigation/JavaReflectionNavigationTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/navigation/JavaReflectionNavigationTest.java @@ -15,9 +15,7 @@ */ package com.intellij.java.codeInsight.navigation; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiMember; -import com.intellij.psi.PsiReference; +import com.intellij.psi.*; import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; import org.intellij.lang.annotations.Language; import org.intellij.lang.annotations.MagicConstant; @@ -93,6 +91,70 @@ public class JavaReflectionNavigationTest extends LightCodeInsightFixtureTestCas "}"); } + public void testOverloadedMethodBothPublic() { + doTestOverloadedMethod("foo", + "class Overloaded {\n" + + " public void foo() {}\n" + + " public void foo(String s) {}\n" + + "}", false, "java.lang.String"); + } + + public void testOverloadedMethodPublicFirst() { + doTestOverloadedMethod("foo", + "class Overloaded {\n" + + " public void foo() {}\n" + + " void foo(String s) {}\n" + + "}", false); + } + + public void testOverloadedMethodPublicSecond() { + doTestOverloadedMethod("foo", + "class Overloaded {\n" + + " void foo() {}\n" + + " public void foo(String s) {}\n" + + "}", false, "java.lang.String"); + } + + public void testOverloadedDeclaredMethod() { + doTestOverloadedMethod("foo", + "class Overloaded {\n" + + " public void foo() {}\n" + + " public void foo(String s) {}\n" + + "}", true, "java.lang.String"); + } + + public void testOverloadedInheritedMethod() { + doTestOverloadedMethod("bar", + "class OverloadedParent {" + + " public void bar(String s) {}\n" + + "}" + + "" + + "class Overloaded extends OverloadedParent {\n" + + " public void bar() {}\n" + + "}", false, "java.lang.String"); + } + + private void doTestOverloadedMethod(String name, + @NotNull @NonNls @Language("JAVA") String classText, + boolean isDeclared, + String... expectedParameterTypes) { + myFixture.addClass(classText); + + PsiMember member = doTestImpl(name, + "class Main {" + + " void main() {" + + " Overloaded.class.get" + (isDeclared?"Declared":"") + "Method(\""+name+"\", String.class);" + + " }" + + "}"); + assertTrue("Target is a method", member instanceof PsiMethod); + PsiMethod method = (PsiMethod)member; + PsiParameter[] parameters = method.getParameterList().getParameters(); + assertEquals("Parameter count", expectedParameterTypes.length, parameters.length); + for (int i = 0; i < expectedParameterTypes.length; i++) { + assertEquals("Parameter type " + i, expectedParameterTypes[i], parameters[0].getType().getCanonicalText()); + } + } + private void doTest(String name, @MagicConstant(stringValues = {FIELD, METHOD, DF, DM}) String type) { @@ -104,7 +166,7 @@ public class JavaReflectionNavigationTest extends LightCodeInsightFixtureTestCas doTestImpl(name, mainClassText); } - private void doTestImpl(String name, String mainClassText) { + private PsiMember doTestImpl(String name, @NotNull @NonNls @Language("JAVA") String mainClassText) { PsiReference reference = getReference(mainClassText); assertEquals("Reference text", name, reference.getCanonicalText()); PsiElement resolved = reference.resolve(); @@ -112,6 +174,7 @@ public class JavaReflectionNavigationTest extends LightCodeInsightFixtureTestCas assertTrue("Target is a member", resolved instanceof PsiMember); PsiMember member = (PsiMember)resolved; assertEquals("Target name", name, member.getName()); + return member; } private void doNegativeTest(String name, From 6b7f8028db789cf2849fa7ca9dfe5ac8847dee42 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Mon, 5 Jun 2017 15:44:59 +0300 Subject: [PATCH 024/136] AIOOBE (IDEA-173816) --- .../ipp/functional/ExtractToMethodReferenceIntention.java | 2 +- .../extractToMethodReference/EmptyCodeBlock.java | 5 +++++ .../extractToMethodReference/EmptyCodeBlock_after.java | 8 ++++++++ .../ipp/functional/ExtractToMethodReferenceTest.java | 4 ++++ 4 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/functional/extractToMethodReference/EmptyCodeBlock.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/functional/extractToMethodReference/EmptyCodeBlock_after.java diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/functional/ExtractToMethodReferenceIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/functional/ExtractToMethodReferenceIntention.java index 065bfd5899af..294f6f5074e9 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/functional/ExtractToMethodReferenceIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/functional/ExtractToMethodReferenceIntention.java @@ -130,7 +130,7 @@ public class ExtractToMethodReferenceIntention extends BaseElementAtCaretIntenti targetMethodName + parameters, targetClass); PsiCodeBlock targetMethodBody = emptyMethod.getBody(); LOG.assertTrue(targetMethodBody != null); - targetMethodBody.addRange(elements[0], elements[elements.length - 1]); + if (elements.length > 0) targetMethodBody.addRange(elements[0], elements[elements.length - 1]); PsiMethod method = (PsiMethod)CodeStyleManager.getInstance(project).reformat(JavaCodeStyleManager.getInstance(project).shortenClassReferences(targetClass.add(emptyMethod))); PsiMethodReferenceExpression methodReference = diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/functional/extractToMethodReference/EmptyCodeBlock.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/functional/extractToMethodReference/EmptyCodeBlock.java new file mode 100644 index 000000000000..bf1787f705eb --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/functional/extractToMethodReference/EmptyCodeBlock.java @@ -0,0 +1,5 @@ +class B { + { + Runnable r = () -> {} ; + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/functional/extractToMethodReference/EmptyCodeBlock_after.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/functional/extractToMethodReference/EmptyCodeBlock_after.java new file mode 100644 index 000000000000..8ab95092e18a --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/functional/extractToMethodReference/EmptyCodeBlock_after.java @@ -0,0 +1,8 @@ +class B { + { + Runnable r = B::run; + } + + private static void run() { + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/functional/ExtractToMethodReferenceTest.java b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/functional/ExtractToMethodReferenceTest.java index 5be4b04af3bf..c594a70339ac 100644 --- a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/functional/ExtractToMethodReferenceTest.java +++ b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/functional/ExtractToMethodReferenceTest.java @@ -54,6 +54,10 @@ public class ExtractToMethodReferenceTest extends IPPTestCase { assertIntentionNotAvailable(); } + public void testEmptyCodeBlock() throws Exception { + doTest(); + } + public void testUsedLocalVariables() throws Exception { assertIntentionNotAvailable(); } From 02bc8de1f4952ab4a6c3618a78ba4c26e953c32d Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Mon, 5 Jun 2017 16:25:39 +0300 Subject: [PATCH 025/136] wrap with explicit array: disable for non-reifiable types (IDEA-173825) --- .../ipp/varargs/VarargArgumentsPredicate.java | 31 +++++++++---------- ...gumentsWithExplicitArrayIntentionTest.java | 12 +++++++ 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/varargs/VarargArgumentsPredicate.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/varargs/VarargArgumentsPredicate.java index bc9fc916519b..d2fb11276c48 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/varargs/VarargArgumentsPredicate.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/varargs/VarargArgumentsPredicate.java @@ -15,7 +15,9 @@ */ package com.siyeh.ipp.varargs; +import com.intellij.codeInsight.daemon.impl.analysis.JavaGenericsUtil; import com.intellij.psi.*; +import com.intellij.psi.util.PsiTypesUtil; import com.intellij.psi.util.PsiUtil; import com.siyeh.ipp.base.PsiElementPredicate; import org.jetbrains.annotations.NotNull; @@ -48,29 +50,22 @@ class VarargArgumentsPredicate implements PsiElementPredicate { // "Unnecessarily qualified static usage" inspection // the psi gets into a bad state, this guards against that. // http://www.jetbrains.net/jira/browse/IDEADEV-40124 - final PsiReferenceExpression methodExpression = - methodCallExpression.getMethodExpression(); - final PsiExpression qualifier = - methodExpression.getQualifierExpression(); + final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); + final PsiExpression qualifier = methodExpression.getQualifierExpression(); if (qualifier == null) { - final PsiReferenceParameterList typeParameterList = - methodExpression.getParameterList(); + final PsiReferenceParameterList typeParameterList = methodExpression.getParameterList(); if (typeParameterList != null) { - final PsiTypeElement[] typeParameterElements = - typeParameterList.getTypeParameterElements(); + final PsiTypeElement[] typeParameterElements = typeParameterList.getTypeParameterElements(); if (typeParameterElements.length > 0) { return false; } } } - final PsiParameter[] parameters = parameterList.getParameters(); - final PsiParameter lastParameter = parameters[parameters.length - 1]; - final PsiEllipsisType lastParameterType = (PsiEllipsisType)lastParameter.getType(); - final PsiType lastType = lastParameterType.getComponentType(); final JavaResolveResult resolveResult = methodCallExpression.resolveMethodGenerics(); final PsiSubstitutor substitutor = resolveResult.getSubstitutor(); - final PsiType substitutedType = substitutor.substitute(lastType); + PsiType lastParameterType = PsiTypesUtil.getParameterType(parameterList.getParameters(), parametersCount - 1, true); + final PsiType substitutedType = substitutor.substitute(lastParameterType); if (substitutedType instanceof PsiCapturedWildcardType) { final PsiCapturedWildcardType capturedWildcardType = (PsiCapturedWildcardType)substitutedType; if (!capturedWildcardType.getWildcard().isSuper()) { @@ -78,13 +73,15 @@ class VarargArgumentsPredicate implements PsiElementPredicate { return false; } } + + if (!JavaGenericsUtil.isReifiableType(substitutedType)) { + return false; + } if (arguments.length != parametersCount) { return true; } - final PsiExpression lastExpression = - arguments[arguments.length - 1]; - final PsiExpression expression = PsiUtil.deparenthesizeExpression( - lastExpression); + final PsiExpression lastExpression = arguments[arguments.length - 1]; + final PsiExpression expression = PsiUtil.deparenthesizeExpression(lastExpression); if (expression instanceof PsiLiteralExpression) { final String text = expression.getText(); if ("null".equals(text)) { diff --git a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/vararg/WrapVarargArgumentsWithExplicitArrayIntentionTest.java b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/vararg/WrapVarargArgumentsWithExplicitArrayIntentionTest.java index fc355c2f8579..4f811c316cc4 100644 --- a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/vararg/WrapVarargArgumentsWithExplicitArrayIntentionTest.java +++ b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/vararg/WrapVarargArgumentsWithExplicitArrayIntentionTest.java @@ -58,4 +58,16 @@ public class WrapVarargArgumentsWithExplicitArrayIntentionTest extends IPPTestCa " }\n" + "}"); } + + public void testNonReifiable() { + doTestIntentionNotAvailable( + "" + + "import java.util.*;\n" + + "class Y {\n" + + " void m(Set... t){}\n" + + " public static void run(Set s) {\n" + + " m(/*_Wrap vararg arguments with explicit array creation*/s);\n" + + " }\n" + + "}"); + } } From dc610f3287df866af06a90a39270bcc214951f49 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Mon, 5 Jun 2017 16:42:15 +0300 Subject: [PATCH 026/136] split into declaration and assignment for fields (IDEA-173822) ensure field is not used before existing class initializer --- ...DeclarationAndInitializationIntention.java | 54 ++++++++++--------- .../FieldUsedBeforeInitializer.java | 8 +++ .../FieldUsedBeforeInitializer_after.java | 13 +++++ ...arationAndInitializationIntentionTest.java | 1 + 4 files changed, 50 insertions(+), 26 deletions(-) create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/initialization/FieldUsedBeforeInitializer.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/initialization/FieldUsedBeforeInitializer_after.java diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/initialization/SplitDeclarationAndInitializationIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/initialization/SplitDeclarationAndInitializationIntention.java index aebb8f568814..c7f1a29a45aa 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/initialization/SplitDeclarationAndInitializationIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/initialization/SplitDeclarationAndInitializationIntention.java @@ -16,8 +16,11 @@ package com.siyeh.ipp.initialization; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleManager; +import com.intellij.psi.search.LocalSearchScope; +import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.refactoring.util.RefactoringUtil; import com.siyeh.IntentionPowerPackBundle; import com.siyeh.ipp.base.Intention; @@ -26,6 +29,8 @@ import com.siyeh.ipp.psiutils.HighlightUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import java.util.function.Predicate; + public class SplitDeclarationAndInitializationIntention extends Intention { @Override @@ -46,58 +51,55 @@ public class SplitDeclarationAndInitializationIntention extends Intention { if (containingClass == null) { return; } - final boolean fieldIsStatic = - field.hasModifierProperty(PsiModifier.STATIC); - final PsiClassInitializer[] classInitializers = - containingClass.getInitializers(); + final boolean fieldIsStatic = field.hasModifierProperty(PsiModifier.STATIC); + final PsiClassInitializer[] classInitializers = containingClass.getInitializers(); PsiClassInitializer classInitializer = null; final int fieldOffset = field.getTextOffset(); for (PsiClassInitializer existingClassInitializer : classInitializers) { - final int initializerOffset = - existingClassInitializer.getTextOffset(); + final int initializerOffset = existingClassInitializer.getTextOffset(); if (initializerOffset <= fieldOffset) { continue; } - final boolean initializerIsStatic = - existingClassInitializer.hasModifierProperty( - PsiModifier.STATIC); + final boolean initializerIsStatic = existingClassInitializer.hasModifierProperty(PsiModifier.STATIC); if (initializerIsStatic == fieldIsStatic) { - classInitializer = existingClassInitializer; - break; + Predicate usedBeforeInitializer = ref -> { + PsiElement refElement = ref.getElement(); + if (refElement == null) { + return true; + } + TextRange textRange = refElement.getTextRange(); + return textRange == null || textRange.getStartOffset() < initializerOffset; + }; + if (ReferencesSearch.search(field, new LocalSearchScope(containingClass)).findAll().stream().noneMatch(usedBeforeInitializer)) { + classInitializer = existingClassInitializer; + break; + } } } final PsiManager manager = field.getManager(); final Project project = manager.getProject(); - final PsiElementFactory elementFactory = - JavaPsiFacade.getInstance(project).getElementFactory(); + final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(project).getElementFactory(); if (classInitializer == null) { classInitializer = elementFactory.createClassInitializer(); - classInitializer = (PsiClassInitializer) - containingClass.addAfter(classInitializer, field); + classInitializer = (PsiClassInitializer)containingClass.addAfter(classInitializer, field); // add some whitespace between the field and the class initializer - final PsiElement whitespace = - PsiParserFacade.SERVICE.getInstance(project).createWhiteSpaceFromText("\n"); + final PsiElement whitespace = PsiParserFacade.SERVICE.getInstance(project).createWhiteSpaceFromText("\n"); containingClass.addAfter(whitespace, field); } final PsiCodeBlock body = classInitializer.getBody(); - @NonNls final String initializationStatementText = - field.getName() + " = " + initializerText + ';'; - final PsiExpressionStatement statement = - (PsiExpressionStatement)elementFactory.createStatementFromText( - initializationStatementText, body); + @NonNls final String initializationStatementText = field.getName() + " = " + initializerText + ';'; + final PsiExpressionStatement statement = (PsiExpressionStatement)elementFactory.createStatementFromText(initializationStatementText, body); final PsiElement addedElement = body.add(statement); if (fieldIsStatic) { - final PsiModifierList modifierList = - classInitializer.getModifierList(); + final PsiModifierList modifierList = classInitializer.getModifierList(); if (modifierList != null) { modifierList.setModifierProperty(PsiModifier.STATIC, true); } } initializer.delete(); CodeStyleManager.getInstance(manager.getProject()).reformat(classInitializer); - HighlightUtil.highlightElement(addedElement, - IntentionPowerPackBundle.message( + HighlightUtil.highlightElement(addedElement, IntentionPowerPackBundle.message( "press.escape.to.remove.highlighting.message")); } } \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/initialization/FieldUsedBeforeInitializer.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/initialization/FieldUsedBeforeInitializer.java new file mode 100644 index 000000000000..594414f839bb --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/initialization/FieldUsedBeforeInitializer.java @@ -0,0 +1,8 @@ +class Foo { + static final int f1 = 2; + static final int f2 = f1 + 1; + + static { + System.out.println(); + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/initialization/FieldUsedBeforeInitializer_after.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/initialization/FieldUsedBeforeInitializer_after.java new file mode 100644 index 000000000000..1697906d1cb0 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/initialization/FieldUsedBeforeInitializer_after.java @@ -0,0 +1,13 @@ +class Foo { + static final int f1; + + static { + f1 = 2; + } + + static final int f2 = f1 + 1; + + static { + System.out.println(); + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/initialization/SplitDeclarationAndInitializationIntentionTest.java b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/initialization/SplitDeclarationAndInitializationIntentionTest.java index 933d5e2e7964..e8c5659abdd4 100644 --- a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/initialization/SplitDeclarationAndInitializationIntentionTest.java +++ b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/initialization/SplitDeclarationAndInitializationIntentionTest.java @@ -26,6 +26,7 @@ public class SplitDeclarationAndInitializationIntentionTest extends IPPTestCase public void testArrayInitializer() { doTest(); } public void testArray() { doTest(); } + public void testFieldUsedBeforeInitializer() { doTest(); } @Override protected String getRelativePath() { From 0d1b2368ed8eb5a2db6a9d7f2b8fc1680ad633ad Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Mon, 5 Jun 2017 17:10:17 +0300 Subject: [PATCH 027/136] disable concatenation intentions in annotation methods (IDEA-173817) --- .../concatenation/SimpleStringConcatenationPredicate.java | 5 ++++- .../ConstantRequiredInsideAnnotationMethod.java | 5 +++++ .../ReplaceConcatenationWithStringBufferIntentionTest.java | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/concatenation/string_builder/ConstantRequiredInsideAnnotationMethod.java diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/SimpleStringConcatenationPredicate.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/SimpleStringConcatenationPredicate.java index 1b5e41ed0fb7..8393cbd955bf 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/SimpleStringConcatenationPredicate.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/SimpleStringConcatenationPredicate.java @@ -16,7 +16,9 @@ package com.siyeh.ipp.concatenation; import com.intellij.codeInsight.AnnotationUtil; +import com.intellij.psi.PsiAnnotationMethod; import com.intellij.psi.PsiElement; +import com.intellij.psi.util.PsiTreeUtil; import com.siyeh.ig.psiutils.ExpressionUtils; import com.siyeh.ipp.base.PsiElementPredicate; @@ -33,6 +35,7 @@ class SimpleStringConcatenationPredicate implements PsiElementPredicate { if (!ExpressionUtils.isConcatenation(element)) { return false; } - return !(excludeConcatenationsInsideAnnotations && AnnotationUtil.isInsideAnnotation(element)); + return !(excludeConcatenationsInsideAnnotations && (AnnotationUtil.isInsideAnnotation(element) || + PsiTreeUtil.getParentOfType(element, PsiAnnotationMethod.class) != null)); } } diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/concatenation/string_builder/ConstantRequiredInsideAnnotationMethod.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/concatenation/string_builder/ConstantRequiredInsideAnnotationMethod.java new file mode 100644 index 000000000000..c69e724452dd --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/concatenation/string_builder/ConstantRequiredInsideAnnotationMethod.java @@ -0,0 +1,5 @@ +package com.siyeh.ipp.concatenation.string_builder; + +public @interface ConstantRequiredInsideAnnotationMethod { + String val() default "hey," + ""; +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/concatenation/ReplaceConcatenationWithStringBufferIntentionTest.java b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/concatenation/ReplaceConcatenationWithStringBufferIntentionTest.java index 82ee9ca4b93e..3baa064bf8c8 100644 --- a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/concatenation/ReplaceConcatenationWithStringBufferIntentionTest.java +++ b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/concatenation/ReplaceConcatenationWithStringBufferIntentionTest.java @@ -10,6 +10,7 @@ public class ReplaceConcatenationWithStringBufferIntentionTest extends IPPTestCa public void testNonStringConcatenationStart() { doTest(); } public void testConcatenationInsideAppend() { doTest(); } + public void testConstantRequiredInsideAnnotationMethod() { assertIntentionNotAvailable(); } @Override protected String getIntentionName() { From 6423538939a7a5f4b4662c29dc1e7cc2b64c5ddf Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Mon, 5 Jun 2017 17:20:27 +0300 Subject: [PATCH 028/136] surround with array fix: disable for non-denotable types (IDEA-173797) --- .../daemon/impl/quickfix/SurroundWithArrayFix.java | 3 ++- .../quickFix/surroundWithArray/beforeRejectNullType.java | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/surroundWithArray/beforeRejectNullType.java diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/SurroundWithArrayFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/SurroundWithArrayFix.java index 123d9771923e..16d5a01a2493 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/SurroundWithArrayFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/SurroundWithArrayFix.java @@ -23,6 +23,7 @@ import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiTypesUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; import com.intellij.util.ArrayUtilRt; @@ -92,7 +93,7 @@ public class SurroundWithArrayFix extends PsiElementBaseIntentionAction { final PsiType paramType = psiParameters[idx].getType(); if (paramType instanceof PsiArrayType) { final PsiType expressionType = TypeConversionUtil.erasure(expression.getType()); - if (expressionType != null) { + if (expressionType != null && PsiTypesUtil.isDenotableType(expressionType) && expressionType != PsiType.NULL) { final PsiType componentType = ((PsiArrayType)paramType).getComponentType(); if (TypeConversionUtil.isAssignable(componentType, expressionType)) { return expression; diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/surroundWithArray/beforeRejectNullType.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/surroundWithArray/beforeRejectNullType.java new file mode 100644 index 000000000000..6972290707e4 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/surroundWithArray/beforeRejectNullType.java @@ -0,0 +1,9 @@ +// "Surround with array initialization" "false" +class A { + void m1(String[] s, + String[] s2, + String[] s3) {} + { + m1( null, null); + } +} \ No newline at end of file From ad6d5ed5164532c9833568c34e60a9e15884b769 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Mon, 5 Jun 2017 17:25:07 +0300 Subject: [PATCH 029/136] method has too many params should not warn on derivatives(IDEA-173754) --- .../siyeh/ig/methodmetrics/ParametersPerMethodInspection.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/methodmetrics/ParametersPerMethodInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/methodmetrics/ParametersPerMethodInspection.java index b760b1ec3936..ea717056a154 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/methodmetrics/ParametersPerMethodInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/methodmetrics/ParametersPerMethodInspection.java @@ -19,7 +19,6 @@ import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiParameterList; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspectionVisitor; -import com.siyeh.ig.psiutils.LibraryUtil; import org.jetbrains.annotations.NotNull; public class ParametersPerMethodInspection extends MethodMetricInspection { @@ -76,7 +75,8 @@ public class ParametersPerMethodInspection extends MethodMetricInspection { if (parametersCount <= getLimit()) { return; } - if (LibraryUtil.isOverrideOfLibraryMethod(method)) { + //skip all derivatives + if (method.findDeepestSuperMethods().length > 0) { return; } registerMethodError(method, Integer.valueOf(parametersCount)); From 61f9e727d318483ce8a82c60f165246ba4b8c74d Mon Sep 17 00:00:00 2001 From: Anton Tarasov Date: Mon, 5 Jun 2017 17:56:12 +0300 Subject: [PATCH 030/136] remove old key in WindowStateServiceImpl --- .../openapi/util/WindowStateServiceImpl.java | 170 +++++++++--------- 1 file changed, 80 insertions(+), 90 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/util/WindowStateServiceImpl.java b/platform/platform-impl/src/com/intellij/openapi/util/WindowStateServiceImpl.java index 67d2c9eb27ad..622e5231de38 100644 --- a/platform/platform-impl/src/com/intellij/openapi/util/WindowStateServiceImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/util/WindowStateServiceImpl.java @@ -26,11 +26,13 @@ import com.intellij.util.ui.UIUtil; import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.awt.*; import java.util.Map; import java.util.TreeMap; -import java.util.function.Supplier; +import java.util.function.BiFunction; +import java.util.function.Function; /** * @author Sergey.Malenkov @@ -219,9 +221,32 @@ abstract class WindowStateServiceImpl extends WindowStateService implements Pers private T getFor(Object object, @NotNull String key, @NotNull Class type) { GraphicsDevice screen = getScreen(object); - T state = get(getKey(screen, key), type); + float scale = getSysScale(screen); + + Function getState = (myKey) -> { + WindowState state = myStateMap.get(myKey); + if (state == null) return null; + state = state.copy().scaleDown(scale); + if (isVisible(state)) { + if (type == WindowState.class) { + return (T)state; + } + if (type == Point.class) { + return (T)state.getLocation(); + } + if (type == Dimension.class) { + return (T)state.getSize(); + } + if (type == Rectangle.class) { + return (T)state.getBounds(); + } + } + return null; + }; + + T state = getState.apply(getKey(screen, key)); if (state == null) { - state = get(getOldKey(screen, key), type); + state = getState.apply(getOldKey(screen, key)); } if (state != null) { return state; @@ -229,29 +254,7 @@ abstract class WindowStateServiceImpl extends WindowStateService implements Pers if (object != null) { return getFor(null, key, type); } - return get(new KeyPair(key, 1f), type); - } - - @SuppressWarnings("unchecked") - private T get(@NotNull KeyPair keyPair, @NotNull Class type) { - WindowState state = myStateMap.get(keyPair.first); - if (state == null) return null; - state = state.copy().scaleDown(keyPair.second); - if (isVisible(state)) { - if (type == WindowState.class) { - return (T)state; - } - if (type == Point.class) { - return (T)state.getLocation(); - } - if (type == Dimension.class) { - return (T)state.getSize(); - } - if (type == Rectangle.class) { - return (T)state.getBounds(); - } - } - return null; + return getState.apply(key); } private void putFor(Object object, @NotNull String key, @@ -261,63 +264,64 @@ abstract class WindowStateServiceImpl extends WindowStateService implements Pers boolean fullScreen, boolean fullScreenSet) { synchronized (myStateMap) { GraphicsDevice screen = getScreen(object); - KeyPair oldKeyPair = getOldKey(screen, key); + float scale = getSysScale(screen); - putImpl(getKey(screen, key), oldKeyPair, location, locationSet, size, sizeSet, maximized, maximizedSet, fullScreen, fullScreenSet); + BiFunction putState = (myNewKey, myOldKey) -> { + // remove & migrate the old key state + WindowState oldState = myOldKey != null ? myStateMap.remove(myOldKey) : null; + if (oldState != null) { + oldState.scaleDown(scale); + WindowState newState = myStateMap.get(myNewKey); + if (newState != null) { + newState.merge(oldState); + } else { + myStateMap.put(myNewKey, oldState); + } + } + // put the new key state + WindowState state = myStateMap.get(myNewKey); + if (state != null) { + if (state.set(location, locationSet, size, sizeSet, maximized, maximizedSet, fullScreen, fullScreenSet)) { + state.scaleUp(scale); + } else { + myStateMap.remove(myNewKey); + } + } + else { + state = new WindowState(); + if (state.set(location, locationSet, size, sizeSet, maximized, maximizedSet, fullScreen, fullScreenSet)) { + state.scaleUp(scale); + myStateMap.put(myNewKey, state); + } + } + return null; + }; + + putState.apply(getKey(screen, key), getOldKey(screen, key)); if (screen != null) { - putImpl(getKey(null, key), oldKeyPair, location, locationSet, size, sizeSet, maximized, maximizedSet, fullScreen, fullScreenSet); + putState.apply(getKey(null, key), getOldKey(null, key)); } - putImpl(new KeyPair(key, 1f), oldKeyPair, location, locationSet, size, sizeSet, maximized, maximizedSet, fullScreen, fullScreenSet); + putState.apply(key, null); } } - private void putImpl(@NotNull KeyPair keyPair, - @NotNull KeyPair oldKeyPair, - Point location, boolean locationSet, - Dimension size, boolean sizeSet, - boolean maximized, boolean maximizedSet, - boolean fullScreen, boolean fullScreenSet) { - WindowState state = myStateMap.get(keyPair.first); - - // may be convert the old key state to the new key - WindowState oldState = myStateMap.remove(oldKeyPair.first); - if (oldState != null) { - oldState.scaleDown(oldKeyPair.second); - if (state != null) { - state.merge(oldState); - } else { - myStateMap.put(keyPair.first, state = oldState); - } - } - if (state != null) { - if (state.set(location, locationSet, size, sizeSet, maximized, maximizedSet, fullScreen, fullScreenSet)) { - state.scaleUp(keyPair.second); - } else { - myStateMap.remove(keyPair.first); - } - } - else { - state = new WindowState(); - if (state.set(location, locationSet, size, sizeSet, maximized, maximizedSet, fullScreen, fullScreenSet)) { - state.scaleUp(keyPair.second); - myStateMap.put(keyPair.first, state); - } - } + private static float getSysScale(GraphicsDevice screen) { + return UIUtil.isJreHiDPIEnabled() && screen != null ? JBUI.sysScale(screen.getDefaultConfiguration()) : 1f; } /* * todo: old hidpi-unaware key; to be removed */ @NotNull - private static KeyPair getOldKey(GraphicsDevice screen, String key) { + private static String getOldKey(@Nullable GraphicsDevice screen, String key) { GraphicsEnvironment environment = GraphicsEnvironment.getLocalGraphicsEnvironment(); if (environment.isHeadlessInstance()) { - return new KeyPair(key + ".headless", 1f); + return key + ".headless"; } StringBuilder sb = new StringBuilder(key); - float scale = 1f; for (GraphicsDevice device : environment.getScreenDevices()) { Rectangle bounds = device.getDefaultConfiguration().getBounds(); + normalizeSize(device, bounds); sb.append('/').append(bounds.x); sb.append('.').append(bounds.y); sb.append('.').append(bounds.width); @@ -325,25 +329,22 @@ abstract class WindowStateServiceImpl extends WindowStateService implements Pers } if (screen != null) { Rectangle bounds = screen.getDefaultConfiguration().getBounds(); + normalizeSize(screen, bounds); sb.append('@').append(bounds.x); sb.append('.').append(bounds.y); sb.append('.').append(bounds.width); sb.append('.').append(bounds.height); - if (UIUtil.isJreHiDPIEnabled()) { - scale = JBUI.sysScale(screen.getDefaultConfiguration()); - } } - return new KeyPair(sb.toString(), scale); + return sb.toString(); } @NotNull - private static KeyPair getKey(GraphicsDevice screen, String key) { + private static String getKey(@Nullable GraphicsDevice screen, String key) { GraphicsEnvironment environment = GraphicsEnvironment.getLocalGraphicsEnvironment(); if (environment.isHeadlessInstance()) { - return new KeyPair(key + ".headless", 1f); + return key + ".headless"; } StringBuilder sb = new StringBuilder(key); - float scale = 1f; // not storing screen x,y due to relying on isVisible(state) on key retrieval if (screen == null) { for (GraphicsDevice device : environment.getScreenDevices()) { @@ -360,13 +361,10 @@ abstract class WindowStateServiceImpl extends WindowStateService implements Pers sb.append('.').append(bounds.height); if (JBUI.isPixHiDPI(screen.getDefaultConfiguration())) { int dpi = ((int)(96 * JBUI.pixScale(screen.getDefaultConfiguration()))); - sb.append("@" + dpi + "dpi"); - } - if (UIUtil.isJreHiDPIEnabled()) { - scale = JBUI.sysScale(screen.getDefaultConfiguration()); + sb.append("@").append(dpi).append("dpi"); } } - return new KeyPair(sb.toString(), scale); + return sb.toString(); } private static void normalizeSize(GraphicsDevice screen, Rectangle bounds) { @@ -390,15 +388,13 @@ abstract class WindowStateServiceImpl extends WindowStateService implements Pers } if (object instanceof Window) { Window window = (Window)object; - object = ScreenUtil.getScreenDevice(window.getBounds()); - if (object == null) { - LOG.warn("cannot find a screen for " + window); - return null; + GraphicsConfiguration gc = window.getGraphicsConfiguration(); + GraphicsDevice device = gc != null ? + window.getGraphicsConfiguration().getDevice() : ScreenUtil.getScreenDevice(window.getBounds()); + if (device != null) { + return device; } } - if (object instanceof GraphicsDevice) { - return (GraphicsDevice)object; - } LOG.warn("cannot find a screen for " + object); return null; } @@ -492,10 +488,4 @@ abstract class WindowStateServiceImpl extends WindowStateService implements Pers } return ScreenUtil.isVisible(new Rectangle(location, size)); } - - private static class KeyPair extends Pair { - public KeyPair(String key, Float scale) { - super(key, scale); - } - } } From 0c0ca5158de6e94a8c15fefeac1970dc4ec568f3 Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Mon, 5 Jun 2017 18:17:46 +0300 Subject: [PATCH 031/136] [vcs-log] fix older index files not being removed When a persistent structure is created for index, we provide it with a file that looks like "logId.kind.version". If this file does not exist, we assume that index version was incremented and attempt to delete files that start with "logId.kind". But this approach does not work with MapReduceIndex: it adds ".storage" suffix to the file given to it. In order to avoid deleting index files all the time, a brilliant solution was introduced in 91c7d97f0901bc97733a81c595c51ea51e0a0009: do not remove older versions at all. While the correct solution is to remove files that start with prefix corresponding to the older versions, which is done here. --- .../log/data/index/VcsLogFullDetailsIndex.java | 8 +------- .../vcs/log/data/index/VcsLogPathsIndex.java | 5 +++-- .../log/data/index/VcsLogPersistentIndex.java | 6 +++--- .../intellij/vcs/log/util/PersistentUtil.java | 16 +++++++++++----- 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogFullDetailsIndex.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogFullDetailsIndex.java index 61470653fda3..0cf752a66693 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogFullDetailsIndex.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogFullDetailsIndex.java @@ -31,7 +31,6 @@ import com.intellij.vcs.log.util.PersistentUtil; import gnu.trove.TIntHashSet; import org.jetbrains.annotations.NotNull; -import java.io.File; import java.io.IOException; import java.util.Collection; import java.util.Map; @@ -130,11 +129,6 @@ public class VcsLogFullDetailsIndex implements Disposable { if (myDisposed) throw new ProcessCanceledException(); } - @NotNull - public static File getStorageFile(@NotNull String kind, @NotNull String id) { - return PersistentUtil.getStorageFile(INDEX, kind, id, getVersion(), false); - } - private class MyMapReduceIndex extends MapReduceIndex { public MyMapReduceIndex(@NotNull DataIndexer indexer, @NotNull DataExternalizer externalizer, @@ -157,7 +151,7 @@ public class VcsLogFullDetailsIndex implements Disposable { private static class MyMapIndexStorage extends MapIndexStorage { public MyMapIndexStorage(@NotNull String name, @NotNull String logId, @NotNull DataExternalizer externalizer) throws IOException { - super(VcsLogFullDetailsIndex.getStorageFile(name, logId), EnumeratorIntegerDescriptor.INSTANCE, externalizer, 5000, false); + super(PersistentUtil.getStorageFile(INDEX, name, logId, getVersion()), EnumeratorIntegerDescriptor.INSTANCE, externalizer, 5000, false); } @Override diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPathsIndex.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPathsIndex.java index 899713a03e5b..9347f8fa478b 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPathsIndex.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPathsIndex.java @@ -71,7 +71,7 @@ public class VcsLogPathsIndex extends VcsLogFullDetailsIndex createPathsEnumerator(@NotNull String logId) throws IOException { - File storageFile = PersistentUtil.getStorageFile(INDEX, INDEX_PATHS_IDS, logId, getVersion(), true); + File storageFile = PersistentUtil.getStorageFile(INDEX, INDEX_PATHS_IDS, logId, getVersion()); return new PersistentBTreeEnumerator<>(storageFile, SystemInfo.isFileSystemCaseSensitive ? EnumeratorStringDescriptor.INSTANCE : new ToLowerCaseStringDescriptor(), Page.PAGE_SIZE, null, getVersion()); @@ -309,7 +309,8 @@ public class VcsLogPathsIndex extends VcsLogFullDetailsIndex beforeData = fillDataWithNulls(result, parent, beforeId); beforeData.add(new ChangeData(ChangeKind.RENAMED_FROM, afterId)); diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPersistentIndex.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPersistentIndex.java index f9a4eeeb5874..783eaf34bf4c 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPersistentIndex.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPersistentIndex.java @@ -415,11 +415,11 @@ public class VcsLogPersistentIndex implements VcsLogIndex, Disposable { try { int version = getVersion(); - File commitsStorage = getStorageFile(INDEX, COMMITS, logId, version, true); + File commitsStorage = getStorageFile(INDEX, COMMITS, logId, version); commits = new PersistentSetImpl<>(commitsStorage, EnumeratorIntegerDescriptor.INSTANCE, Page.PAGE_SIZE, null, version); Disposer.register(disposable, () -> catchAndWarn(commits::close)); - File messagesStorage = getStorageFile(INDEX, MESSAGES, logId, VcsLogStorageImpl.VERSION + MESSAGES_VERSION, true); + File messagesStorage = getStorageFile(INDEX, MESSAGES, logId, VcsLogStorageImpl.VERSION + MESSAGES_VERSION); messages = new PersistentHashMap<>(messagesStorage, new IntInlineKeyDescriptor(), EnumeratorStringDescriptor.INSTANCE, Page.PAGE_SIZE); Disposer.register(disposable, () -> catchAndWarn(messages::close)); @@ -428,7 +428,7 @@ public class VcsLogPersistentIndex implements VcsLogIndex, Disposable { users = new VcsLogUserIndex(logId, userRegistry, fatalErrorHandler, disposable); paths = new VcsLogPathsIndex(logId, roots, fatalErrorHandler, disposable); - File parentsStorage = getStorageFile(INDEX, PARENTS, logId, getVersion(), true); + File parentsStorage = getStorageFile(INDEX, PARENTS, logId, getVersion()); parents = new PersistentHashMap<>(parentsStorage, EnumeratorIntegerDescriptor.INSTANCE, new IntListDataExternalizer(), Page.PAGE_SIZE, getVersion()); Disposer.register(disposable, () -> catchAndWarn(parents::close)); diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/util/PersistentUtil.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/util/PersistentUtil.java index e81891dd7d9f..8f6da02b8bf7 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/util/PersistentUtil.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/util/PersistentUtil.java @@ -110,17 +110,23 @@ public class PersistentUtil { public static File getStorageFile(@NotNull String subdirName, @NotNull String kind, @NotNull String id, - int version, - boolean cleanupOldVersions) { + int version) { File subdir = new File(LOG_CACHE, subdirName); String safeLogId = PathUtilRt.suggestFileName(id, true, true); - File file = new File(subdir, safeLogId + "." + kind + "." + version); - if (cleanupOldVersions && !file.exists()) { - IOUtil.deleteAllFilesStartingWith(new File(subdir, safeLogId + "." + kind)); + File file = getFileName(kind, subdir, safeLogId, version); + if (!file.exists()) { + for (int oldVersion = 0; oldVersion < version; oldVersion++) { + IOUtil.deleteAllFilesStartingWith(getFileName(kind, subdir, safeLogId, oldVersion)); + } } return file; } + @NotNull + private static File getFileName(@NotNull String kind, @NotNull File subdir, @NotNull String safeLogId, int version) { + return new File(subdir, safeLogId + "." + kind + "." + version); + } + @NotNull public static File getCorruptionMarkerFile() { return new File(LOG_CACHE, CORRUPTION_MARKER); From 056896852cd5ede202aa22ded764d37bbb0b2c42 Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Mon, 5 Jun 2017 18:23:00 +0300 Subject: [PATCH 032/136] [vcs-log] use version variable instead of method getVersion call --- .../intellij/vcs/log/data/index/VcsLogPersistentIndex.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPersistentIndex.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPersistentIndex.java index 783eaf34bf4c..d74819c3b7e1 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPersistentIndex.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPersistentIndex.java @@ -428,9 +428,9 @@ public class VcsLogPersistentIndex implements VcsLogIndex, Disposable { users = new VcsLogUserIndex(logId, userRegistry, fatalErrorHandler, disposable); paths = new VcsLogPathsIndex(logId, roots, fatalErrorHandler, disposable); - File parentsStorage = getStorageFile(INDEX, PARENTS, logId, getVersion()); + File parentsStorage = getStorageFile(INDEX, PARENTS, logId, version); parents = new PersistentHashMap<>(parentsStorage, EnumeratorIntegerDescriptor.INSTANCE, - new IntListDataExternalizer(), Page.PAGE_SIZE, getVersion()); + new IntListDataExternalizer(), Page.PAGE_SIZE, version); Disposer.register(disposable, () -> catchAndWarn(parents::close)); } catch (Throwable t) { From 45bb74c79fdf0e5fe4713b56eceb8ca6cf1c5d4f Mon Sep 17 00:00:00 2001 From: "liana.bakradze" Date: Mon, 15 May 2017 14:31:39 +0300 Subject: [PATCH 033/136] initial support of pandas Series (PY-21763) --- .../pydev/_pydevd_bundle/pydevd_vars.py | 56 +++++++++++++++++++ .../python/debugger/PyDebugValue.java | 20 +++---- .../containerview/DataViewStrategy.java | 10 +++- 3 files changed, 75 insertions(+), 11 deletions(-) diff --git a/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py b/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py index f3d9d030fd2d..46c27ddf404b 100644 --- a/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py +++ b/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py @@ -425,6 +425,9 @@ def table_like_struct_to_xml(array, name, roffset, coffset, rows, cols, format): xml += array_to_xml(array, roffset, coffset, rows, cols, format) elif type_name == 'DataFrame': xml = dataframe_to_xml(array, name, roffset, coffset, rows, cols, format) + elif type_name == 'Series': + xml = series_to_xml(array, name, roffset, 0, rows, 1, format) + else: raise VariableError("Do not know how to convert type %s to table" % (type_name)) @@ -616,3 +619,56 @@ def dataframe_to_xml(df, name, roffset, coffset, rows, cols, format): value = col_formats[col] % value xml += var_to_xml(value, '') return xml + + +def series_to_xml(df, name, roffset, coffset, rows, cols, format): + """ + :type df: pandas.core.frame.DataFrame + :type name: str + :type coffset: int + :type roffset: int + :type rows: int + :type cols: int + :type format: str + + + """ + num_rows = df.shape[0] + xml = '\n' % \ + (name, num_rows, 1) + + if (rows, cols) == (-1, -1): + rows, cols = num_rows, 1 + + rows = min(rows, 100) + dtype = df.dtype.kind + col_bounds = [(df.min(), df.max()) if dtype in "biufc" else (0, 0)] + + df = df.iloc[roffset: roffset + rows] + rows, cols = df.shape[0], 1 + + + xml += "\n" % (rows, cols) + format = format.replace('%', '') + col_formats = [] + + + for col in range(cols): + fmt = format if (dtype == 'f' and format) else array_default_format(dtype) + col_formats.append('%' + fmt) + bounds = col_bounds[col] + + xml += '\n' % \ + (str(col), 1, dtype, fmt, bounds[1], bounds[0]) + for row, label in enumerate(iter(df.axes[0])): + xml += "\n" % \ + (str(row), 1) + xml += "\n" + xml += "\n" % (rows, cols) + for row in range(rows): + xml += "\n" % str(row) + for col in range(cols): + value = df.iat[row] + value = col_formats[col] % value + xml += var_to_xml(value, '') + return xml diff --git a/python/pydevSrc/com/jetbrains/python/debugger/PyDebugValue.java b/python/pydevSrc/com/jetbrains/python/debugger/PyDebugValue.java index 09444ad6d618..4d2d4a9ccdfb 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/PyDebugValue.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/PyDebugValue.java @@ -4,12 +4,15 @@ import com.google.common.base.Strings; import com.intellij.icons.AllIcons; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.Pair; +import com.intellij.util.containers.ContainerUtil; import com.intellij.xdebugger.frame.*; import com.jetbrains.python.debugger.pydev.PyVariableLocator; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -17,6 +20,10 @@ import java.util.regex.Pattern; // todo: null modifier for modify modules, class objects etc. public class PyDebugValue extends XNamedValue { private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.pydev.PyDebugValue"); + private static final String DATA_FRAME = "DataFrame"; + private static final String SERIES = "Series"; + private static final Map EVALUATOR_PREFIXES = + ContainerUtil.newHashMap(Pair.create("ndarray", "Array"), Pair.create(DATA_FRAME, DATA_FRAME), Pair.create(SERIES, SERIES)); public static final int MAX_VALUE = 256; public static final String RETURN_VALUES_PREFIX = "__pydevd_ret_val_dict"; @@ -204,23 +211,16 @@ public class PyDebugValue extends XNamedValue { node.setPresentation(getValueIcon(), myType, value, myContainer); } - private boolean isDataFrame() { - return "DataFrame".equals(myType); - } - - private boolean isNdarray() { - return "ndarray".equals(myType); - } - private void setFullValueEvaluator(XValueNode node, String value) { String treeName = getFullTreeName(); - if (!isDataFrame() && !isNdarray()) { + String postfix = EVALUATOR_PREFIXES.get(myType); + if (postfix == null) { if (value.length() >= MAX_VALUE) { node.setFullValueEvaluator(new PyFullValueEvaluator(myFrameAccessor, treeName)); } return; } - String linkText = "...View as " + (isDataFrame() ? "DataFrame" : "Array"); + String linkText = "...View as " + postfix; node.setFullValueEvaluator(new PyNumericContainerValueEvaluator(linkText, myFrameAccessor, treeName)); } diff --git a/python/src/com/jetbrains/python/debugger/containerview/DataViewStrategy.java b/python/src/com/jetbrains/python/debugger/containerview/DataViewStrategy.java index 1eb2678b3715..842781afcbf6 100644 --- a/python/src/com/jetbrains/python/debugger/containerview/DataViewStrategy.java +++ b/python/src/com/jetbrains/python/debugger/containerview/DataViewStrategy.java @@ -29,7 +29,7 @@ import java.util.Set; public abstract class DataViewStrategy { private static class StrategyHolder { - private static final Set STRATEGIES = ContainerUtil.newHashSet(new ArrayViewStrategy(), new DataFrameViewStrategy()); + private static final Set STRATEGIES = ContainerUtil.newHashSet(new ArrayViewStrategy(), new DataFrameViewStrategy(), new DataFrameViewStrategy(), new SeriesViewStrategy()); } public abstract AsyncArrayTableModel createTableModel(int rowCount, int columnCount, @NotNull PyDataViewerPanel panel, @NotNull PyDebugValue debugValue); @@ -53,4 +53,12 @@ public abstract class DataViewStrategy { } return null; } + + private static class SeriesViewStrategy extends DataFrameViewStrategy { + @NotNull + @Override + public String getTypeName() { + return "Series"; + } + } } \ No newline at end of file From 761ee433538af468a72816896114210b9d92cfec Mon Sep 17 00:00:00 2001 From: "Liana.Bakradze" Date: Tue, 16 May 2017 12:33:22 +0300 Subject: [PATCH 034/136] use constant (PY-21763) --- python/helpers/pydev/_pydevd_bundle/pydevd_vars.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py b/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py index 46c27ddf404b..effd51274548 100644 --- a/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py +++ b/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py @@ -640,7 +640,7 @@ def series_to_xml(df, name, roffset, coffset, rows, cols, format): if (rows, cols) == (-1, -1): rows, cols = num_rows, 1 - rows = min(rows, 100) + rows = min(rows, MAXIMUM_ARRAY_SIZE) dtype = df.dtype.kind col_bounds = [(df.min(), df.max()) if dtype in "biufc" else (0, 0)] From 4fe9bb51abd89a77fb0841397341083aec0fc5e5 Mon Sep 17 00:00:00 2001 From: "Liana.Bakradze" Date: Tue, 16 May 2017 17:19:13 +0300 Subject: [PATCH 035/136] use dict to map converter and type (PY-21763) --- .../pydev/_pydevd_bundle/pydevd_vars.py | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py b/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py index effd51274548..420defeca9ab 100644 --- a/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py +++ b/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py @@ -413,25 +413,16 @@ def change_attr_expression(thread_id, frame_id, attr, expression, dbg, value=SEN MAXIMUM_ARRAY_SIZE = 100 -def table_like_struct_to_xml(array, name, roffset, coffset, rows, cols, format): - _, type_name, _ = get_type(array) - if type_name == 'ndarray': - array, metaxml, r, c, f = array_to_meta_xml(array, name, format) - xml = metaxml - format = '%' + f - if rows == -1 and cols == -1: - rows = r - cols = c - xml += array_to_xml(array, roffset, coffset, rows, cols, format) - elif type_name == 'DataFrame': - xml = dataframe_to_xml(array, name, roffset, coffset, rows, cols, format) - elif type_name == 'Series': - xml = series_to_xml(array, name, roffset, 0, rows, 1, format) - else: - raise VariableError("Do not know how to convert type %s to table" % (type_name)) - - return "%s" % xml +def array_to_xml_converter(array, name, roffset, coffset, rows, cols, format): + array, metaxml, r, c, f = array_to_meta_xml(array, name, format) + xml = metaxml + format = '%' + f + if rows == -1 and cols == -1: + rows = r + cols = c + xml += array_to_xml(array, roffset, coffset, rows, cols, format) + return xml def array_to_xml(array, roffset, coffset, rows, cols, format): @@ -672,3 +663,13 @@ def series_to_xml(df, name, roffset, coffset, rows, cols, format): value = col_formats[col] % value xml += var_to_xml(value, '') return xml + +TYPE_TO_XML_CONVERTERS = {"ndarray": array_to_xml_converter, "DataFrame": dataframe_to_xml, "Series": series_to_xml} + + +def table_like_struct_to_xml(array, name, roffset, coffset, rows, cols, format): + _, type_name, _ = get_type(array) + if type_name in TYPE_TO_XML_CONVERTERS: + return "%s" % TYPE_TO_XML_CONVERTERS[type_name](array, name, roffset, coffset, rows, cols, format) + else: + raise VariableError("type %s not supported" % type_name) From 806c592b44ea57a1ba91d16acd75775008fe6751 Mon Sep 17 00:00:00 2001 From: "Liana.Bakradze" Date: Wed, 17 May 2017 14:17:05 +0300 Subject: [PATCH 036/136] extract writing row to xml (PY-21763) --- .../pydev/_pydevd_bundle/pydevd_vars.py | 34 ++++++++----------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py b/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py index 420defeca9ab..dc9ba7e3da97 100644 --- a/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py +++ b/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py @@ -449,24 +449,20 @@ def array_to_xml(array, roffset, coffset, rows, cols, format): xml += "" % (rows, cols) for row in range(rows): - xml += "" % to_string(row) - for col in range(cols): + def get_value(col): value = array if rows == 1 or cols == 1: if rows == 1 and cols == 1: value = array[0] else: - if rows == 1: - dim = col - else: - dim = row - value = array[dim] + value = array[(col if rows == 1 else row)] if "ndarray" in str(type(value)): value = value[0] else: value = array[row][col] - value = format % value - xml += var_to_xml(value, '') + return value + xml += row_to_xml(row, (get_value(col) for col in range(cols))) + return xml @@ -604,11 +600,7 @@ def dataframe_to_xml(df, name, roffset, coffset, rows, cols, format): xml += "\n" xml += "\n" % (rows, cols) for row in range(rows): - xml += "\n" % str(row) - for col in range(cols): - value = df.iat[row, col] - value = col_formats[col] % value - xml += var_to_xml(value, '') + xml += row_to_xml(row, (col_formats[col] % df.iat[row, col] for col in range(cols))) return xml @@ -657,13 +649,17 @@ def series_to_xml(df, name, roffset, coffset, rows, cols, format): xml += "\n" xml += "\n" % (rows, cols) for row in range(rows): - xml += "\n" % str(row) - for col in range(cols): - value = df.iat[row] - value = col_formats[col] % value - xml += var_to_xml(value, '') + xml += row_to_xml(row, (col_formats[col] % df.iat[row] for col in range(cols))) return xml + +def row_to_xml(index, formatted_values): + xml = "\n" % to_string(index) + for value in formatted_values: + xml += var_to_xml(value, '') + return xml + + TYPE_TO_XML_CONVERTERS = {"ndarray": array_to_xml_converter, "DataFrame": dataframe_to_xml, "Series": series_to_xml} From 80534296c4b9ef24dfb3b9d24fabbfb69afb4162 Mon Sep 17 00:00:00 2001 From: "Liana.Bakradze" Date: Wed, 17 May 2017 15:04:35 +0300 Subject: [PATCH 037/136] extract writing array data to xml (PY-21763) --- .../pydev/_pydevd_bundle/pydevd_vars.py | 45 +++++++++---------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py b/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py index dc9ba7e3da97..c45a27b0c99f 100644 --- a/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py +++ b/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py @@ -447,22 +447,19 @@ def array_to_xml(array, roffset, coffset, rows, cols, format): array = array[roffset:] rows = min(rows, len(array)) - xml += "" % (rows, cols) - for row in range(rows): - def get_value(col): - value = array - if rows == 1 or cols == 1: - if rows == 1 and cols == 1: - value = array[0] - else: - value = array[(col if rows == 1 else row)] - if "ndarray" in str(type(value)): - value = value[0] + def get_value(row, col): + value = array + if rows == 1 or cols == 1: + if rows == 1 and cols == 1: + value = array[0] else: - value = array[row][col] - return value - xml += row_to_xml(row, (get_value(col) for col in range(cols))) - + value = array[(col if rows == 1 else row)] + if "ndarray" in str(type(value)): + value = value[0] + else: + value = array[row][col] + return value + xml += array_data_to_xml(rows, cols, lambda r: (get_value(r, c) for c in range(cols))) return xml @@ -598,9 +595,7 @@ def dataframe_to_xml(df, name, roffset, coffset, rows, cols, format): xml += "\n" % \ (str(row), get_label(label)) xml += "\n" - xml += "\n" % (rows, cols) - for row in range(rows): - xml += row_to_xml(row, (col_formats[col] % df.iat[row, col] for col in range(cols))) + xml += array_data_to_xml(rows, cols, lambda r: (col_formats[c] % df.iat[row, c] for c in range(cols))) return xml @@ -647,16 +642,16 @@ def series_to_xml(df, name, roffset, coffset, rows, cols, format): xml += "\n" % \ (str(row), 1) xml += "\n" - xml += "\n" % (rows, cols) - for row in range(rows): - xml += row_to_xml(row, (col_formats[col] % df.iat[row] for col in range(cols))) + xml += array_data_to_xml(rows, cols, lambda r: (col_formats[c] % df.iat[r] for c in range(cols))) return xml -def row_to_xml(index, formatted_values): - xml = "\n" % to_string(index) - for value in formatted_values: - xml += var_to_xml(value, '') +def array_data_to_xml(rows, cols, get_row): + xml = "\n" % (rows, cols) + for row in range(rows): + xml += "\n" % to_string(row) + for value in get_row(row): + xml += var_to_xml(value, '') return xml From c11be637b680b718b2b5ac3cce184569842c6221 Mon Sep 17 00:00:00 2001 From: "Liana.Bakradze" Date: Wed, 17 May 2017 15:06:17 +0300 Subject: [PATCH 038/136] fix labels for series rows (PY-21763) --- python/helpers/pydev/_pydevd_bundle/pydevd_vars.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py b/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py index c45a27b0c99f..60818af333a6 100644 --- a/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py +++ b/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py @@ -640,7 +640,7 @@ def series_to_xml(df, name, roffset, coffset, rows, cols, format): (str(col), 1, dtype, fmt, bounds[1], bounds[0]) for row, label in enumerate(iter(df.axes[0])): xml += "\n" % \ - (str(row), 1) + (str(row), str(label)) xml += "\n" xml += array_data_to_xml(rows, cols, lambda r: (col_formats[c] % df.iat[r] for c in range(cols))) return xml From dd983f8037a82890e46f3daf8d06c7c25c295abb Mon Sep 17 00:00:00 2001 From: "Liana.Bakradze" Date: Wed, 17 May 2017 15:29:53 +0300 Subject: [PATCH 039/136] extract writing slice to xml (PY-21763) --- .../pydev/_pydevd_bundle/pydevd_vars.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py b/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py index 60818af333a6..f50178e3ba19 100644 --- a/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py +++ b/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py @@ -520,9 +520,7 @@ def array_to_meta_xml(array, name, format): bounds = (0, 0) if type in "biufc": bounds = (array.min(), array.max()) - xml = '' % \ - (slice, rows, cols, format, type, bounds[1], bounds[0]) - return array, xml, rows, cols, format + return array, slice_to_xml(slice, rows, cols, format, type, bounds), rows, cols, format def array_default_format(type): @@ -554,8 +552,7 @@ def dataframe_to_xml(df, name, roffset, coffset, rows, cols, format): else: slice = '' slice = name + slice - xml = '\n' % \ - (slice, num_rows, num_cols) + xml = slice_to_xml(slice, num_rows, num_cols, "", "", (0, 0)) if (rows, cols) == (-1, -1): rows, cols = num_rows, num_cols @@ -612,14 +609,14 @@ def series_to_xml(df, name, roffset, coffset, rows, cols, format): """ num_rows = df.shape[0] - xml = '\n' % \ - (name, num_rows, 1) + dtype = df.dtype.kind + format = format.replace('%', '') + xml = slice_to_xml(name, num_rows, 1, dtype, format, (0, 0)) if (rows, cols) == (-1, -1): rows, cols = num_rows, 1 rows = min(rows, MAXIMUM_ARRAY_SIZE) - dtype = df.dtype.kind col_bounds = [(df.min(), df.max()) if dtype in "biufc" else (0, 0)] df = df.iloc[roffset: roffset + rows] @@ -627,7 +624,6 @@ def series_to_xml(df, name, roffset, coffset, rows, cols, format): xml += "\n" % (rows, cols) - format = format.replace('%', '') col_formats = [] @@ -655,6 +651,11 @@ def array_data_to_xml(rows, cols, get_row): return xml +def slice_to_xml(slice, rows, cols, format, type, bounds): + return '' % \ + (slice, rows, cols, format, type, bounds[1], bounds[0]) + + TYPE_TO_XML_CONVERTERS = {"ndarray": array_to_xml_converter, "DataFrame": dataframe_to_xml, "Series": series_to_xml} From eccd8751f542f5834e42051f617fa51af6ab90a4 Mon Sep 17 00:00:00 2001 From: "Liana.Bakradze" Date: Wed, 17 May 2017 15:35:37 +0300 Subject: [PATCH 040/136] remove redundant code (PY-21763) we don't have max slice constraint now so this condition is always false --- python/helpers/pydev/_pydevd_bundle/pydevd_vars.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py b/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py index f50178e3ba19..6c302aae7a64 100644 --- a/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py +++ b/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py @@ -546,13 +546,7 @@ def dataframe_to_xml(df, name, roffset, coffset, rows, cols, format): """ num_rows = df.shape[0] num_cols = df.shape[1] - if (num_rows, num_cols) != df.shape: - df = df.iloc[0:num_rows, 0: num_cols] - slice = '.iloc[0:%s, 0:%s]' % (num_rows, num_cols) - else: - slice = '' - slice = name + slice - xml = slice_to_xml(slice, num_rows, num_cols, "", "", (0, 0)) + xml = slice_to_xml(name, num_rows, num_cols, "", "", (0, 0)) if (rows, cols) == (-1, -1): rows, cols = num_rows, num_cols From 0ce7189a9272fc7539280039041d4539c29c43c0 Mon Sep 17 00:00:00 2001 From: "Liana.Bakradze" Date: Wed, 17 May 2017 15:52:39 +0300 Subject: [PATCH 041/136] fix label for series with multi-indexes (PY-21763) --- python/helpers/pydev/_pydevd_bundle/pydevd_vars.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py b/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py index 6c302aae7a64..b5ba01257e52 100644 --- a/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py +++ b/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py @@ -628,9 +628,10 @@ def series_to_xml(df, name, roffset, coffset, rows, cols, format): xml += '\n' % \ (str(col), 1, dtype, fmt, bounds[1], bounds[0]) + get_label = lambda label: str(label) if not isinstance(label, tuple) else '/'.join(map(str, label)) for row, label in enumerate(iter(df.axes[0])): xml += "\n" % \ - (str(row), str(label)) + (str(row), get_label(label)) xml += "\n" xml += array_data_to_xml(rows, cols, lambda r: (col_formats[c] % df.iat[r] for c in range(cols))) return xml From ac9e8e7459fe2869f689595cf5423687c91e5841 Mon Sep 17 00:00:00 2001 From: "Liana.Bakradze" Date: Thu, 18 May 2017 19:14:26 +0300 Subject: [PATCH 042/136] extract writing header to xml (PY-21763) --- .../pydev/_pydevd_bundle/pydevd_vars.py | 69 ++++++++----------- 1 file changed, 30 insertions(+), 39 deletions(-) diff --git a/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py b/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py index b5ba01257e52..6ce55448ba19 100644 --- a/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py +++ b/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py @@ -532,6 +532,10 @@ def array_default_format(type): return 's' +def get_label(label): + return str(label) if not isinstance(label, tuple) else '/'.join(map(str, label)) + + def dataframe_to_xml(df, name, roffset, coffset, rows, cols, format): """ :type df: pandas.core.frame.DataFrame @@ -566,27 +570,17 @@ def dataframe_to_xml(df, name, roffset, coffset, rows, cols, format): df = df.iloc[roffset: roffset + rows, coffset: coffset + cols] rows, cols = df.shape - - - xml += "\n" % (rows, cols) format = format.replace('%', '') - col_formats = [] - get_label = lambda label: str(label) if not isinstance(label, tuple) else '/'.join(map(str, label)) + col_to_label = lambda col: get_label(df.axes[1].values[col]) + col_to_type = lambda col: df.dtypes.iloc[col].kind + col_to_bounds = lambda col: col_bounds[col] + col_to_format = lambda col: format if col_to_type(col) == 'f' and format else array_default_format(col_to_type(col)) + row_to_label = lambda row: get_label(df.axes[0].values[row]) - for col in range(cols): - dtype = df.dtypes.iloc[col].kind - fmt = format if (dtype == 'f' and format) else array_default_format(dtype) - col_formats.append('%' + fmt) - bounds = col_bounds[col] + xml += header_data_to_xml(rows, cols, col_to_label, col_to_type, col_to_bounds, col_to_format, row_to_label) - xml += '\n' % \ - (str(col), get_label(df.axes[1].values[col]), dtype, fmt, bounds[1], bounds[0]) - for row, label in enumerate(iter(df.axes[0])): - xml += "\n" % \ - (str(row), get_label(label)) - xml += "\n" - xml += array_data_to_xml(rows, cols, lambda r: (col_formats[c] % df.iat[row, c] for c in range(cols))) + xml += array_data_to_xml(rows, cols, lambda r: (("%" + col_to_format(c)) % df.iat[r, c] for c in range(cols))) return xml @@ -603,37 +597,24 @@ def series_to_xml(df, name, roffset, coffset, rows, cols, format): """ num_rows = df.shape[0] - dtype = df.dtype.kind - format = format.replace('%', '') - xml = slice_to_xml(name, num_rows, 1, dtype, format, (0, 0)) + + xml = slice_to_xml(name, num_rows, 1, "", "", (0, 0)) if (rows, cols) == (-1, -1): rows, cols = num_rows, 1 rows = min(rows, MAXIMUM_ARRAY_SIZE) - col_bounds = [(df.min(), df.max()) if dtype in "biufc" else (0, 0)] + dtype = df.dtype.kind + col_bounds = (df.min(), df.max()) if dtype in "biufc" else (0, 0) df = df.iloc[roffset: roffset + rows] rows, cols = df.shape[0], 1 + format = format.replace('%', '') + format = format if (dtype == 'f' and format) else array_default_format(dtype) + xml += header_data_to_xml(rows, cols, lambda col: str(col), lambda col: dtype, lambda col: col_bounds, + lambda col: format, lambda row: get_label(df.axes[0].values[row])) - - xml += "\n" % (rows, cols) - col_formats = [] - - - for col in range(cols): - fmt = format if (dtype == 'f' and format) else array_default_format(dtype) - col_formats.append('%' + fmt) - bounds = col_bounds[col] - - xml += '\n' % \ - (str(col), 1, dtype, fmt, bounds[1], bounds[0]) - get_label = lambda label: str(label) if not isinstance(label, tuple) else '/'.join(map(str, label)) - for row, label in enumerate(iter(df.axes[0])): - xml += "\n" % \ - (str(row), get_label(label)) - xml += "\n" - xml += array_data_to_xml(rows, cols, lambda r: (col_formats[c] % df.iat[r] for c in range(cols))) + xml += array_data_to_xml(rows, cols, lambda r: (('%' + format) % df.iat[r] for c in range(cols))) return xml @@ -651,6 +632,16 @@ def slice_to_xml(slice, rows, cols, format, type, bounds): (slice, rows, cols, format, type, bounds[1], bounds[0]) +def header_data_to_xml(rows, cols, col_to_labels, col_to_type, col_to_bounds, col_to_format, row_to_label): + xml = "\n" % (rows, cols) + for col in range(cols): + xml += '\n' % \ + (str(col), col_to_labels(col), col_to_type(col), col_to_format(col), col_to_bounds(col)[1], col_to_bounds(col)[0]) + for row in range(rows): + xml += "\n" % (str(row), row_to_label(row)) + xml += "\n" + return xml + TYPE_TO_XML_CONVERTERS = {"ndarray": array_to_xml_converter, "DataFrame": dataframe_to_xml, "Series": series_to_xml} From ced6560dcdb2e1baeaa638bb2a81b45d72aac454 Mon Sep 17 00:00:00 2001 From: "Liana.Bakradze" Date: Thu, 18 May 2017 20:12:08 +0300 Subject: [PATCH 043/136] merge helpers (PY-21763) --- .../pydev/_pydevd_bundle/pydevd_vars.py | 90 +++++++------------ 1 file changed, 32 insertions(+), 58 deletions(-) diff --git a/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py b/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py index 6ce55448ba19..ed693bc35674 100644 --- a/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py +++ b/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py @@ -548,73 +548,45 @@ def dataframe_to_xml(df, name, roffset, coffset, rows, cols, format): """ + dim = len(df.axes) num_rows = df.shape[0] - num_cols = df.shape[1] + num_cols = df.shape[1] if dim > 1 else 1 xml = slice_to_xml(name, num_rows, num_cols, "", "", (0, 0)) if (rows, cols) == (-1, -1): rows, cols = num_rows, num_cols rows = min(rows, MAXIMUM_ARRAY_SIZE) - cols = min(min(cols, MAXIMUM_ARRAY_SIZE), num_cols) + cols = min(cols, MAXIMUM_ARRAY_SIZE, num_cols) # need to precompute column bounds here before slicing! col_bounds = [None] * cols - for col in range(cols): - dtype = df.dtypes.iloc[coffset + col].kind - if dtype in "biufc": - cvalues = df.iloc[:, coffset + col] - bounds = (cvalues.min(), cvalues.max()) - else: - bounds = (0, 0) - col_bounds[col] = bounds + dtypes = [None] * cols + if dim > 1: + for col in range(cols): + dtype = df.dtypes.iloc[coffset + col].kind + dtypes[col] = dtype + if dtype in "biufc": + cvalues = df.iloc[:, coffset + col] + bounds = (cvalues.min(), cvalues.max()) + else: + bounds = (0, 0) + col_bounds[col] = bounds + else: + dtype = df.dtype.kind + dtypes[0] = dtype + col_bounds[0] = (df.min(), df.max()) if dtype in "biufc" else (0, 0) - df = df.iloc[roffset: roffset + rows, coffset: coffset + cols] - rows, cols = df.shape + df = df.iloc[roffset: roffset + rows, coffset: coffset + cols] if dim > 1 else df.iloc[roffset: roffset + rows] + rows = df.shape[0] + cols = df.shape[1] if dim > 1 else 1 format = format.replace('%', '') - col_to_label = lambda col: get_label(df.axes[1].values[col]) - col_to_type = lambda col: df.dtypes.iloc[col].kind - col_to_bounds = lambda col: col_bounds[col] - col_to_format = lambda col: format if col_to_type(col) == 'f' and format else array_default_format(col_to_type(col)) - row_to_label = lambda row: get_label(df.axes[0].values[row]) + def col_to_format(c): + return format if dtypes[c] == 'f' and format else array_default_format(dtypes[c]) - xml += header_data_to_xml(rows, cols, col_to_label, col_to_type, col_to_bounds, col_to_format, row_to_label) - - xml += array_data_to_xml(rows, cols, lambda r: (("%" + col_to_format(c)) % df.iat[r, c] for c in range(cols))) - return xml - - -def series_to_xml(df, name, roffset, coffset, rows, cols, format): - """ - :type df: pandas.core.frame.DataFrame - :type name: str - :type coffset: int - :type roffset: int - :type rows: int - :type cols: int - :type format: str - - - """ - num_rows = df.shape[0] - - xml = slice_to_xml(name, num_rows, 1, "", "", (0, 0)) - - if (rows, cols) == (-1, -1): - rows, cols = num_rows, 1 - - rows = min(rows, MAXIMUM_ARRAY_SIZE) - dtype = df.dtype.kind - col_bounds = (df.min(), df.max()) if dtype in "biufc" else (0, 0) - - df = df.iloc[roffset: roffset + rows] - rows, cols = df.shape[0], 1 - format = format.replace('%', '') - format = format if (dtype == 'f' and format) else array_default_format(dtype) - xml += header_data_to_xml(rows, cols, lambda col: str(col), lambda col: dtype, lambda col: col_bounds, - lambda col: format, lambda row: get_label(df.axes[0].values[row])) - - xml += array_data_to_xml(rows, cols, lambda r: (('%' + format) % df.iat[r] for c in range(cols))) + xml += header_data_to_xml(rows, cols, dtypes, col_bounds, col_to_format, df, dim) + xml += array_data_to_xml(rows, cols, lambda r: (("%" + col_to_format(c)) % (df.iat[r, c] if dim > 1 else df.iat[r]) + for c in range(cols))) return xml @@ -632,17 +604,19 @@ def slice_to_xml(slice, rows, cols, format, type, bounds): (slice, rows, cols, format, type, bounds[1], bounds[0]) -def header_data_to_xml(rows, cols, col_to_labels, col_to_type, col_to_bounds, col_to_format, row_to_label): +def header_data_to_xml(rows, cols, dtypes, col_bounds, col_to_format, df, dim): xml = "\n" % (rows, cols) for col in range(cols): + col_label = get_label(df.axes[1].values[col]) if dim > 1 else str(col) + bounds = col_bounds[col] xml += '\n' % \ - (str(col), col_to_labels(col), col_to_type(col), col_to_format(col), col_to_bounds(col)[1], col_to_bounds(col)[0]) + (str(col), col_label, dtypes[col], col_to_format(col), bounds[1], bounds[0]) for row in range(rows): - xml += "\n" % (str(row), row_to_label(row)) + xml += "\n" % (str(row), get_label(df.axes[0].values[row])) xml += "\n" return xml -TYPE_TO_XML_CONVERTERS = {"ndarray": array_to_xml_converter, "DataFrame": dataframe_to_xml, "Series": series_to_xml} +TYPE_TO_XML_CONVERTERS = {"ndarray": array_to_xml_converter, "DataFrame": dataframe_to_xml, "Series": dataframe_to_xml} def table_like_struct_to_xml(array, name, roffset, coffset, rows, cols, format): From 5cc4d2d3fe3d15cdb1ef2dc1331c560e30b0c840 Mon Sep 17 00:00:00 2001 From: "Liana.Bakradze" Date: Fri, 19 May 2017 13:46:51 +0300 Subject: [PATCH 044/136] do not show column header for series (PY-21763) Series are 1-dimensional array, so column header is useless for them --- .../python/debugger/containerview/DataViewStrategy.java | 9 +++++++++ .../python/debugger/containerview/PyDataViewerPanel.java | 1 + 2 files changed, 10 insertions(+) diff --git a/python/src/com/jetbrains/python/debugger/containerview/DataViewStrategy.java b/python/src/com/jetbrains/python/debugger/containerview/DataViewStrategy.java index 842781afcbf6..18e53d8d8ad1 100644 --- a/python/src/com/jetbrains/python/debugger/containerview/DataViewStrategy.java +++ b/python/src/com/jetbrains/python/debugger/containerview/DataViewStrategy.java @@ -41,6 +41,10 @@ public abstract class DataViewStrategy { @NotNull public abstract String getTypeName(); + public boolean showColumnHeader() { + return true; + } + /** * @return null if no strategy for this type */ @@ -60,5 +64,10 @@ public abstract class DataViewStrategy { public String getTypeName() { return "Series"; } + + @Override + public boolean showColumnHeader() { + return false; + } } } \ No newline at end of file diff --git a/python/src/com/jetbrains/python/debugger/containerview/PyDataViewerPanel.java b/python/src/com/jetbrains/python/debugger/containerview/PyDataViewerPanel.java index a61f40f4141c..3d4c187e1961 100644 --- a/python/src/com/jetbrains/python/debugger/containerview/PyDataViewerPanel.java +++ b/python/src/com/jetbrains/python/debugger/containerview/PyDataViewerPanel.java @@ -205,6 +205,7 @@ public class PyDataViewerPanel extends JPanel { if (myTable.getColumnCount() > 0) { myTable.setDefaultRenderer(myTable.getColumnClass(0), cellRenderer); } + myTable.setShowColumns(strategy.showColumnHeader()); }); } From b2ea29fcfa739736014b699ecca382c117186beb Mon Sep 17 00:00:00 2001 From: "Liana.Bakradze" Date: Fri, 19 May 2017 13:50:42 +0300 Subject: [PATCH 045/136] incorrect name (PY-21763) --- .../pydevSrc/com/jetbrains/python/debugger/PyDebugValue.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/pydevSrc/com/jetbrains/python/debugger/PyDebugValue.java b/python/pydevSrc/com/jetbrains/python/debugger/PyDebugValue.java index 4d2d4a9ccdfb..f6aaed10719b 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/PyDebugValue.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/PyDebugValue.java @@ -22,7 +22,7 @@ public class PyDebugValue extends XNamedValue { private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.pydev.PyDebugValue"); private static final String DATA_FRAME = "DataFrame"; private static final String SERIES = "Series"; - private static final Map EVALUATOR_PREFIXES = + private static final Map EVALUATOR_POSTFIXES = ContainerUtil.newHashMap(Pair.create("ndarray", "Array"), Pair.create(DATA_FRAME, DATA_FRAME), Pair.create(SERIES, SERIES)); public static final int MAX_VALUE = 256; @@ -213,7 +213,7 @@ public class PyDebugValue extends XNamedValue { private void setFullValueEvaluator(XValueNode node, String value) { String treeName = getFullTreeName(); - String postfix = EVALUATOR_PREFIXES.get(myType); + String postfix = EVALUATOR_POSTFIXES.get(myType); if (postfix == null) { if (value.length() >= MAX_VALUE) { node.setFullValueEvaluator(new PyFullValueEvaluator(myFrameAccessor, treeName)); From d954222265fc046c96aefa53b07782352940ce61 Mon Sep 17 00:00:00 2001 From: "Liana.Bakradze" Date: Fri, 19 May 2017 13:56:51 +0300 Subject: [PATCH 046/136] code cleanup (PY-21763) --- .../containerview/DataViewStrategy.java | 16 ++-------- .../dataframe/SeriesViewStrategy.java | 31 +++++++++++++++++++ 2 files changed, 33 insertions(+), 14 deletions(-) create mode 100644 python/src/com/jetbrains/python/debugger/dataframe/SeriesViewStrategy.java diff --git a/python/src/com/jetbrains/python/debugger/containerview/DataViewStrategy.java b/python/src/com/jetbrains/python/debugger/containerview/DataViewStrategy.java index 18e53d8d8ad1..9f1538ccbe04 100644 --- a/python/src/com/jetbrains/python/debugger/containerview/DataViewStrategy.java +++ b/python/src/com/jetbrains/python/debugger/containerview/DataViewStrategy.java @@ -21,6 +21,7 @@ import com.jetbrains.python.debugger.PyDebugValue; import com.jetbrains.python.debugger.array.ArrayViewStrategy; import com.jetbrains.python.debugger.array.AsyncArrayTableModel; import com.jetbrains.python.debugger.dataframe.DataFrameViewStrategy; +import com.jetbrains.python.debugger.dataframe.SeriesViewStrategy; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -29,7 +30,7 @@ import java.util.Set; public abstract class DataViewStrategy { private static class StrategyHolder { - private static final Set STRATEGIES = ContainerUtil.newHashSet(new ArrayViewStrategy(), new DataFrameViewStrategy(), new DataFrameViewStrategy(), new SeriesViewStrategy()); + private static final Set STRATEGIES = ContainerUtil.newHashSet(new ArrayViewStrategy(), new DataFrameViewStrategy(), new SeriesViewStrategy()); } public abstract AsyncArrayTableModel createTableModel(int rowCount, int columnCount, @NotNull PyDataViewerPanel panel, @NotNull PyDebugValue debugValue); @@ -57,17 +58,4 @@ public abstract class DataViewStrategy { } return null; } - - private static class SeriesViewStrategy extends DataFrameViewStrategy { - @NotNull - @Override - public String getTypeName() { - return "Series"; - } - - @Override - public boolean showColumnHeader() { - return false; - } - } } \ No newline at end of file diff --git a/python/src/com/jetbrains/python/debugger/dataframe/SeriesViewStrategy.java b/python/src/com/jetbrains/python/debugger/dataframe/SeriesViewStrategy.java new file mode 100644 index 000000000000..1775ef163883 --- /dev/null +++ b/python/src/com/jetbrains/python/debugger/dataframe/SeriesViewStrategy.java @@ -0,0 +1,31 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.jetbrains.python.debugger.dataframe; + +import org.jetbrains.annotations.NotNull; + +public class SeriesViewStrategy extends DataFrameViewStrategy { + @NotNull + @Override + public String getTypeName() { + return "Series"; + } + + @Override + public boolean showColumnHeader() { + return false; + } +} From b12fa91e404c4b97e67b1992f0e1c2b1ffa5f038 Mon Sep 17 00:00:00 2001 From: "Liana.Bakradze" Date: Fri, 19 May 2017 14:23:46 +0300 Subject: [PATCH 047/136] add env test for series (PY-21763) --- python/testData/debug/test_series.py | 8 ++++++++ .../env/python/PythonDataViewerTest.java | 15 +++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 python/testData/debug/test_series.py diff --git a/python/testData/debug/test_series.py b/python/testData/debug/test_series.py new file mode 100644 index 000000000000..151c1211927d --- /dev/null +++ b/python/testData/debug/test_series.py @@ -0,0 +1,8 @@ +import pandas as pd +import numpy as np + +frame = pd.DataFrame(data=np.random.randint(0, high=10, size=(4, 2)), columns=['a', 'b'], index=pd.MultiIndex([['s', 'd'], [2, 3]], [[0, 0, 1, 1], [0, 1, 0, 1]])) + +series = frame.a + +print(series) # line 7 diff --git a/python/testSrc/com/jetbrains/env/python/PythonDataViewerTest.java b/python/testSrc/com/jetbrains/env/python/PythonDataViewerTest.java index 7686c210bf46..81ffcd89ccad 100644 --- a/python/testSrc/com/jetbrains/env/python/PythonDataViewerTest.java +++ b/python/testSrc/com/jetbrains/env/python/PythonDataViewerTest.java @@ -20,6 +20,7 @@ import com.intellij.util.Consumer; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebuggerTestUtil; import com.jetbrains.env.PyEnvTestCase; +import com.jetbrains.env.Staging; import com.jetbrains.env.python.debug.PyDebuggerTask; import com.jetbrains.python.debugger.ArrayChunk; import com.jetbrains.python.debugger.PyDebugValue; @@ -75,6 +76,20 @@ public class PythonDataViewerTest extends PyEnvTestCase { }); } + @Test + @Staging + public void testSeries() throws Exception { + runPythonTest(new PyDataFrameDebuggerTask(getRelativeTestDataPath(), "test_series.py", ImmutableSet.of(7)) { + @Override + public void testing() throws Exception { + doTest("series", 4, 1, arrayChunk -> { + List labels = arrayChunk.getRowLabels(); + assertSameElements(labels, "s/2", "s/3", "d/2", "d/3"); + }); + } + }); + } + private static class PyDataFrameDebuggerTask extends PyDebuggerTask { private Set myLines; From d2848fbcfcbd9748305ed61387525bd3760fd412 Mon Sep 17 00:00:00 2001 From: "Liana.Bakradze" Date: Tue, 30 May 2017 12:37:40 +0300 Subject: [PATCH 048/136] use immutable collections (PY-21763) --- .../com/jetbrains/python/debugger/PyDebugValue.java | 6 ++---- .../python/debugger/containerview/DataViewStrategy.java | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/python/pydevSrc/com/jetbrains/python/debugger/PyDebugValue.java b/python/pydevSrc/com/jetbrains/python/debugger/PyDebugValue.java index f6aaed10719b..546da2bd248d 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/PyDebugValue.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/PyDebugValue.java @@ -1,11 +1,10 @@ package com.jetbrains.python.debugger; import com.google.common.base.Strings; +import com.google.common.collect.ImmutableMap; import com.intellij.icons.AllIcons; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.util.Pair; -import com.intellij.util.containers.ContainerUtil; import com.intellij.xdebugger.frame.*; import com.jetbrains.python.debugger.pydev.PyVariableLocator; import org.jetbrains.annotations.NotNull; @@ -22,8 +21,7 @@ public class PyDebugValue extends XNamedValue { private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.pydev.PyDebugValue"); private static final String DATA_FRAME = "DataFrame"; private static final String SERIES = "Series"; - private static final Map EVALUATOR_POSTFIXES = - ContainerUtil.newHashMap(Pair.create("ndarray", "Array"), Pair.create(DATA_FRAME, DATA_FRAME), Pair.create(SERIES, SERIES)); + private static final Map EVALUATOR_POSTFIXES = ImmutableMap.of("ndarray", "Array", DATA_FRAME, DATA_FRAME, SERIES, SERIES); public static final int MAX_VALUE = 256; public static final String RETURN_VALUES_PREFIX = "__pydevd_ret_val_dict"; diff --git a/python/src/com/jetbrains/python/debugger/containerview/DataViewStrategy.java b/python/src/com/jetbrains/python/debugger/containerview/DataViewStrategy.java index 9f1538ccbe04..7f40c9caca3c 100644 --- a/python/src/com/jetbrains/python/debugger/containerview/DataViewStrategy.java +++ b/python/src/com/jetbrains/python/debugger/containerview/DataViewStrategy.java @@ -15,7 +15,7 @@ */ package com.jetbrains.python.debugger.containerview; -import com.intellij.util.containers.ContainerUtil; +import com.google.common.collect.ImmutableSet; import com.jetbrains.python.debugger.ArrayChunk; import com.jetbrains.python.debugger.PyDebugValue; import com.jetbrains.python.debugger.array.ArrayViewStrategy; @@ -30,7 +30,7 @@ import java.util.Set; public abstract class DataViewStrategy { private static class StrategyHolder { - private static final Set STRATEGIES = ContainerUtil.newHashSet(new ArrayViewStrategy(), new DataFrameViewStrategy(), new SeriesViewStrategy()); + private static final Set STRATEGIES = ImmutableSet.of(new ArrayViewStrategy(), new DataFrameViewStrategy(), new SeriesViewStrategy()); } public abstract AsyncArrayTableModel createTableModel(int rowCount, int columnCount, @NotNull PyDataViewerPanel panel, @NotNull PyDebugValue debugValue); From 32708887c48861b65873c8fd60e47f8bb3fd6417 Mon Sep 17 00:00:00 2001 From: "Liana.Bakradze" Date: Tue, 30 May 2017 13:31:32 +0300 Subject: [PATCH 049/136] inline function (PY-21763) --- python/helpers/pydev/_pydevd_bundle/pydevd_vars.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py b/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py index ed693bc35674..2e896252409b 100644 --- a/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py +++ b/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py @@ -414,19 +414,13 @@ def change_attr_expression(thread_id, frame_id, attr, expression, dbg, value=SEN MAXIMUM_ARRAY_SIZE = 100 -def array_to_xml_converter(array, name, roffset, coffset, rows, cols, format): - array, metaxml, r, c, f = array_to_meta_xml(array, name, format) - xml = metaxml +def array_to_xml(array, name, roffset, coffset, rows, cols, format): + array, xml, r, c, f = array_to_meta_xml(array, name, format) format = '%' + f if rows == -1 and cols == -1: rows = r cols = c - xml += array_to_xml(array, roffset, coffset, rows, cols, format) - return xml - -def array_to_xml(array, roffset, coffset, rows, cols, format): - xml = "" rows = min(rows, MAXIMUM_ARRAY_SIZE) cols = min(cols, MAXIMUM_ARRAY_SIZE) @@ -616,7 +610,7 @@ def header_data_to_xml(rows, cols, dtypes, col_bounds, col_to_format, df, dim): xml += "\n" return xml -TYPE_TO_XML_CONVERTERS = {"ndarray": array_to_xml_converter, "DataFrame": dataframe_to_xml, "Series": dataframe_to_xml} +TYPE_TO_XML_CONVERTERS = {"ndarray": array_to_xml, "DataFrame": dataframe_to_xml, "Series": dataframe_to_xml} def table_like_struct_to_xml(array, name, roffset, coffset, rows, cols, format): From 358409aa28370490b6381a8f9058daea34a9b6a2 Mon Sep 17 00:00:00 2001 From: Konstantin Ulitin Date: Mon, 5 Jun 2017 18:57:19 +0300 Subject: [PATCH 050/136] use base language BreadcrumbsProvider when exact is not available --- .../xml/breadcrumbs/BreadcrumbsXmlWrapper.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java b/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java index 372035d8794d..5f0d72f7af76 100644 --- a/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java +++ b/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java @@ -493,12 +493,16 @@ public class BreadcrumbsXmlWrapper extends JComponent implements Disposable { @Nullable private static BreadcrumbsProvider getInfoProvider(@NotNull Language language) { - for (BreadcrumbsProvider provider : BreadcrumbsProvider.EP_NAME.getExtensions()) { - for (Language supported : provider.getLanguages()) { - if (supported.isKindOf(language)) { - return provider; + BreadcrumbsProvider[] providers = BreadcrumbsProvider.EP_NAME.getExtensions(); + while (language != null) { + for (BreadcrumbsProvider provider : providers) { + for (Language supported : provider.getLanguages()) { + if (language.is(supported)) { + return provider; + } } } + language = language.getBaseLanguage(); } return null; } From fc44d1a3e4bb17dd0c48233013c42db6c1e8007a Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Mon, 5 Jun 2017 19:24:36 +0300 Subject: [PATCH 051/136] convert to threadlocal: fix variable access inside thread local initializer (IDEA-173748) --- .../VariableAccessFromInnerClassFix.java | 50 ++++++++----- .../rules/ThreadLocalConversionRule.java | 35 ++++++++- .../rules/TypeConversionRuleUtil.java | 74 +++++++++++++++++++ .../ConvertToThreadLocalIntentionTest.java | 5 -- ...rFieldAssignmentFromNonFinalParameter.java | 17 +++++ ...eFieldAssignmentFromNonFinalParameter.java | 11 +++ 6 files changed, 168 insertions(+), 24 deletions(-) create mode 100644 java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/TypeConversionRuleUtil.java create mode 100644 java/typeMigration/testData/intentions/threadLocal/afterFieldAssignmentFromNonFinalParameter.java create mode 100644 java/typeMigration/testData/intentions/threadLocal/beforeFieldAssignmentFromNonFinalParameter.java diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/VariableAccessFromInnerClassFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/VariableAccessFromInnerClassFix.java index 532199968579..06b50ce80d31 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/VariableAccessFromInnerClassFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/VariableAccessFromInnerClassFix.java @@ -114,7 +114,7 @@ public class VariableAccessFromInnerClassFix implements IntentionAction { makeArray(); break; case COPY_TO_FINAL: - copyToFinal(); + copyToFinal(myVariable, myContext); break; } } @@ -129,7 +129,7 @@ public class VariableAccessFromInnerClassFix implements IntentionAction { private void makeArray() { for (PsiVariable var : getVariablesToFix()) { - makeArray(var); + makeArray(var, myContext); } } @@ -165,11 +165,11 @@ public class VariableAccessFromInnerClassFix implements IntentionAction { } } - private void makeArray(PsiVariable variable) throws IncorrectOperationException { + private static void makeArray(PsiVariable variable, PsiElement context) throws IncorrectOperationException { variable.normalizeDeclaration(); PsiType type = variable.getType(); - PsiElementFactory factory = JavaPsiFacade.getInstance(myContext.getProject()).getElementFactory(); + PsiElementFactory factory = JavaPsiFacade.getInstance(context.getProject()).getElementFactory(); PsiType newType = type.createArrayType(); PsiDeclarationStatement variableDeclarationStatement; @@ -199,22 +199,22 @@ public class VariableAccessFromInnerClassFix implements IntentionAction { variable.replace(newVariable); } - private void copyToFinal() throws IncorrectOperationException { - PsiManager psiManager = myContext.getManager(); + private static void copyToFinal(PsiVariable variable, PsiElement context) throws IncorrectOperationException { + PsiManager psiManager = context.getManager(); final Project project = psiManager.getProject(); PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory(); - PsiExpression initializer = factory.createExpressionFromText(myVariable.getName(), myContext); - String newName = suggestNewName(project, myVariable); - PsiType type = myVariable.getType(); + PsiExpression initializer = factory.createExpressionFromText(variable.getName(), context); + String newName = suggestNewName(project, variable); + PsiType type = variable.getType(); PsiDeclarationStatement copyDecl = factory.createVariableDeclarationStatement(newName, type, initializer); PsiVariable newVariable = (PsiVariable)copyDecl.getDeclaredElements()[0]; final boolean mustBeFinal = - !PsiUtil.isLanguageLevel8OrHigher(myContext) || CodeStyleSettingsManager.getSettings(project).GENERATE_FINAL_LOCALS; + !PsiUtil.isLanguageLevel8OrHigher(context) || CodeStyleSettingsManager.getSettings(project).GENERATE_FINAL_LOCALS; PsiUtil.setModifierProperty(newVariable, PsiModifier.FINAL, mustBeFinal); - PsiElement statement = getStatementToInsertBefore(); + PsiElement statement = getStatementToInsertBefore(variable, context); if (statement == null) return; - PsiExpression newExpression = factory.createExpressionFromText(newName, myVariable); - replaceReferences(myContext, myVariable, newExpression); + PsiExpression newExpression = factory.createExpressionFromText(newName, variable); + replaceReferences(context, variable, newExpression); if (RefactoringUtil.isLoopOrIf(statement.getParent())) { RefactoringUtil.putStatementInLoopBody(copyDecl, statement.getParent(), statement); } else { @@ -222,12 +222,12 @@ public class VariableAccessFromInnerClassFix implements IntentionAction { } } - private PsiElement getStatementToInsertBefore() { - PsiElement declarationScope = myVariable instanceof PsiParameter - ? ((PsiParameter)myVariable).getDeclarationScope() : PsiUtil.getVariableCodeBlock(myVariable, null); + private static PsiElement getStatementToInsertBefore(PsiVariable variable, PsiElement context) { + PsiElement declarationScope = variable instanceof PsiParameter + ? ((PsiParameter)variable).getDeclarationScope() : PsiUtil.getVariableCodeBlock(variable, null); if (declarationScope == null) return null; - PsiElement statement = myContext; + PsiElement statement = context; nextInnerClass: do { statement = RefactoringUtil.getParentStatement(statement, false); @@ -361,4 +361,20 @@ public class VariableAccessFromInnerClassFix implements IntentionAction { public boolean startInWriteAction() { return false; } + + public static void fixAccess(@NotNull PsiVariable variable, @NotNull PsiElement context) { + int type = getQuickFixType(variable); + if (type == -1) return; + switch (type) { + case MAKE_FINAL: + PsiUtil.setModifierProperty(variable, PsiModifier.FINAL, true); + break; + case MAKE_ARRAY: + makeArray(variable, context); + break; + case COPY_TO_FINAL: + copyToFinal(variable, context); + break; + } + } } diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/ThreadLocalConversionRule.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/ThreadLocalConversionRule.java index e403dfb45e99..db91ae18d825 100644 --- a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/ThreadLocalConversionRule.java +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/ThreadLocalConversionRule.java @@ -1,5 +1,7 @@ package com.intellij.refactoring.typeMigration.rules; +import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil; +import com.intellij.codeInsight.daemon.impl.quickfix.VariableAccessFromInnerClassFix; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Comparing; import com.intellij.psi.*; @@ -8,10 +10,14 @@ import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; import com.intellij.refactoring.typeMigration.TypeConversionDescriptor; import com.intellij.refactoring.typeMigration.TypeConversionDescriptorBase; +import com.intellij.refactoring.typeMigration.TypeEvaluator; import com.intellij.refactoring.typeMigration.TypeMigrationLabeler; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.List; + public class ThreadLocalConversionRule extends TypeConversionRule { private static final Logger LOG = Logger.getInstance(ThreadLocalConversionRule.class); @@ -150,7 +156,9 @@ public class ThreadLocalConversionRule extends TypeConversionRule { public static TypeConversionDescriptor wrapWithNewExpression(PsiType to, PsiType from, PsiExpression initializer) { final String boxedTypeName = from instanceof PsiPrimitiveType ? ((PsiPrimitiveType)from).getBoxedTypeName() : from.getCanonicalText(); - return new TypeConversionDescriptor("$qualifier$", "new " + + List toMakeFinal = TypeConversionRuleUtil.getVariablesToMakeFinal(initializer); + if (toMakeFinal == null) return null; + return new WrappingWithInnerClassOrLambdaDescriptor("$qualifier$", "new " + to.getCanonicalText() + "() {\n" + "@Override \n" + @@ -167,7 +175,8 @@ public class ThreadLocalConversionRule extends TypeConversionRule { ")" : initializer.getText())) + ";\n" + "}\n" + - "}", initializer); + "}", initializer, + toMakeFinal); } private static String toPrimitive(String replaceByArg, PsiType from, PsiElement context) { @@ -221,5 +230,27 @@ public class ThreadLocalConversionRule extends TypeConversionRule { return toBoxed(arg, from, context); } + private static class WrappingWithInnerClassOrLambdaDescriptor extends TypeConversionDescriptor { + private final List myVariablesToMakeFinal; + private WrappingWithInnerClassOrLambdaDescriptor(@NonNls final String stringToReplace, + @NonNls final String replaceByString, + final PsiExpression expression, + @NotNull List toMakeFinal) { + super(stringToReplace, replaceByString, expression); + myVariablesToMakeFinal = toMakeFinal; + } + + @Override + public PsiExpression replace(PsiExpression expression, @NotNull TypeEvaluator evaluator) { + PsiExpression replaced = super.replace(expression, evaluator); + boolean atLeastJava8 = PsiUtil.isLanguageLevel8OrHigher(replaced); + for (PsiVariable var : myVariablesToMakeFinal) { + if (!atLeastJava8 || !HighlightControlFlowUtil.isEffectivelyFinal(var, replaced, null)) { + VariableAccessFromInnerClassFix.fixAccess(var, replaced); + } + } + return replaced; + } + } } \ No newline at end of file diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/TypeConversionRuleUtil.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/TypeConversionRuleUtil.java new file mode 100644 index 000000000000..6b4af6fdefa7 --- /dev/null +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/TypeConversionRuleUtil.java @@ -0,0 +1,74 @@ +/* + * Copyright 2000-2017 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.refactoring.typeMigration.rules; + +import com.intellij.psi.*; +import com.intellij.psi.controlFlow.*; +import com.intellij.psi.util.PsiTreeUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +class TypeConversionRuleUtil { + static List getVariablesToMakeFinal(@NotNull PsiExpression expression) { + final ControlFlow controlFlow; + try { + controlFlow = ControlFlowFactory.getInstance(expression.getProject()).getControlFlow(expression, new MyControlFlowPolicy(expression), false, false); + } + catch (AnalysisCanceledException e) { + return null; + } + + Collection writtenVariables = ControlFlowUtil.getWrittenVariables(controlFlow, 0, controlFlow.getSize(), false); + if (!writtenVariables.isEmpty()) return null; + + return ControlFlowUtil.getUsedVariables(controlFlow, 0, controlFlow.getSize()) + .stream() + .filter(v -> !v.hasModifierProperty(PsiModifier.FINAL)) + .collect(Collectors.toList()); + } + + private static class MyControlFlowPolicy implements ControlFlowPolicy { + private final PsiElement myElement; + + public MyControlFlowPolicy(PsiElement element) {myElement = element;} + + @Override + public PsiVariable getUsedVariable(@NotNull PsiReferenceExpression refExpr) { + if (refExpr.isQualified()) return null; + + PsiElement refElement = refExpr.resolve(); + if ((refElement instanceof PsiLocalVariable || refElement instanceof PsiParameter) && + !PsiTreeUtil.isAncestor(myElement, refElement, true)) { + return (PsiVariable) refElement; + } + + return null; + } + + @Override + public boolean isParameterAccepted(@NotNull PsiParameter psiParameter) { + return true; + } + + @Override + public boolean isLocalVariableAccepted(@NotNull PsiLocalVariable psiVariable) { + return true; + } + } +} diff --git a/java/typeMigration/test/com/intellij/codeInsight/ConvertToThreadLocalIntentionTest.java b/java/typeMigration/test/com/intellij/codeInsight/ConvertToThreadLocalIntentionTest.java index e45b26ba77c8..6a358bbbb2d1 100644 --- a/java/typeMigration/test/com/intellij/codeInsight/ConvertToThreadLocalIntentionTest.java +++ b/java/typeMigration/test/com/intellij/codeInsight/ConvertToThreadLocalIntentionTest.java @@ -8,11 +8,6 @@ import org.jetbrains.annotations.NotNull; * @author anna */ public class ConvertToThreadLocalIntentionTest extends LightQuickFixParameterizedTestCase { - @Override - protected boolean shouldBeAvailableAfterExecution() { - return true; - } - @Override protected String getBasePath() { return "/intentions/threadLocal"; diff --git a/java/typeMigration/testData/intentions/threadLocal/afterFieldAssignmentFromNonFinalParameter.java b/java/typeMigration/testData/intentions/threadLocal/afterFieldAssignmentFromNonFinalParameter.java new file mode 100644 index 000000000000..f0531b222ae9 --- /dev/null +++ b/java/typeMigration/testData/intentions/threadLocal/afterFieldAssignmentFromNonFinalParameter.java @@ -0,0 +1,17 @@ +// "Convert to ThreadLocal" "true" +class Main { + private final ThreadLocal property; + + Main3(boolean property) {] + if (property) { + property = false; + } + boolean finalProperty = property; + this.property = new ThreadLocal() { + @Override + protected Boolean initialValue() { + return finalProperty; + } + }; + } +} \ No newline at end of file diff --git a/java/typeMigration/testData/intentions/threadLocal/beforeFieldAssignmentFromNonFinalParameter.java b/java/typeMigration/testData/intentions/threadLocal/beforeFieldAssignmentFromNonFinalParameter.java new file mode 100644 index 000000000000..4e1ffd387df8 --- /dev/null +++ b/java/typeMigration/testData/intentions/threadLocal/beforeFieldAssignmentFromNonFinalParameter.java @@ -0,0 +1,11 @@ +// "Convert to ThreadLocal" "true" +class Main { + private final boolean property; + + Main3(boolean property) {] + if (property) { + property = false; + } + this.property = property; + } +} \ No newline at end of file From ca188f4863c129aed2b1b84393937c633f6239f3 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Mon, 5 Jun 2017 19:29:16 +0300 Subject: [PATCH 052/136] Return Git root, not Git repository folder Follow-up to e6c8b18, which works incorrectly e.g. for worktrees. The issue found in IDEA-CR-21166 --- plugins/git4idea/src/git4idea/GitUtil.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/git4idea/src/git4idea/GitUtil.java b/plugins/git4idea/src/git4idea/GitUtil.java index 038ac6e059b4..a6d53798ac1a 100644 --- a/plugins/git4idea/src/git4idea/GitUtil.java +++ b/plugins/git4idea/src/git4idea/GitUtil.java @@ -368,8 +368,9 @@ public class GitUtil { public static VirtualFile getGitRootOrNull(@NotNull final FilePath filePath) { File root = filePath.getIOFile(); while (root != null) { - File gitDir = findGitDir(root); - if (gitDir != null) return LocalFileSystem.getInstance().findFileByIoFile(root); + if (isGitRoot(root)) { + return LocalFileSystem.getInstance().findFileByIoFile(root); + } root = root.getParentFile(); } return null; From 970c6848cf7bb8e105e77a897e6ae8fe10eebca1 Mon Sep 17 00:00:00 2001 From: Alexander Zolotov Date: Mon, 5 Jun 2017 17:30:45 +0300 Subject: [PATCH 053/136] EA-102471 --- .../xml/breadcrumbs/BreadcrumbsInitializingActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsInitializingActivity.java b/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsInitializingActivity.java index 0058cbe3babe..418da0e5308f 100644 --- a/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsInitializingActivity.java +++ b/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsInitializingActivity.java @@ -41,7 +41,7 @@ import org.jetbrains.annotations.NotNull; public class BreadcrumbsInitializingActivity implements StartupActivity, DumbAware { @Override public void runActivity(@NotNull Project project) { - if (project.isDefault() || ApplicationManager.getApplication().isUnitTestMode()) { + if (project.isDefault() || ApplicationManager.getApplication().isUnitTestMode() || project.isDisposed()) { return; } From a43c830373b63e968986c2068d5018b389aa1e90 Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Mon, 5 Jun 2017 19:36:58 +0300 Subject: [PATCH 054/136] convert to threadlocal: convert initializer to modern ThreadLocal.withInitial if java version >= 8 --- .../rules/ThreadLocalConversionRule.java | 48 ++++++++++++------- .../intentions/threadLocal/after1.java | 7 +-- .../intentions/threadLocal/after2.java | 7 +-- .../intentions/threadLocal/after3.java | 7 +-- .../intentions/threadLocal/after4.java | 7 +-- .../intentions/threadLocal/after5.java | 7 +-- .../intentions/threadLocal/after6.java | 7 +-- .../intentions/threadLocal/after7.java | 7 +-- .../intentions/threadLocal/after8.java | 7 +-- .../threadLocal/afterArrayInitializer.java | 7 +-- ...rFieldAssignmentFromNonFinalParameter.java | 9 +--- .../threadLocal/afterFinalField.java | 7 +-- .../intentions/threadLocal/afterJava6.java | 13 +++++ .../threadLocal/afterNormalize.java | 7 +-- .../afterPrimitiveNoInitializer.java | 7 +-- .../intentions/threadLocal/afterTA1.java | 7 +-- ...eFieldAssignmentFromNonFinalParameter.java | 2 +- .../intentions/threadLocal/beforeJava6.java | 8 ++++ 18 files changed, 67 insertions(+), 104 deletions(-) create mode 100644 java/typeMigration/testData/intentions/threadLocal/afterJava6.java create mode 100644 java/typeMigration/testData/intentions/threadLocal/beforeJava6.java diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/ThreadLocalConversionRule.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/ThreadLocalConversionRule.java index db91ae18d825..bf7e2cc746aa 100644 --- a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/ThreadLocalConversionRule.java +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/ThreadLocalConversionRule.java @@ -158,27 +158,39 @@ public class ThreadLocalConversionRule extends TypeConversionRule { final String boxedTypeName = from instanceof PsiPrimitiveType ? ((PsiPrimitiveType)from).getBoxedTypeName() : from.getCanonicalText(); List toMakeFinal = TypeConversionRuleUtil.getVariablesToMakeFinal(initializer); if (toMakeFinal == null) return null; - return new WrappingWithInnerClassOrLambdaDescriptor("$qualifier$", "new " + - to.getCanonicalText() + - "() {\n" + - "@Override \n" + - "protected " + - boxedTypeName + - " initialValue() {\n" + - " return " + - (PsiUtil.isLanguageLevel5OrHigher(initializer) - ? initializer.getText() - : (from instanceof PsiPrimitiveType ? "new " + - ((PsiPrimitiveType)from).getBoxedTypeName() + - "(" + - initializer.getText() + - ")" : initializer.getText())) + - ";\n" + - "}\n" + - "}", initializer, + return new WrappingWithInnerClassOrLambdaDescriptor("$qualifier$", + createThreadLocalInitializerReplacement(to, from, initializer, boxedTypeName), + initializer, toMakeFinal); } + private static String createThreadLocalInitializerReplacement(PsiType to, + PsiType from, + PsiExpression initializer, + String boxedTypeName) { + if (PsiUtil.isLanguageLevel8OrHigher(initializer)) { + return "java.lang.ThreadLocal.withInitial(() -> " + initializer.getText() + ")"; + } + return "new " + + to.getCanonicalText() + + "() {\n" + + "@Override \n" + + "protected " + + boxedTypeName + + " initialValue() {\n" + + " return " + + (PsiUtil.isLanguageLevel5OrHigher(initializer) + ? initializer.getText() + : (from instanceof PsiPrimitiveType ? "new " + + ((PsiPrimitiveType)from).getBoxedTypeName() + + "(" + + initializer.getText() + + ")" : initializer.getText())) + + ";\n" + + "}\n" + + "}"; + } + private static String toPrimitive(String replaceByArg, PsiType from, PsiElement context) { return PsiUtil.isLanguageLevel5OrHigher(context) ? replaceByArg diff --git a/java/typeMigration/testData/intentions/threadLocal/after1.java b/java/typeMigration/testData/intentions/threadLocal/after1.java index b5df8fd35214..be391d4abec4 100644 --- a/java/typeMigration/testData/intentions/threadLocal/after1.java +++ b/java/typeMigration/testData/intentions/threadLocal/after1.java @@ -1,11 +1,6 @@ // "Convert to ThreadLocal" "true" class Test { - final ThreadLocal field = new ThreadLocal() { - @Override - protected Integer initialValue() { - return 0; - } - }; + final ThreadLocal field = ThreadLocal.withInitial(() -> 0); void foo() { field.set(field.get() + 1); } diff --git a/java/typeMigration/testData/intentions/threadLocal/after2.java b/java/typeMigration/testData/intentions/threadLocal/after2.java index 3a546c5b0c9a..5f02c2f3925d 100644 --- a/java/typeMigration/testData/intentions/threadLocal/after2.java +++ b/java/typeMigration/testData/intentions/threadLocal/after2.java @@ -1,11 +1,6 @@ // "Convert to ThreadLocal" "true" class Test { - final ThreadLocal field = new ThreadLocal() { - @Override - protected String initialValue() { - return ""; - } - }; + final ThreadLocal field = ThreadLocal.withInitial(() -> ""); void foo() { System.out.println(field.get()); } diff --git a/java/typeMigration/testData/intentions/threadLocal/after3.java b/java/typeMigration/testData/intentions/threadLocal/after3.java index 9fc0f825ac4b..2e2f950d899a 100644 --- a/java/typeMigration/testData/intentions/threadLocal/after3.java +++ b/java/typeMigration/testData/intentions/threadLocal/after3.java @@ -1,11 +1,6 @@ // "Convert to ThreadLocal" "true" class Test { - final ThreadLocal field = new ThreadLocal() { - @Override - protected Integer initialValue() { - return new Integer(0); - } - }; + final ThreadLocal field = ThreadLocal.withInitial(() -> new Integer(0)); void foo() { if (field.get() == null) return; } diff --git a/java/typeMigration/testData/intentions/threadLocal/after4.java b/java/typeMigration/testData/intentions/threadLocal/after4.java index 8410a1c33c0e..4dbc67bb1b47 100644 --- a/java/typeMigration/testData/intentions/threadLocal/after4.java +++ b/java/typeMigration/testData/intentions/threadLocal/after4.java @@ -1,11 +1,6 @@ // "Convert to ThreadLocal" "true" class Test { - final ThreadLocal field = new ThreadLocal() { - @Override - protected String initialValue() { - return ""; - } - }; + final ThreadLocal field = ThreadLocal.withInitial(() -> ""); void foo() { if (field.get().indexOf("a") == -1) return; } diff --git a/java/typeMigration/testData/intentions/threadLocal/after5.java b/java/typeMigration/testData/intentions/threadLocal/after5.java index 435e1b6a50e6..31ca64fbaaef 100644 --- a/java/typeMigration/testData/intentions/threadLocal/after5.java +++ b/java/typeMigration/testData/intentions/threadLocal/after5.java @@ -1,11 +1,6 @@ // "Convert to ThreadLocal" "true" class Test { - final ThreadLocal field = new ThreadLocal() { - @Override - protected Integer initialValue() { - return new Integer(0); - } - }; + final ThreadLocal field = ThreadLocal.withInitial(() -> new Integer(0)); void foo(Test t) { if (t.field.get() == null) return; } diff --git a/java/typeMigration/testData/intentions/threadLocal/after6.java b/java/typeMigration/testData/intentions/threadLocal/after6.java index 1879a46be16d..d153a6579061 100644 --- a/java/typeMigration/testData/intentions/threadLocal/after6.java +++ b/java/typeMigration/testData/intentions/threadLocal/after6.java @@ -2,11 +2,6 @@ class Test { static final ThreadLocal field; static { - field = new ThreadLocal() { - @Override - protected Integer initialValue() { - return new Integer(0); - } - }; + field = ThreadLocal.withInitial(() -> new Integer(0)); } } \ No newline at end of file diff --git a/java/typeMigration/testData/intentions/threadLocal/after7.java b/java/typeMigration/testData/intentions/threadLocal/after7.java index 5398ff5e2991..14fa782d7060 100644 --- a/java/typeMigration/testData/intentions/threadLocal/after7.java +++ b/java/typeMigration/testData/intentions/threadLocal/after7.java @@ -1,11 +1,6 @@ // "Convert to ThreadLocal" "true" class X { - private final ThreadLocal bytes = new ThreadLocal() { - @Override - protected byte[] initialValue() { - return new byte[10]; - } - }; + private final ThreadLocal bytes = ThreadLocal.withInitial(() -> new byte[10]); byte foo(byte b) { bytes.get()[0] = 1; diff --git a/java/typeMigration/testData/intentions/threadLocal/after8.java b/java/typeMigration/testData/intentions/threadLocal/after8.java index d158894b6130..2d4aa4b61658 100644 --- a/java/typeMigration/testData/intentions/threadLocal/after8.java +++ b/java/typeMigration/testData/intentions/threadLocal/after8.java @@ -1,9 +1,4 @@ // "Convert to ThreadLocal" "true" class X { - final ThreadLocal i = new ThreadLocal() { - @Override - protected Integer initialValue() { - return 0; - } - }; + final ThreadLocal i = ThreadLocal.withInitial(() -> 0); } \ No newline at end of file diff --git a/java/typeMigration/testData/intentions/threadLocal/afterArrayInitializer.java b/java/typeMigration/testData/intentions/threadLocal/afterArrayInitializer.java index 59323f18e0e5..dde597cbc1da 100644 --- a/java/typeMigration/testData/intentions/threadLocal/afterArrayInitializer.java +++ b/java/typeMigration/testData/intentions/threadLocal/afterArrayInitializer.java @@ -1,9 +1,4 @@ // "Convert to ThreadLocal" "true" class Test { - final ThreadLocal field = new ThreadLocal() { - @Override - protected String[] initialValue() { - return new String[]{}; - } - }; + final ThreadLocal field = ThreadLocal.withInitial(() -> new String[]{}); } \ No newline at end of file diff --git a/java/typeMigration/testData/intentions/threadLocal/afterFieldAssignmentFromNonFinalParameter.java b/java/typeMigration/testData/intentions/threadLocal/afterFieldAssignmentFromNonFinalParameter.java index f0531b222ae9..654691a7f2c9 100644 --- a/java/typeMigration/testData/intentions/threadLocal/afterFieldAssignmentFromNonFinalParameter.java +++ b/java/typeMigration/testData/intentions/threadLocal/afterFieldAssignmentFromNonFinalParameter.java @@ -2,16 +2,11 @@ class Main { private final ThreadLocal property; - Main3(boolean property) {] + Main3(boolean property) { if (property) { property = false; } boolean finalProperty = property; - this.property = new ThreadLocal() { - @Override - protected Boolean initialValue() { - return finalProperty; - } - }; + this.property = ThreadLocal.withInitial(() -> finalProperty); } } \ No newline at end of file diff --git a/java/typeMigration/testData/intentions/threadLocal/afterFinalField.java b/java/typeMigration/testData/intentions/threadLocal/afterFinalField.java index 9575f8c84842..da8f5235f329 100644 --- a/java/typeMigration/testData/intentions/threadLocal/afterFinalField.java +++ b/java/typeMigration/testData/intentions/threadLocal/afterFinalField.java @@ -3,11 +3,6 @@ class Foo { private final ThreadLocal property; Foo(boolean property) { - this.property = new ThreadLocal() { - @Override - protected Boolean initialValue() { - return property; - } - }; + this.property = ThreadLocal.withInitial(() -> property); } } \ No newline at end of file diff --git a/java/typeMigration/testData/intentions/threadLocal/afterJava6.java b/java/typeMigration/testData/intentions/threadLocal/afterJava6.java new file mode 100644 index 000000000000..77bd711defff --- /dev/null +++ b/java/typeMigration/testData/intentions/threadLocal/afterJava6.java @@ -0,0 +1,13 @@ +// "Convert to ThreadLocal" "true" +class Main { + private final ThreadLocal property; + + Main3(final boolean property) { + this.property = new ThreadLocal() { + @Override + protected Boolean initialValue() { + return property; + } + }; + } +} \ No newline at end of file diff --git a/java/typeMigration/testData/intentions/threadLocal/afterNormalize.java b/java/typeMigration/testData/intentions/threadLocal/afterNormalize.java index 1b2532d9d1de..9c7a8342ebe8 100644 --- a/java/typeMigration/testData/intentions/threadLocal/afterNormalize.java +++ b/java/typeMigration/testData/intentions/threadLocal/afterNormalize.java @@ -1,11 +1,6 @@ // "Convert to ThreadLocal" "true" class X { - private final ThreadLocal s = new ThreadLocal() { - @Override - protected String initialValue() { - return ""; - } - }; + private final ThreadLocal s = ThreadLocal.withInitial(() -> ""); private String t; private String u; } \ No newline at end of file diff --git a/java/typeMigration/testData/intentions/threadLocal/afterPrimitiveNoInitializer.java b/java/typeMigration/testData/intentions/threadLocal/afterPrimitiveNoInitializer.java index b922e385cde7..2560a2a708d6 100644 --- a/java/typeMigration/testData/intentions/threadLocal/afterPrimitiveNoInitializer.java +++ b/java/typeMigration/testData/intentions/threadLocal/afterPrimitiveNoInitializer.java @@ -1,9 +1,4 @@ // "Convert to ThreadLocal" "true" class Test { - final ThreadLocal field = new ThreadLocal() { - @Override - protected Integer initialValue() { - return 0; - } - }; + final ThreadLocal field = ThreadLocal.withInitial(() -> 0); } \ No newline at end of file diff --git a/java/typeMigration/testData/intentions/threadLocal/afterTA1.java b/java/typeMigration/testData/intentions/threadLocal/afterTA1.java index 0e3687e146b6..d1da14e75739 100644 --- a/java/typeMigration/testData/intentions/threadLocal/afterTA1.java +++ b/java/typeMigration/testData/intentions/threadLocal/afterTA1.java @@ -5,10 +5,5 @@ import java.lang.annotation.*; public @interface TA { int value(); } class Test { - final ThreadLocal<@TA(42) Integer> field = new ThreadLocal() { - @Override - protected Integer initialValue() { - return 0; - } - }; + final ThreadLocal<@TA(42) Integer> field = ThreadLocal.withInitial(() -> 0); } \ No newline at end of file diff --git a/java/typeMigration/testData/intentions/threadLocal/beforeFieldAssignmentFromNonFinalParameter.java b/java/typeMigration/testData/intentions/threadLocal/beforeFieldAssignmentFromNonFinalParameter.java index 4e1ffd387df8..48a644f90053 100644 --- a/java/typeMigration/testData/intentions/threadLocal/beforeFieldAssignmentFromNonFinalParameter.java +++ b/java/typeMigration/testData/intentions/threadLocal/beforeFieldAssignmentFromNonFinalParameter.java @@ -2,7 +2,7 @@ class Main { private final boolean property; - Main3(boolean property) {] + Main3(boolean property) { if (property) { property = false; } diff --git a/java/typeMigration/testData/intentions/threadLocal/beforeJava6.java b/java/typeMigration/testData/intentions/threadLocal/beforeJava6.java new file mode 100644 index 000000000000..acc57b15ecde --- /dev/null +++ b/java/typeMigration/testData/intentions/threadLocal/beforeJava6.java @@ -0,0 +1,8 @@ +// "Convert to ThreadLocal" "true" +class Main { + private final boolean property; + + Main3(boolean property) { + this.property = property; + } +} \ No newline at end of file From 60966c068f657936600b9aa34361ae9b8e135947 Mon Sep 17 00:00:00 2001 From: Alexander Zolotov Date: Mon, 5 Jun 2017 19:39:11 +0300 Subject: [PATCH 055/136] CSS: remove deprecated class --- .../options/editor/CssFoldingSettings.java | 62 ------------------- .../options/editor/XmlFoldingSettings.java | 8 --- 2 files changed, 70 deletions(-) delete mode 100644 xml/xml-psi-impl/src/com/intellij/application/options/editor/CssFoldingSettings.java diff --git a/xml/xml-psi-impl/src/com/intellij/application/options/editor/CssFoldingSettings.java b/xml/xml-psi-impl/src/com/intellij/application/options/editor/CssFoldingSettings.java deleted file mode 100644 index 264eb995b2be..000000000000 --- a/xml/xml-psi-impl/src/com/intellij/application/options/editor/CssFoldingSettings.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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.application.options.editor; - -import com.intellij.openapi.components.PersistentStateComponent; -import com.intellij.openapi.components.ServiceManager; -import com.intellij.openapi.components.State; -import com.intellij.openapi.components.Storage; -import com.intellij.util.xmlb.XmlSerializerUtil; -import org.jetbrains.annotations.Nullable; - -/** - * User: zolotov - * Date: 4/18/13 - * @deprecated use {@link XmlFoldingSettings} - * todo: delete after 2017.1 release - */ -@State( - name="CssFoldingSettings", - storages= { - @Storage(value = "editor.codeinsight.xml", deprecated = true)} -) -public class CssFoldingSettings implements PersistentStateComponent { - public static CssFoldingSettings getInstance() { - return ServiceManager.getService(CssFoldingSettings.class); - } - - private boolean myCollapseDataUri = true; - - public boolean isCollapseDataUri() { - return myCollapseDataUri; - } - - @SuppressWarnings("UnusedDeclaration") - public void setCollapseDataUri(boolean value) { - myCollapseDataUri = value; - } - - @Nullable - @Override - public CssFoldingSettings getState() { - return this; - } - - @Override - public void loadState(CssFoldingSettings state) { - XmlSerializerUtil.copyBean(state, this); - } -} diff --git a/xml/xml-psi-impl/src/com/intellij/application/options/editor/XmlFoldingSettings.java b/xml/xml-psi-impl/src/com/intellij/application/options/editor/XmlFoldingSettings.java index 54e8be90eef2..96873f18e36b 100644 --- a/xml/xml-psi-impl/src/com/intellij/application/options/editor/XmlFoldingSettings.java +++ b/xml/xml-psi-impl/src/com/intellij/application/options/editor/XmlFoldingSettings.java @@ -31,14 +31,6 @@ public class XmlFoldingSettings implements XmlCodeFoldingSettings, PersistentSta return ServiceManager.getService(XmlFoldingSettings.class); } - public XmlFoldingSettings() { - // todo: remove after 2017.1 release - CssFoldingSettings cssFoldingSettings = CssFoldingSettings.getInstance(); - if (cssFoldingSettings != null) { - myState.COLLAPSE_DATA_URI = cssFoldingSettings.isCollapseDataUri(); - } - } - @Override public boolean isCollapseXmlTags() { return myState.COLLAPSE_XML_TAGS; From d115095c6a378ee44b7c9deb15c7a2d0bd6e7183 Mon Sep 17 00:00:00 2001 From: Nadya Zabrodina Date: Tue, 30 May 2017 14:44:10 +0300 Subject: [PATCH 056/136] [branchPopup]: add toolbar with Restore Default Size action * (IDEA-168078) Option to Reset Branches widget size to auto-size; * (IDEA-168355) Remote branches status bar popup: UI has gotten worse; --- .../dvcs/ui/BranchActionGroupPopup.java | 39 ++++++++++++++++-- platform/icons/src/vcs/restoreDefaultSize.png | Bin 0 -> 143 bytes .../icons/src/vcs/restoreDefaultSize@2x.png | Bin 0 -> 216 bytes .../src/vcs/restoreDefaultSize@2x_dark.png | Bin 0 -> 217 bytes .../icons/src/vcs/restoreDefaultSize_dark.png | Bin 0 -> 143 bytes .../util/src/com/intellij/icons/AllIcons.java | 1 + 6 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 platform/icons/src/vcs/restoreDefaultSize.png create mode 100644 platform/icons/src/vcs/restoreDefaultSize@2x.png create mode 100644 platform/icons/src/vcs/restoreDefaultSize@2x_dark.png create mode 100644 platform/icons/src/vcs/restoreDefaultSize_dark.png diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java b/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java index 17445b9d0402..b81951c11cf2 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java @@ -15,6 +15,7 @@ */ package com.intellij.dvcs.ui; +import com.intellij.icons.AllIcons; import com.intellij.ide.DataManager; import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.actionSystem.*; @@ -28,10 +29,7 @@ import com.intellij.openapi.ui.popup.PopupStep; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.WindowStateService; import com.intellij.openapi.vcs.ui.FlatSpeedSearchPopup; -import com.intellij.ui.ErrorLabel; -import com.intellij.ui.JBColor; -import com.intellij.ui.ScrollingUtil; -import com.intellij.ui.SeparatorWithText; +import com.intellij.ui.*; import com.intellij.ui.components.panels.OpaquePanel; import com.intellij.ui.popup.KeepingPopupOpenAction; import com.intellij.ui.popup.PopupFactoryImpl; @@ -84,6 +82,39 @@ public class BranchActionGroupPopup extends FlatSpeedSearchPopup { } } trackDimensions(dimensionKey); + createTitlePanelToolbar(dimensionKey); + } + + void createTitlePanelToolbar(@Nullable String dimensionKey) { + if (dimensionKey == null) return; + AnAction restoreDefaultSizeAction = + new DumbAwareAction("Restore Size", "Restore default size for widget", AllIcons.Vcs.RestoreDefaultSize) { + @Override + public void actionPerformed(AnActionEvent e) { + WindowStateService.getInstance(myProject).putSizeFor(myProject, dimensionKey, null); + myUserSizeChanged = false; + pack(true, true); + } + + @Override + public void update(AnActionEvent e) { + e.getPresentation().setEnabledAndVisible(myUserSizeChanged || + WindowStateService.getInstance(myProject).getSizeFor(myProject, dimensionKey) != null); + } + }; + final ActionToolbar popupTitleToolbar = ActionManager.getInstance() + .createActionToolbar("BranchWidget", new DefaultActionGroup(restoreDefaultSizeAction), true); + final JComponent toolbarComponent = popupTitleToolbar.getComponent(); + popupTitleToolbar.setReservePlaceAutoPopupIcon(false); + toolbarComponent.setBorder(JBUI.Borders.emptyRight(2)); + toolbarComponent.setOpaque(false); + + getTitle().setButtonComponent(new ActiveComponent.Adapter() { + @Override + public JComponent getComponent() { + return toolbarComponent; + } + }, null); } //for child popups only diff --git a/platform/icons/src/vcs/restoreDefaultSize.png b/platform/icons/src/vcs/restoreDefaultSize.png new file mode 100644 index 0000000000000000000000000000000000000000..c3dd16b7aa697724c7c5c3edbe104baa679fc56b GIT binary patch literal 143 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`VV*9IAr-fh6C^k;T(LFe>6kxh zQKH?hGy&f(#{Z6O6P8MzWDxl0wJFVn`JZxidkpgl;}*R_Yx%n?MWmhbxPE(0V?5Ex r@=R!xNwW98BN88`tPyeJWneIUU3m4+Gp92^8yGxY{an^LB{Ts5)1okL literal 0 HcmV?d00001 diff --git a/platform/icons/src/vcs/restoreDefaultSize@2x.png b/platform/icons/src/vcs/restoreDefaultSize@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..7b9dbc291778bd127287fa77abb9936748959964 GIT binary patch literal 216 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJ`JOJ0Ar-gYPCLkXK!L~g?s47f zw=0D$rU{p@oDbt>-8zx2>wj>TLdWv&1{06WkUS7^?d-BIn&nzH^OYI=xX*tVWD1R3b;>SnWHk`tXL_tE!nC94%#S~3%4W4K^1cw? z6vy{ON;E=M<9Vfn`NQ0q8K)Ury=@uWw+o5vNMo&6ez4Gbj>3WF|5ki2Sr$bUewnxf P=u`$zS3j3^P6F}pvu^6&a|9GM33VMk3og|gGn1!V$=>);h>J1CGOeaA&Dz)bqUsBM zM}799UamDlQ$E`)nEhd!&z7eRhnCK3I2_H%6|=c%zu<=pvVH;&4%W-E?>c@v%tf#| Q66jV2Pgg&ebxsLQ04M@er~m)} literal 0 HcmV?d00001 diff --git a/platform/icons/src/vcs/restoreDefaultSize_dark.png b/platform/icons/src/vcs/restoreDefaultSize_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..ed9d8114615c3299fccd6615ab58f0474af75612 GIT binary patch literal 143 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`VV*9IAr-fh6C^k`q}(-&SFm&M zYO5 Date: Tue, 30 May 2017 18:57:39 +0300 Subject: [PATCH 057/136] [branchPopup]: track window popup size instead of its component * track size changed event and mark it as internal if it comes from 'Restore Size' action; * move track dimensions subscriptions to afterShown method, because popup window is null before show method is called; --- .../dvcs/ui/BranchActionGroupPopup.java | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java b/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java index b81951c11cf2..c9441acd93ce 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java @@ -58,11 +58,13 @@ import static com.intellij.util.ui.UIUtil.DEFAULT_VGAP; public class BranchActionGroupPopup extends FlatSpeedSearchPopup { private static final DataKey POPUP_MODEL = DataKey.create("VcsPopupModel"); + private Project myProject; private MyPopupListElementRenderer myListElementRenderer; private boolean myShown; - @NotNull private Dimension myPrevSize = JBUI.emptySize(); private boolean myUserSizeChanged; - private Project myProject; + private boolean myInternalSizeChanged; + @Nullable private final String myKey; + @NotNull private Dimension myPrevSize = JBUI.emptySize(); public BranchActionGroupPopup(@NotNull String title, @NotNull Project project, @@ -74,25 +76,24 @@ public class BranchActionGroupPopup extends FlatSpeedSearchPopup { myProject = project; DataManager.registerDataProvider(getList(), dataId -> POPUP_MODEL.is(dataId) ? getListModel() : null); installOnHoverIconsSupport(getListElementRenderer()); - if (dimensionKey != null) { - Dimension storedSize = WindowStateService.getInstance(myProject).getSizeFor(myProject, dimensionKey); + myKey = dimensionKey; + if (myKey != null) { + Dimension storedSize = WindowStateService.getInstance(myProject).getSizeFor(myProject, myKey); if (storedSize != null) { //set forced size before component is shown setSize(storedSize); } + createTitlePanelToolbar(myKey); } - trackDimensions(dimensionKey); - createTitlePanelToolbar(dimensionKey); } - void createTitlePanelToolbar(@Nullable String dimensionKey) { - if (dimensionKey == null) return; + void createTitlePanelToolbar(@NotNull String dimensionKey) { AnAction restoreDefaultSizeAction = new DumbAwareAction("Restore Size", "Restore default size for widget", AllIcons.Vcs.RestoreDefaultSize) { @Override public void actionPerformed(AnActionEvent e) { WindowStateService.getInstance(myProject).putSizeFor(myProject, dimensionKey, null); - myUserSizeChanged = false; + myInternalSizeChanged = true; pack(true, true); } @@ -120,14 +121,16 @@ public class BranchActionGroupPopup extends FlatSpeedSearchPopup { //for child popups only private BranchActionGroupPopup(@Nullable WizardPopup aParent, @NotNull ListPopupStep aStep, @Nullable Object parentValue) { super(aParent, aStep, DataContext.EMPTY_CONTEXT, parentValue); + // don't store children popup userSize; + myKey = null; DataManager.registerDataProvider(getList(), dataId -> POPUP_MODEL.is(dataId) ? getListModel() : null); installOnHoverIconsSupport(getListElementRenderer()); - // don't store children popup userSize; - trackDimensions(null); } private void trackDimensions(@Nullable String dimensionKey) { - getComponent().addComponentListener(new ComponentAdapter() { + Window popupWindow = getPopupWindow(); + if (popupWindow == null) return; + popupWindow.addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { if (myShown) { @@ -149,7 +152,7 @@ public class BranchActionGroupPopup extends FlatSpeedSearchPopup { private void processOnSizeChanged() { Dimension newSize = ObjectUtils.assertNotNull(getSize()); - if (myPrevSize.height < newSize.height) { + if (!myInternalSizeChanged && myPrevSize.height < newSize.height) { List mores = getMoreActions(); for (MoreAction more : mores) { if (!getList().getScrollableTracksViewportHeight()) break; @@ -160,7 +163,9 @@ public class BranchActionGroupPopup extends FlatSpeedSearchPopup { } } myPrevSize = newSize; - myUserSizeChanged = true; + //ugly properties to distinguish user size changed from pack method call after Restore Size action performed + myUserSizeChanged = !myInternalSizeChanged; + myInternalSizeChanged = false; } @NotNull @@ -196,6 +201,7 @@ public class BranchActionGroupPopup extends FlatSpeedSearchPopup { if (size != null) { myPrevSize = size; } + trackDimensions(myKey); } private static void createSpeedSearchActions(@NotNull ActionGroup actionGroup, From f4fec35664d097ef49f0924d44a0f6c19ce3e68a Mon Sep 17 00:00:00 2001 From: Nadya Zabrodina Date: Wed, 31 May 2017 19:44:21 +0300 Subject: [PATCH 058/136] [branchPopup]: show git current branch with other branches * (IDEA-171718) Not possible make current (checked out) branch favorite --- .../git4idea/ui/branch/GitBranchPopup.java | 18 +++++--- .../ui/branch/GitBranchPopupActions.java | 44 ++++++++++++++----- 2 files changed, 46 insertions(+), 16 deletions(-) diff --git a/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java b/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java index 30f02e80538d..747ead0b1614 100644 --- a/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java +++ b/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java @@ -122,11 +122,19 @@ class GitBranchPopup extends DvcsBranchPopup { popupGroup.addAll(createRepositoriesActions()); popupGroup.addSeparator("Common Local Branches"); - List localBranchActions = - myMultiRootBranchConfig.getLocalBranchNames().stream().map(l -> createLocalBranchActions(allRepositories, l)).filter(Objects::nonNull) - .collect(toList()); - wrapWithMoreActionIfNeeded(myProject, popupGroup, ContainerUtil.sorted(localBranchActions, FAVORITE_BRANCH_COMPARATOR), - getNumOfTopShownBranches(localBranchActions), SHOW_ALL_LOCALS_KEY, true); + List localBranchActions = myMultiRootBranchConfig.getLocalBranchNames().stream() + .map(l -> createLocalBranchActions(allRepositories, l)) + .filter(Objects::nonNull) + .sorted(FAVORITE_BRANCH_COMPARATOR) + .collect(toList()); + int topShownBranches = getNumOfTopShownBranches(localBranchActions); + String currentBranch = myMultiRootBranchConfig.getCurrentBranch(); + if (currentBranch != null) { + localBranchActions + .add(0, new GitBranchPopupActions.CurrentBranchActions(myProject, allRepositories, currentBranch, myCurrentRepository)); + topShownBranches++; + } + wrapWithMoreActionIfNeeded(myProject, popupGroup, localBranchActions, topShownBranches, SHOW_ALL_LOCALS_KEY, true); popupGroup.addSeparator("Common Remote Branches"); List remoteBranchActions = map(((GitMultiRootBranchConfig)myMultiRootBranchConfig).getRemoteBranches(), diff --git a/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopupActions.java b/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopupActions.java index 3aff81024041..f7488605b70c 100644 --- a/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopupActions.java +++ b/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopupActions.java @@ -27,6 +27,7 @@ import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.util.containers.ContainerUtil; +import git4idea.GitLocalBranch; import git4idea.branch.GitBranchUtil; import git4idea.branch.GitBrancher; import git4idea.branch.GitNewBranchOptions; @@ -74,15 +75,21 @@ class GitBranchPopupActions { } popupGroup.addSeparator("Local Branches" + repoInfo); - List localBranchActions = - myRepository.getBranches().getLocalBranches().stream() - .sorted() - .filter(branch -> !branch.equals(myRepository.getCurrentBranch())) - .map(branch -> new LocalBranchActions(myProject, repositoryList, branch.getName(), myRepository)) - .collect(toList()); + GitLocalBranch currentBranch = myRepository.getCurrentBranch(); + List localBranchActions = myRepository.getBranches().getLocalBranches().stream() + .sorted() + .filter(branch -> !branch.equals(currentBranch)) + .map(branch -> new LocalBranchActions(myProject, repositoryList, branch.getName(), myRepository)) + .sorted(FAVORITE_BRANCH_COMPARATOR) + .collect(toList()); + int topShownBranches = getNumOfTopShownBranches(localBranchActions); + if (currentBranch != null) { + localBranchActions.add(0, new CurrentBranchActions(myProject, repositoryList, currentBranch.getName(), myRepository)); + topShownBranches++; + } // if there are only a few local favorites -> show all; for remotes it's better to show only favorites; - wrapWithMoreActionIfNeeded(myProject, popupGroup, ContainerUtil.sorted(localBranchActions, FAVORITE_BRANCH_COMPARATOR), - getNumOfTopShownBranches(localBranchActions), firstLevelGroup ? GitBranchPopup.SHOW_ALL_LOCALS_KEY : null, + wrapWithMoreActionIfNeeded(myProject, popupGroup, localBranchActions, + topShownBranches, firstLevelGroup ? GitBranchPopup.SHOW_ALL_LOCALS_KEY : null, firstLevelGroup); popupGroup.addSeparator("Remote Branches" + repoInfo); @@ -160,9 +167,9 @@ class GitBranchPopupActions { */ static class LocalBranchActions extends BranchActionGroup implements PopupElementWithAdditionalInfo { - private final Project myProject; - private final List myRepositories; - private final String myBranchName; + protected final Project myProject; + protected final List myRepositories; + protected final String myBranchName; @NotNull private final GitRepository mySelectedRepository; private final GitBranchManager myGitBranchManager; @@ -310,6 +317,21 @@ class GitBranchPopupActions { } } + static class CurrentBranchActions extends LocalBranchActions { + CurrentBranchActions(@NotNull Project project, + @NotNull List repositories, + @NotNull String branchName, + @NotNull GitRepository selectedRepository) { + super(project, repositories, branchName, selectedRepository); + } + + @NotNull + @Override + public AnAction[] getChildren(@Nullable AnActionEvent e) { + return new AnAction[]{new LocalBranchActions.RenameBranchAction(myProject, myRepositories, myBranchName)}; + } + } + /** * Actions available for remote branches */ From 66fd243815a01fbb584a130aed1b7a6dcc1d4a93 Mon Sep 17 00:00:00 2001 From: Nadya Zabrodina Date: Thu, 1 Jun 2017 15:09:45 +0300 Subject: [PATCH 059/136] [branchPopup]: show hg current branch/bookmark with other branches --- .../branch/DvcsMultiRootBranchConfig.java | 8 +++- .../zmlx/hg4idea/branch/HgBranchPopup.java | 23 ++++++--- .../hg4idea/branch/HgBranchPopupActions.java | 47 ++++++++++++++++--- 3 files changed, 63 insertions(+), 15 deletions(-) diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsMultiRootBranchConfig.java b/platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsMultiRootBranchConfig.java index ee24209167fa..d999bf5334c8 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsMultiRootBranchConfig.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsMultiRootBranchConfig.java @@ -20,6 +20,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; +import java.util.function.Function; public abstract class DvcsMultiRootBranchConfig { @NotNull protected final Collection myRepositories; @@ -34,9 +35,14 @@ public abstract class DvcsMultiRootBranchConfig { @Nullable public String getCurrentBranch() { + return getCommonName(Repository::getCurrentBranchName); + } + + @Nullable + public String getCommonName(@NotNull Function nameSupplier) { String commonBranch = null; for (Repo repository : myRepositories) { - String branchName = repository.getCurrentBranchName(); + String branchName = nameSupplier.apply(repository); if (branchName == null) { return null; } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchPopup.java b/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchPopup.java index 67979119747b..cb9ef1f6a3e8 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchPopup.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchPopup.java @@ -26,7 +26,6 @@ import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Conditions; -import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.zmlx.hg4idea.HgProjectSettings; @@ -99,16 +98,26 @@ public class HgBranchPopup extends DvcsBranchPopup { List branchActions = myMultiRootBranchConfig.getLocalBranchNames().stream() .map(b -> createLocalBranchActions(allRepositories, b, false)) - .filter(Objects::nonNull).collect(toList()); - wrapWithMoreActionIfNeeded(myProject, popupGroup, ContainerUtil.sorted(branchActions, FAVORITE_BRANCH_COMPARATOR), - getNumOfTopShownBranches(branchActions), SHOW_ALL_BRANCHES_KEY, true); + .filter(Objects::nonNull).sorted(FAVORITE_BRANCH_COMPARATOR).collect(toList()); + int topShownBranches = getNumOfTopShownBranches(branchActions); + String commonBranch = myMultiRootBranchConfig.getCommonName(HgRepository::getCurrentBranch); + if (commonBranch != null) { + branchActions.add(0, new HgBranchPopupActions.CurrentBranch(myProject, allRepositories, commonBranch)); + topShownBranches++; + } + wrapWithMoreActionIfNeeded(myProject, popupGroup, branchActions, topShownBranches, SHOW_ALL_BRANCHES_KEY, true); popupGroup.addSeparator("Common Bookmarks"); List bookmarkActions = ((HgMultiRootBranchConfig)myMultiRootBranchConfig).getBookmarkNames().stream() .map(bm -> createLocalBranchActions(allRepositories, bm, true)) - .filter(Objects::nonNull).collect(toList()); - wrapWithMoreActionIfNeeded(myProject, popupGroup, ContainerUtil.sorted(bookmarkActions, FAVORITE_BRANCH_COMPARATOR), - getNumOfTopShownBranches(bookmarkActions), SHOW_ALL_BOOKMARKS_KEY, true); + .filter(Objects::nonNull).sorted(FAVORITE_BRANCH_COMPARATOR).collect(toList()); + int topShownBookmarks = getNumOfTopShownBranches(bookmarkActions); + String commonBookmark = myMultiRootBranchConfig.getCommonName(HgRepository::getCurrentBookmark); + if (commonBookmark != null) { + bookmarkActions.add(0, new HgBranchPopupActions.CurrentActiveBookmark(myProject, allRepositories, commonBookmark)); + topShownBookmarks++; + } + wrapWithMoreActionIfNeeded(myProject, popupGroup, bookmarkActions, topShownBookmarks, SHOW_ALL_BOOKMARKS_KEY, true); } @Nullable diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchPopupActions.java b/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchPopupActions.java index b80579503082..22413177ab72 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchPopupActions.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchPopupActions.java @@ -87,13 +87,19 @@ public class HgBranchPopupActions { } popupGroup.addSeparator("Bookmarks" + repoInfo); + String currentBookmark = myRepository.getCurrentBookmark(); List bookmarkActions = getSortedNamesWithoutHashes(myRepository.getBookmarks()).stream() + .filter(bm -> !bm.equals(currentBookmark)) .map(bm -> new BookmarkActions(myProject, Collections.singletonList(myRepository), bm)) + .sorted(FAVORITE_BRANCH_COMPARATOR) .collect(toList()); - // if there are only a few local favorites -> show all; for remotes it's better to show only favorites; - wrapWithMoreActionIfNeeded(myProject, popupGroup, ContainerUtil.sorted(bookmarkActions, FAVORITE_BRANCH_COMPARATOR), - getNumOfTopShownBranches(bookmarkActions), firstLevelGroup ? HgBranchPopup.SHOW_ALL_BOOKMARKS_KEY : null, - firstLevelGroup); + int topShownBookmarks = getNumOfTopShownBranches(bookmarkActions); + if (currentBookmark != null) { + bookmarkActions.add(0, new CurrentActiveBookmark(myProject, Collections.singletonList(myRepository), currentBookmark)); + topShownBookmarks++; + } + wrapWithMoreActionIfNeeded(myProject, popupGroup, bookmarkActions, topShownBookmarks, + firstLevelGroup ? HgBranchPopup.SHOW_ALL_BOOKMARKS_KEY : null, firstLevelGroup); //only opened branches have to be shown popupGroup.addSeparator("Branches" + repoInfo); @@ -102,10 +108,11 @@ public class HgBranchPopupActions { .sorted() .filter(b -> !b.equals(myRepository.getCurrentBranch())) .map(b -> new BranchActions(myProject, Collections.singletonList(myRepository), b)) + .sorted(FAVORITE_BRANCH_COMPARATOR) .collect(toList()); - wrapWithMoreActionIfNeeded(myProject, popupGroup, ContainerUtil.sorted(branchActions, FAVORITE_BRANCH_COMPARATOR), - getNumOfTopShownBranches(branchActions), firstLevelGroup ? HgBranchPopup.SHOW_ALL_BRANCHES_KEY : null, - firstLevelGroup); + branchActions.add(0, new CurrentBranch(myProject, Collections.singletonList(myRepository), myRepository.getCurrentBranch())); + wrapWithMoreActionIfNeeded(myProject, popupGroup, branchActions, getNumOfTopShownBranches(branchActions) + 1, + firstLevelGroup ? HgBranchPopup.SHOW_ALL_BRANCHES_KEY : null, firstLevelGroup); return popupGroup; } @@ -279,6 +286,19 @@ public class HgBranchPopupActions { super(project, repositories, branchName, HgBranchType.BRANCH); } } + + public static class CurrentBranch extends BranchActions{ + + public CurrentBranch(@NotNull Project project, @NotNull List repositories, @NotNull String branchName) { + super(project, repositories, branchName); + } + + @NotNull + @Override + public AnAction[] getChildren(@Nullable AnActionEvent e) { + return AnAction.EMPTY_ARRAY; + } + } /** * Actions available for bookmarks. @@ -314,4 +334,17 @@ public class HgBranchPopupActions { } } } + + public static class CurrentActiveBookmark extends BookmarkActions{ + + public CurrentActiveBookmark(@NotNull Project project, @NotNull List repositories, @NotNull String branchName) { + super(project, repositories, branchName); + } + + @NotNull + @Override + public AnAction[] getChildren(@Nullable AnActionEvent e) { + return new AnAction[]{new BookmarkActions.DeleteBookmarkAction(myProject, myRepositories, myBranchName)}; + } + } } From f36c6cfcb6a92637c13c47442b23304fd0083bcd Mon Sep 17 00:00:00 2001 From: Nadya Zabrodina Date: Thu, 1 Jun 2017 15:29:40 +0300 Subject: [PATCH 060/136] [branchPopup]: show info label for current branch/bookmark --- .../dvcs/ui/BranchActionGroupPopup.java | 32 ++++++++++++++----- .../ui/PopupElementWithAdditionalInfo.java | 5 ++- .../ui/branch/GitBranchPopupActions.java | 6 ++++ .../hg4idea/branch/HgBranchPopupActions.java | 24 +++++++++----- 4 files changed, 50 insertions(+), 17 deletions(-) diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java b/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java index c9441acd93ce..1ce059697920 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java @@ -38,6 +38,7 @@ import com.intellij.ui.popup.list.IconListPopupRenderer; import com.intellij.ui.popup.list.ListPopupImpl; import com.intellij.ui.popup.list.ListPopupModel; import com.intellij.ui.popup.list.PopupListElementRenderer; +import com.intellij.util.FontUtil; import com.intellij.util.ObjectUtils; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.JBUI; @@ -327,6 +328,7 @@ public class BranchActionGroupPopup extends FlatSpeedSearchPopup { private class MyPopupListElementRenderer extends PopupListElementRenderer implements IconListPopupRenderer { + private ErrorLabel myPrefixLabel; private ErrorLabel myInfoLabel; private IconComponent myIconLabel; @@ -365,26 +367,36 @@ public class BranchActionGroupPopup extends FlatSpeedSearchPopup { } myIconLabel.setIcon(myDescriptor.getIconFor(value)); PopupElementWithAdditionalInfo additionalInfoAction = getSpecificAction(value, PopupElementWithAdditionalInfo.class); - String infoText = additionalInfoAction != null ? additionalInfoAction.getInfoText() : null; + updateInfoComponent(myPrefixLabel, additionalInfoAction != null ? additionalInfoAction.getPrefixInfo() : null, isSelected); + updateInfoComponent(myInfoLabel, additionalInfoAction != null ? additionalInfoAction.getInfoText() : null, isSelected); + } + + private void updateInfoComponent(@NotNull ErrorLabel infoLabel, @Nullable String infoText, boolean isSelected) { if (infoText != null) { - myInfoLabel.setVisible(true); - myInfoLabel.setText(infoText); + infoLabel.setVisible(true); + infoLabel.setText(infoText); if (isSelected) { - setSelected(myInfoLabel); + setSelected(infoLabel); } else { - myInfoLabel.setBackground(getBackground()); - myInfoLabel.setForeground(JBColor.GRAY); // different foreground than for other elements + infoLabel.setBackground(getBackground()); + infoLabel.setForeground(JBColor.GRAY); // different foreground than for other elements } } else { - myInfoLabel.setVisible(false); + infoLabel.setVisible(false); } } @Override protected JComponent createItemComponent() { + myPrefixLabel = new ErrorLabel(); + myPrefixLabel.setOpaque(true); + myPrefixLabel.setBorder(JBUI.Borders.empty(1, 1, 1, DEFAULT_HGAP)); + Font minusOneFont = FontUtil.minusOne(myPrefixLabel.getFont()); + myPrefixLabel.setFont(minusOneFont); + myTextLabel = new ErrorLabel(); myTextLabel.setOpaque(true); myTextLabel.setBorder(JBUI.Borders.empty(1)); @@ -392,15 +404,19 @@ public class BranchActionGroupPopup extends FlatSpeedSearchPopup { myInfoLabel = new ErrorLabel(); myInfoLabel.setOpaque(true); myInfoLabel.setBorder(JBUI.Borders.empty(1, DEFAULT_HGAP, 1, 1)); + myInfoLabel.setFont(minusOneFont); JPanel compoundPanel = new OpaquePanel(new BorderLayout(), JBColor.WHITE); myIconLabel = new IconComponent(); myInfoLabel.setHorizontalAlignment(SwingConstants.RIGHT); + JPanel compoundTextPanel = new OpaquePanel(new BorderLayout(), compoundPanel.getBackground()); JPanel textPanel = new OpaquePanel(new BorderLayout(), compoundPanel.getBackground()); compoundPanel.add(myIconLabel, BorderLayout.WEST); textPanel.add(myTextLabel, BorderLayout.WEST); textPanel.add(myInfoLabel, BorderLayout.CENTER); - compoundPanel.add(textPanel, BorderLayout.CENTER); + compoundTextPanel.add(myPrefixLabel, BorderLayout.WEST); + compoundTextPanel.add(textPanel, BorderLayout.CENTER); + compoundPanel.add(compoundTextPanel, BorderLayout.CENTER); return layoutComponent(compoundPanel); } diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/ui/PopupElementWithAdditionalInfo.java b/platform/dvcs-impl/src/com/intellij/dvcs/ui/PopupElementWithAdditionalInfo.java index d59929095a9f..b7508b845dfb 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/ui/PopupElementWithAdditionalInfo.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/ui/PopupElementWithAdditionalInfo.java @@ -19,5 +19,8 @@ import org.jetbrains.annotations.Nullable; public interface PopupElementWithAdditionalInfo { @Nullable - String getInfoText(); + default String getInfoText() {return null;} + + @Nullable + default String getPrefixInfo() {return null;} } diff --git a/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopupActions.java b/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopupActions.java index f7488605b70c..8012860e3ac3 100644 --- a/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopupActions.java +++ b/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopupActions.java @@ -330,6 +330,12 @@ class GitBranchPopupActions { public AnAction[] getChildren(@Nullable AnActionEvent e) { return new AnAction[]{new LocalBranchActions.RenameBranchAction(myProject, myRepositories, myBranchName)}; } + + @Nullable + @Override + public String getPrefixInfo() { + return "current"; + } } /** diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchPopupActions.java b/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchPopupActions.java index 22413177ab72..1adb7c3478e3 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchPopupActions.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchPopupActions.java @@ -18,6 +18,7 @@ package org.zmlx.hg4idea.branch; import com.intellij.dvcs.DvcsUtil; import com.intellij.dvcs.repo.Repository; import com.intellij.dvcs.ui.NewBranchAction; +import com.intellij.dvcs.ui.PopupElementWithAdditionalInfo; import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.ActionGroup; import com.intellij.openapi.actionSystem.AnAction; @@ -34,7 +35,6 @@ import com.intellij.openapi.vcs.VcsBundle; import com.intellij.openapi.vcs.changes.*; import com.intellij.openapi.vcs.changes.ui.CommitChangeListDialog; import com.intellij.util.ArrayUtil; -import com.intellij.util.PlatformIcons; import com.intellij.util.containers.ContainerUtil; import com.intellij.vcs.log.Hash; import com.intellij.vcs.log.impl.HashImpl; @@ -287,8 +287,7 @@ public class HgBranchPopupActions { } } - public static class CurrentBranch extends BranchActions{ - + public static class CurrentBranch extends BranchActions implements PopupElementWithAdditionalInfo { public CurrentBranch(@NotNull Project project, @NotNull List repositories, @NotNull String branchName) { super(project, repositories, branchName); } @@ -298,6 +297,12 @@ public class HgBranchPopupActions { public AnAction[] getChildren(@Nullable AnActionEvent e) { return AnAction.EMPTY_ARRAY; } + + @Nullable + @Override + public String getPrefixInfo() { + return "current"; + } } /** @@ -307,9 +312,6 @@ public class HgBranchPopupActions { BookmarkActions(@NotNull Project project, @NotNull List repositories, @NotNull String branchName) { super(project, repositories, branchName, HgBranchType.BOOKMARK); - if (myRepositories.size() == 1 && branchName.equals(myRepositories.get(0).getCurrentBookmark())) { - getTemplatePresentation().setIcon(PlatformIcons.CHECK_ICON); - } } @NotNull @@ -335,8 +337,8 @@ public class HgBranchPopupActions { } } - public static class CurrentActiveBookmark extends BookmarkActions{ - + public static class CurrentActiveBookmark extends BookmarkActions implements PopupElementWithAdditionalInfo { + public CurrentActiveBookmark(@NotNull Project project, @NotNull List repositories, @NotNull String branchName) { super(project, repositories, branchName); } @@ -346,5 +348,11 @@ public class HgBranchPopupActions { public AnAction[] getChildren(@Nullable AnActionEvent e) { return new AnAction[]{new BookmarkActions.DeleteBookmarkAction(myProject, myRepositories, myBranchName)}; } + + @Nullable + @Override + public String getPrefixInfo() { + return "active"; + } } } From a2dd6063d452a80e8a2fdf72751dc72e7fca6be0 Mon Sep 17 00:00:00 2001 From: Nadya Zabrodina Date: Thu, 1 Jun 2017 15:45:32 +0300 Subject: [PATCH 061/136] [branchPopup]: remove unnecessary current branch captions and rootInfo --- .../intellij/dvcs/branch/DvcsBranchPopup.java | 13 +++++------- .../dvcs/ui/BranchActionGroupPopup.java | 12 +---------- .../src/com/intellij/dvcs/ui/RootAction.java | 11 +++------- .../git4idea/ui/branch/GitBranchPopup.java | 21 +------------------ .../zmlx/hg4idea/branch/HgBranchPopup.java | 13 +++--------- 5 files changed, 13 insertions(+), 57 deletions(-) diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsBranchPopup.java b/platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsBranchPopup.java index 1f3a87c7917f..6253c4b9be90 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsBranchPopup.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsBranchPopup.java @@ -34,7 +34,6 @@ import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import javax.swing.*; import javax.swing.event.HyperlinkEvent; import java.util.List; @@ -65,7 +64,6 @@ public abstract class DvcsBranchPopup { ? " in " + DvcsUtil.getShortRepositoryName(currentRepository) : ""; myPopup = new BranchActionGroupPopup(title + myRepoTitleInfo, myProject, preselectActionCondition, createActions(), dimensionKey); initBranchSyncPolicyIfNotInitialized(); - setCurrentBranchInfo(); warnThatBranchesDivergedIfNeeded(); } @@ -86,11 +84,6 @@ public abstract class DvcsBranchPopup { } } - protected void setCurrentBranchInfo() { - String branchText = "Current branch : "; - myPopup.setAdText(branchText + myCurrentRepository.getCurrentBranchName(), SwingConstants.CENTER); - } - private void notifyAboutSyncedBranches() { String description = "You have several " + myVcs.getDisplayName() + " roots in the project and they all are checked out at the same branch. " + @@ -144,11 +137,15 @@ public abstract class DvcsBranchPopup { } private void warnThatBranchesDivergedIfNeeded() { - if (myRepositoryManager.moreThanOneRoot() && myMultiRootBranchConfig.diverged() && userWantsSyncControl()) { + if (isBranchesDiverged()) { myPopup.setWarning("Branches have diverged"); } } + protected boolean isBranchesDiverged() { + return myRepositoryManager.moreThanOneRoot() && myMultiRootBranchConfig.diverged() && userWantsSyncControl(); + } + @NotNull protected abstract DefaultActionGroup createRepositoriesActions(); diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java b/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java index 1ce059697920..97bfdf5d0692 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java @@ -284,12 +284,7 @@ public class BranchActionGroupPopup extends FlatSpeedSearchPopup { @Override protected WizardPopup createPopup(WizardPopup parent, PopupStep step, Object parentValue) { - WizardPopup popup = createListPopupStep(parent, step, parentValue); - RootAction rootAction = getRootAction(parentValue); - if (rootAction != null) { - popup.setAdText((rootAction).getCaption()); - } - return popup; + return createListPopupStep(parent, step, parentValue); } private WizardPopup createListPopupStep(WizardPopup parent, PopupStep step, Object parentValue) { @@ -299,11 +294,6 @@ public class BranchActionGroupPopup extends FlatSpeedSearchPopup { return super.createPopup(parent, step, parentValue); } - @Nullable - private static RootAction getRootAction(Object value) { - return getSpecificAction(value, RootAction.class); - } - private static T getSpecificAction(Object value, @NotNull Class clazz) { if (value instanceof PopupFactoryImpl.ActionItem) { AnAction action = ((PopupFactoryImpl.ActionItem)value).getAction(); diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/ui/RootAction.java b/platform/dvcs-impl/src/com/intellij/dvcs/ui/RootAction.java index 7e9f5a22778f..e407d1a5704e 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/ui/RootAction.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/ui/RootAction.java @@ -31,9 +31,9 @@ public class RootAction extends ActionGroup implements Pop @NotNull protected final T myRepository; @NotNull private final ActionGroup myGroup; - @NotNull private final String myBranchText; + @Nullable private final String myBranchText; - public RootAction(@NotNull T repository, @NotNull ActionGroup actionsGroup, @NotNull String branchText) { + public RootAction(@NotNull T repository, @NotNull ActionGroup actionsGroup, @Nullable String branchText) { super("", true); myRepository = repository; myGroup = actionsGroup; @@ -41,18 +41,13 @@ public class RootAction extends ActionGroup implements Pop getTemplatePresentation().setText(DvcsUtil.getShortRepositoryName(repository), false); } - @NotNull - public String getCaption() { - return "Current branch in " + DvcsUtil.getShortRepositoryName(myRepository) + ": " + getInfoText(); - } - @NotNull @Override public AnAction[] getChildren(@Nullable AnActionEvent e) { return myGroup.getChildren(e); } - @NotNull + @Nullable @Override public String getInfoText() { return myBranchText; diff --git a/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java b/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java index 747ead0b1614..21fd15497557 100644 --- a/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java +++ b/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java @@ -34,7 +34,6 @@ import git4idea.repo.GitRepositoryManager; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import javax.swing.*; import java.util.List; import java.util.Objects; @@ -94,24 +93,6 @@ class GitBranchPopup extends DvcsBranchPopup { preselectActionCondition, DIMENSION_SERVICE_KEY); } - @Override - protected void setCurrentBranchInfo() { - String currentBranchText = "Current branch"; - if (myRepositoryManager.moreThanOneRoot()) { - if (myMultiRootBranchConfig.diverged()) { - currentBranchText += " in " + DvcsUtil.getShortRepositoryName(myCurrentRepository) + ": " + - GitBranchUtil.getDisplayableBranchText(myCurrentRepository); - } - else { - currentBranchText += ": " + myMultiRootBranchConfig.getCurrentBranch(); - } - } - else { - currentBranchText += ": " + GitBranchUtil.getDisplayableBranchText(myCurrentRepository); - } - myPopup.setAdText(currentBranchText, SwingConstants.CENTER); - } - @Override protected void fillWithCommonRepositoryActions(@NotNull DefaultActionGroup popupGroup, @NotNull AbstractRepositoryManager repositoryManager) { @@ -162,7 +143,7 @@ class GitBranchPopup extends DvcsBranchPopup { popupGroup.addSeparator("Repositories"); List rootActions = DvcsUtil.sortRepositories(myRepositoryManager.getRepositories()).stream() .map(repo -> new RootAction<>(repo, new GitBranchPopupActions(repo.getProject(), repo).createActions(), - GitBranchUtil.getDisplayableBranchText(repo))).collect(toList()); + isBranchesDiverged() ? GitBranchUtil.getDisplayableBranchText(repo) : null)).collect(toList()); wrapWithMoreActionIfNeeded(myProject, popupGroup, rootActions, rootActions.size() > MAX_NUM ? DEFAULT_NUM : MAX_NUM, SHOW_ALL_REPOSITORIES); return popupGroup; diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchPopup.java b/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchPopup.java index cb9ef1f6a3e8..482cf5c2d1f6 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchPopup.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchPopup.java @@ -33,7 +33,6 @@ import org.zmlx.hg4idea.repo.HgRepository; import org.zmlx.hg4idea.repo.HgRepositoryManager; import org.zmlx.hg4idea.util.HgUtil; -import javax.swing.*; import java.util.List; import java.util.Objects; @@ -78,12 +77,6 @@ public class HgBranchPopup extends DvcsBranchPopup { super(currentRepository, repositoryManager, hgMultiRootBranchConfig, vcsSettings, preselectActionCondition, DIMENSION_SERVICE_KEY); } - protected void setCurrentBranchInfo() { - String branchText = "Current branch : "; - //always display heavy branch name for additional info // - myPopup.setAdText(branchText + myCurrentRepository.getCurrentBranch(), SwingConstants.CENTER); - } - @Override protected void fillWithCommonRepositoryActions(@NotNull DefaultActionGroup popupGroup, @NotNull AbstractRepositoryManager repositoryManager) { @@ -133,9 +126,9 @@ public class HgBranchPopup extends DvcsBranchPopup { protected DefaultActionGroup createRepositoriesActions() { DefaultActionGroup popupGroup = new DefaultActionGroup(null, false); popupGroup.addSeparator("Repositories"); - List rootActions = DvcsUtil.sortRepositories(myRepositoryManager.getRepositories()).stream() - .map(repo -> new RootAction<>(repo, new HgBranchPopupActions(repo.getProject(), repo).createActions(), - HgUtil.getDisplayableBranchOrBookmarkText(repo))).collect(toList()); + List rootActions = DvcsUtil.sortRepositories(myRepositoryManager.getRepositories()).stream().map( + repo -> new RootAction<>(repo, new HgBranchPopupActions(repo.getProject(), repo).createActions(), + isBranchesDiverged() ? HgUtil.getDisplayableBranchOrBookmarkText(repo) : null)).collect(toList()); wrapWithMoreActionIfNeeded(myProject, popupGroup, rootActions, rootActions.size() > MAX_NUM ? DEFAULT_NUM : MAX_NUM, SHOW_ALL_REPOSITORIES); return popupGroup; From 146e55c5ca1cf4bb635a158ec805f90e3a3db20f Mon Sep 17 00:00:00 2001 From: Nadya Zabrodina Date: Thu, 1 Jun 2017 18:46:44 +0300 Subject: [PATCH 062/136] [branchPopup]: (IDEA-168834) Do not expand branches on resizing a bit --- .../src/com/intellij/dvcs/ui/BranchActionGroupPopup.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java b/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java index 97bfdf5d0692..fdc42fc91cd3 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java @@ -64,6 +64,7 @@ public class BranchActionGroupPopup extends FlatSpeedSearchPopup { private boolean myShown; private boolean myUserSizeChanged; private boolean myInternalSizeChanged; + private int myMeanRowHeight; @Nullable private final String myKey; @NotNull private Dimension myPrevSize = JBUI.emptySize(); @@ -86,6 +87,7 @@ public class BranchActionGroupPopup extends FlatSpeedSearchPopup { } createTitlePanelToolbar(myKey); } + myMeanRowHeight = getList().getCellBounds(0, 0).height + UIUtil.getListCellVPadding() * 2; } void createTitlePanelToolbar(@NotNull String dimensionKey) { @@ -153,7 +155,11 @@ public class BranchActionGroupPopup extends FlatSpeedSearchPopup { private void processOnSizeChanged() { Dimension newSize = ObjectUtils.assertNotNull(getSize()); - if (!myInternalSizeChanged && myPrevSize.height < newSize.height) { + int preferredHeight = getComponent().getPreferredSize().height; + int realHeight = getComponent().getHeight(); + boolean shouldExpand = preferredHeight + myMeanRowHeight < realHeight; + boolean sizeWasIncreased = myPrevSize.height < newSize.height; + if (!myInternalSizeChanged && sizeWasIncreased && shouldExpand) { List mores = getMoreActions(); for (MoreAction more : mores) { if (!getList().getScrollableTracksViewportHeight()) break; From db26e06168a5cbc9020df772cfc49e73b5cb7e1e Mon Sep 17 00:00:00 2001 From: Nadya Zabrodina Date: Mon, 5 Jun 2017 16:48:44 +0300 Subject: [PATCH 063/136] [branchPopup]: (IDEA-168024) Pre-select previous branch in git popup --- .../src/git4idea/ui/branch/GitBranchPopup.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java b/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java index 21fd15497557..7cdc4ee0930e 100644 --- a/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java +++ b/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java @@ -23,6 +23,7 @@ import com.intellij.dvcs.ui.RootAction; import com.intellij.openapi.actionSystem.ActionGroup; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.actionSystem.EmptyAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Condition; import com.intellij.util.containers.ContainerUtil; @@ -42,6 +43,7 @@ import static com.intellij.dvcs.branch.DvcsBranchPopup.MyMoreIndex.MAX_NUM; import static com.intellij.dvcs.ui.BranchActionGroupPopup.wrapWithMoreActionIfNeeded; import static com.intellij.dvcs.ui.BranchActionUtil.FAVORITE_BRANCH_COMPARATOR; import static com.intellij.dvcs.ui.BranchActionUtil.getNumOfTopShownBranches; +import static com.intellij.util.ObjectUtils.tryCast; import static com.intellij.util.containers.ContainerUtil.map; import static java.util.stream.Collectors.toList; @@ -63,8 +65,8 @@ class GitBranchPopup extends DvcsBranchPopup { static GitBranchPopup getInstance(@NotNull final Project project, @NotNull GitRepository currentRepository) { final GitVcsSettings vcsSettings = GitVcsSettings.getInstance(project); Condition preselectActionCondition = action -> { - if (action instanceof GitBranchPopupActions.LocalBranchActions) { - GitBranchPopupActions.LocalBranchActions branchAction = (GitBranchPopupActions.LocalBranchActions)action; + GitBranchPopupActions.LocalBranchActions branchAction = getBranchAction(action); + if (branchAction != null) { String branchName = branchAction.getBranchName(); String recentBranch; @@ -85,6 +87,13 @@ class GitBranchPopup extends DvcsBranchPopup { return new GitBranchPopup(currentRepository, GitUtil.getRepositoryManager(project), vcsSettings, preselectActionCondition); } + @Nullable + private static GitBranchPopupActions.LocalBranchActions getBranchAction(@NotNull AnAction action) { + AnAction resultAction = + action instanceof EmptyAction.MyDelegatingActionGroup ? ((EmptyAction.MyDelegatingActionGroup)action).getDelegate() : action; + return tryCast(resultAction, GitBranchPopupActions.LocalBranchActions.class); + } + private GitBranchPopup(@NotNull GitRepository currentRepository, @NotNull GitRepositoryManager repositoryManager, @NotNull GitVcsSettings vcsSettings, From 4f2dafb7cef628417f93e4390b4de92a97c40901 Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Mon, 5 Jun 2017 19:42:53 +0300 Subject: [PATCH 064/136] create switch intention cleanup --- .../intention/impl/CreateSwitchIntention.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/CreateSwitchIntention.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/CreateSwitchIntention.java index dfb800f41c1a..af7d2ac1aef2 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/CreateSwitchIntention.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/CreateSwitchIntention.java @@ -33,40 +33,40 @@ public class CreateSwitchIntention extends BaseElementAtCaretIntentionAction { public static final String TEXT = "Create switch statement"; @Override - public void invoke(@NotNull final Project project, final Editor editor, @NotNull final PsiElement element) throws IncorrectOperationException { - final PsiExpressionStatement expressionStatement = resolveExpressionStatement(element); - final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(project).getElementFactory(); - PsiSwitchStatement switchStatement = (PsiSwitchStatement)elementFactory - .createStatementFromText(String.format("switch (%s) {}", expressionStatement.getExpression().getText()), null); + public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException { + PsiExpressionStatement expressionStatement = resolveExpressionStatement(element); + PsiElementFactory elementFactory = JavaPsiFacade.getInstance(project).getElementFactory(); + String valueToSwitch = expressionStatement.getExpression().getText(); + PsiSwitchStatement switchStatement = (PsiSwitchStatement)elementFactory.createStatementFromText("switch (" + valueToSwitch + ") {}", null); switchStatement = (PsiSwitchStatement)expressionStatement.replace(switchStatement); CodeStyleManager.getInstance(project).reformat(switchStatement); - final PsiJavaToken lBrace = switchStatement.getBody().getLBrace(); + PsiJavaToken lBrace = switchStatement.getBody().getLBrace(); editor.getCaretModel().moveToOffset(lBrace.getTextOffset() + lBrace.getTextLength()); } @Override - public boolean isAvailable(@NotNull final Project project, final Editor editor, @NotNull final PsiElement element) { - final PsiExpressionStatement expressionStatement = resolveExpressionStatement(element); + public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) { + PsiExpressionStatement expressionStatement = resolveExpressionStatement(element); return expressionStatement != null && isValidTypeForSwitch(expressionStatement.getExpression().getType(), expressionStatement); } - private static PsiExpressionStatement resolveExpressionStatement(final PsiElement element) { + private static PsiExpressionStatement resolveExpressionStatement(PsiElement element) { if (element instanceof PsiExpressionStatement) { return (PsiExpressionStatement)element; } else { - final PsiStatement psiStatement = PsiTreeUtil.getParentOfType(element, PsiStatement.class); + PsiStatement psiStatement = PsiTreeUtil.getParentOfType(element, PsiStatement.class); return psiStatement instanceof PsiExpressionStatement ? (PsiExpressionStatement)psiStatement : null; } } - private static boolean isValidTypeForSwitch(@Nullable final PsiType type, final PsiElement context) { + private static boolean isValidTypeForSwitch(@Nullable PsiType type, PsiElement context) { if (type == null) { return false; } if (type instanceof PsiClassType) { - final PsiClass resolvedClass = ((PsiClassType)type).resolve(); + PsiClass resolvedClass = ((PsiClassType)type).resolve(); if (resolvedClass == null) { return false; } From fbd082f0759fdfe9036fedc2644ed5d079c8b17a Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Mon, 5 Jun 2017 20:06:00 +0300 Subject: [PATCH 065/136] create switch intention: disable in for update IDEA-173690 --- .../intention/impl/CreateSwitchIntention.java | 17 +++++------------ .../createSwitch/notAvailableInForUpdate.java | 5 +++++ .../codeInsight/intention/CreateSwitchTest.java | 4 ++++ 3 files changed, 14 insertions(+), 12 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/createSwitch/notAvailableInForUpdate.java diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/CreateSwitchIntention.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/CreateSwitchIntention.java index af7d2ac1aef2..f536baaef798 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/CreateSwitchIntention.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/CreateSwitchIntention.java @@ -34,7 +34,7 @@ public class CreateSwitchIntention extends BaseElementAtCaretIntentionAction { @Override public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException { - PsiExpressionStatement expressionStatement = resolveExpressionStatement(element); + PsiExpressionStatement expressionStatement = PsiTreeUtil.getParentOfType(element, PsiExpressionStatement.class, false); PsiElementFactory elementFactory = JavaPsiFacade.getInstance(project).getElementFactory(); String valueToSwitch = expressionStatement.getExpression().getText(); PsiSwitchStatement switchStatement = (PsiSwitchStatement)elementFactory.createStatementFromText("switch (" + valueToSwitch + ") {}", null); @@ -47,17 +47,10 @@ public class CreateSwitchIntention extends BaseElementAtCaretIntentionAction { @Override public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) { - PsiExpressionStatement expressionStatement = resolveExpressionStatement(element); - return expressionStatement != null && isValidTypeForSwitch(expressionStatement.getExpression().getType(), expressionStatement); - } - - private static PsiExpressionStatement resolveExpressionStatement(PsiElement element) { - if (element instanceof PsiExpressionStatement) { - return (PsiExpressionStatement)element; - } else { - PsiStatement psiStatement = PsiTreeUtil.getParentOfType(element, PsiStatement.class); - return psiStatement instanceof PsiExpressionStatement ? (PsiExpressionStatement)psiStatement : null; - } + PsiExpressionStatement expressionStatement = PsiTreeUtil.getParentOfType(element, PsiExpressionStatement.class, false); + return expressionStatement != null && + expressionStatement.getParent() instanceof PsiCodeBlock && + isValidTypeForSwitch(expressionStatement.getExpression().getType(), expressionStatement); } private static boolean isValidTypeForSwitch(@Nullable PsiType type, PsiElement context) { diff --git a/java/java-tests/testData/codeInsight/createSwitch/notAvailableInForUpdate.java b/java/java-tests/testData/codeInsight/createSwitch/notAvailableInForUpdate.java new file mode 100644 index 000000000000..f7d6fab86901 --- /dev/null +++ b/java/java-tests/testData/codeInsight/createSwitch/notAvailableInForUpdate.java @@ -0,0 +1,5 @@ +class Test { + void m(int[] array) { + for (int i = 1; i < array.length; i++) { } + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/intention/CreateSwitchTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/intention/CreateSwitchTest.java index 286d1b717a53..e342f4c8692e 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/intention/CreateSwitchTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/intention/CreateSwitchTest.java @@ -55,6 +55,10 @@ public class CreateSwitchTest extends JavaCodeInsightFixtureTestCase { doTestNotAvailable(); } + public void testNotAvailableInForUpdate() { + doTestNotAvailable(); + } + private void doTestString() { final LanguageLevelProjectExtension languageLevelProjectExtension = LanguageLevelProjectExtension.getInstance(getProject()); final LanguageLevel oldLanguageLevel = languageLevelProjectExtension.getLanguageLevel(); From a5501a81c0c80baade96f0f3dfcae0ca80ccba6d Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Mon, 5 Jun 2017 19:40:36 +0300 Subject: [PATCH 066/136] incompatible return types in hierarchy fixed (IDEA-173809) take parameter bounds into account --- .../daemon/impl/analysis/HighlightMethodUtil.java | 4 +++- .../IncompatibleReturnTypeBounds.java | 10 ++++++++++ .../codeInsight/daemon/GenericsHighlightingTest.java | 2 ++ 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IncompatibleReturnTypeBounds.java diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java index 5138d2cabf91..7299220a847a 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java @@ -195,7 +195,9 @@ public class HighlightMethodUtil { if (returnType.equals(substitutedSuperReturnType)) return null; if (!(returnType instanceof PsiPrimitiveType) && substitutedSuperReturnType.getDeepComponentType() instanceof PsiClassType) { - if (isJdk15 && TypeConversionUtil.isAssignable(substitutedSuperReturnType, returnType)) { + if (isJdk15 && LambdaUtil.performWithSubstitutedParameterBounds(methodSignature.getTypeParameters(), + methodSignature.getSubstitutor(), + () -> TypeConversionUtil.isAssignable(substitutedSuperReturnType, returnType))) { return null; } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IncompatibleReturnTypeBounds.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IncompatibleReturnTypeBounds.java new file mode 100644 index 000000000000..9e44426fec3b --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IncompatibleReturnTypeBounds.java @@ -0,0 +1,10 @@ +class ListA { + public Ta foo() { throw new Error(); } +} + +class ListB extends ListA { + public Rb foo() { throw new Error(); } +} + +class ListC extends ListB { +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/GenericsHighlightingTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/GenericsHighlightingTest.java index 2f0480f4bce6..d2276ff4bc6e 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/GenericsHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/GenericsHighlightingTest.java @@ -180,6 +180,7 @@ public class GenericsHighlightingTest extends LightDaemonAnalyzerTestCase { public void testInaccessibleThroughWildcard() { doTest7Incompatibility(false);} public void testInconvertibleTypes() { doTest5(false); } public void testIncompatibleReturnType() { doTest5(false); } + public void testContinueInferenceAfterFirstRawResult() { doTest5(false); } public void testDoNotAcceptLowerBoundIfRaw() { doTest5(false); } public void testStaticOverride() { doTest5(false); } @@ -342,6 +343,7 @@ public class GenericsHighlightingTest extends LightDaemonAnalyzerTestCase { public void testIDEA71582() { doTest5(false); } public void testIDEA65377() { doTest5(false); } public void testIDEA113526() { doTest5(true); } + public void testIncompatibleReturnTypeBounds() { doTest(LanguageLevel.JDK_1_7, JavaSdkVersion.JDK_1_7, false); } public void testIDEA116493() { doTest(LanguageLevel.JDK_1_7, JavaSdkVersion.JDK_1_7, false); } public void testIDEA117827() { doTest(LanguageLevel.JDK_1_7, JavaSdkVersion.JDK_1_7, false); } public void testIDEA118037() { doTest(LanguageLevel.JDK_1_7, JavaSdkVersion.JDK_1_7, false); } From c578a4ede7066716e2cc0993b5b6061e142f4bae Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Mon, 5 Jun 2017 19:44:50 +0300 Subject: [PATCH 067/136] disable assign param to field (IDEA-173747; IDEA-173689) inapplicable types or already assigned --- .../intention/impl/AssignFieldFromParameterAction.java | 2 +- .../intention/impl/FieldFromParameterUtils.java | 6 +++--- .../beforeAlreadyAssigned.java | 10 ++++++++++ .../beforeCheckAssignability.java | 10 ++++++++++ 4 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/assignFieldFromParameter/beforeAlreadyAssigned.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/assignFieldFromParameter/beforeCheckAssignability.java diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/AssignFieldFromParameterAction.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/AssignFieldFromParameterAction.java index 6cf28f28ccb1..fa578c172089 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/AssignFieldFromParameterAction.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/AssignFieldFromParameterAction.java @@ -46,7 +46,7 @@ public class AssignFieldFromParameterAction extends BaseIntentionAction { return false; } final PsiField field = findFieldToAssign(project, myParameter); - if (field == null) return false; + if (field == null || type == null || !field.getType().isAssignableFrom(type)) return false; if (!field.getLanguage().isKindOf(JavaLanguage.INSTANCE)) return false; setText(CodeInsightBundle.message("intention.assign.field.from.parameter.text", field.getName())); diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/FieldFromParameterUtils.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/FieldFromParameterUtils.java index aa644cd96a20..8168ae2fe60a 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/FieldFromParameterUtils.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/FieldFromParameterUtils.java @@ -112,9 +112,9 @@ public final class FieldFromParameterUtils { for (PsiReference reference : ReferencesSearch.search(parameter, new LocalSearchScope(parameter.getDeclarationScope()), false)) { if (!(reference instanceof PsiReferenceExpression)) continue; final PsiReferenceExpression expression = (PsiReferenceExpression)reference; - if (!(expression.getParent() instanceof PsiAssignmentExpression)) continue; - final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)expression.getParent(); - if (assignmentExpression.getRExpression() != expression) continue; + PsiAssignmentExpression assignmentExpression = PsiTreeUtil.getParentOfType(expression, PsiAssignmentExpression.class, true, PsiClass.class); + if (assignmentExpression == null) continue; + if (!PsiTreeUtil.isAncestor(assignmentExpression.getRExpression(), expression, false)) continue; final PsiExpression lExpression = assignmentExpression.getLExpression(); if (!(lExpression instanceof PsiReferenceExpression)) continue; final PsiElement element = ((PsiReferenceExpression)lExpression).resolve(); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/assignFieldFromParameter/beforeAlreadyAssigned.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/assignFieldFromParameter/beforeAlreadyAssigned.java new file mode 100644 index 000000000000..94684be58489 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/assignFieldFromParameter/beforeAlreadyAssigned.java @@ -0,0 +1,10 @@ +// "Assign Parameter to Field 'myA'" "false" + +class Person { + int myA; + int myId; + void f(int a, String id) { + this.myA = foo(a); + } + int foo(int a) {return a;} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/assignFieldFromParameter/beforeCheckAssignability.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/assignFieldFromParameter/beforeCheckAssignability.java new file mode 100644 index 000000000000..38db6f1a210d --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/assignFieldFromParameter/beforeCheckAssignability.java @@ -0,0 +1,10 @@ +// "Assign Parameter to Field 'myId'" "false" + +class Person { + int a; + int myId; + void f(int a, String id) { + this.a = foo(a); + } + int foo(int a) {return a;} +} \ No newline at end of file From 03a663c63e18f9e57796342c35f4d27949d4c559 Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 5 Jun 2017 16:12:41 +0200 Subject: [PATCH 068/136] IDEA-165006 Support chained completion for method references in Java 8+ --- .../completion/JavaChainLookupElement.java | 17 ++++++----- .../completion/JavaNoVariantsDelegator.java | 9 +++--- ...erenceExpressionCompletionContributor.java | 11 ++++++-- .../normal/ChainedMethodReference.java | 5 ++++ .../normal/ChainedMethodReference_after.java | 5 ++++ .../completion/Normal8CompletionTest.groovy | 28 ++++++++++++------- 6 files changed, 51 insertions(+), 24 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/completion/normal/ChainedMethodReference.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/completion/normal/ChainedMethodReference_after.java diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaChainLookupElement.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaChainLookupElement.java index 7f3f8a33b0af..40a9d3b70af7 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaChainLookupElement.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaChainLookupElement.java @@ -19,7 +19,6 @@ import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementDecorator; import com.intellij.codeInsight.lookup.LookupElementPresentation; import com.intellij.codeInsight.lookup.TypedLookupItem; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.util.ClassConditionKey; import com.intellij.openapi.util.Key; @@ -39,19 +38,23 @@ import java.util.Set; */ public class JavaChainLookupElement extends LookupElementDecorator implements TypedLookupItem { public static final Key CHAIN_QUALIFIER = Key.create("CHAIN_QUALIFIER"); - private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.JavaChainLookupElement"); public static final ClassConditionKey CLASS_CONDITION_KEY = ClassConditionKey.create(JavaChainLookupElement.class); private final LookupElement myQualifier; + private final String mySeparator; public JavaChainLookupElement(LookupElement qualifier, LookupElement main) { + this(qualifier, main, "."); + } + public JavaChainLookupElement(LookupElement qualifier, LookupElement main, String separator) { super(main); myQualifier = qualifier; + mySeparator = separator; } @NotNull @Override public String getLookupString() { - return maybeAddParentheses(myQualifier.getLookupString()) + "." + getDelegate().getLookupString(); + return maybeAddParentheses(myQualifier.getLookupString()) + mySeparator + getDelegate().getLookupString(); } public LookupElement getQualifier() { @@ -70,7 +73,7 @@ public class JavaChainLookupElement extends LookupElementDecorator + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/completion/normal/ChainedMethodReference_after.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/completion/normal/ChainedMethodReference_after.java new file mode 100644 index 000000000000..eb1f75bd4b66 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/completion/normal/ChainedMethodReference_after.java @@ -0,0 +1,5 @@ +class A { + { + Runnable r = System::setOut; + } +} diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/Normal8CompletionTest.groovy b/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/Normal8CompletionTest.groovy index 30a1fbd37628..ae0af6127049 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/Normal8CompletionTest.groovy +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/Normal8CompletionTest.groovy @@ -212,37 +212,37 @@ class Test88 { void testCollectorsToList() { configureByTestName() selectItem(myItems.find { it.lookupString.contains('toList') }) - checkResultByFile(getTestName(false) + "_after.java") + checkResultByFileName() } void testStaticallyImportedCollectorsToList() { configureByTestName() selectItem(myItems.find { it.lookupString.contains('collect(toList())') }) - checkResultByFile(getTestName(false) + "_after.java") + checkResultByFileName() } void testAllCollectors() { configureByTestName() myFixture.assertPreferredCompletionItems 0, 'collect', 'collect', 'collect(Collectors.toCollection())', 'collect(Collectors.toList())', 'collect(Collectors.toSet())' selectItem(myItems.find { it.lookupString.contains('toCollection') }) - checkResultByFile(getTestName(false) + "_after.java") + checkResultByFileName() } void testCollectorsToSet() { configureByTestName() selectItem(myItems.find { it.lookupString.contains('toSet') }) - checkResultByFile(getTestName(false) + "_after.java") + checkResultByFileName() } void testNoExplicitTypeArgsInTernary() { configureByTestName() selectItem(myItems.find { it.lookupString.contains('empty') }) - checkResultByFile(getTestName(false) + "_after.java") + checkResultByFileName() } void testCallBeforeLambda() { configureByTestName() - checkResultByFile(getTestName(false) + "_after.java") + checkResultByFileName() } void testLambdaInAmbiguousCall() { @@ -264,13 +264,13 @@ class Test88 { void testNoSemicolonAfterVoidMethodInLambda() { configureByTestName() myFixture.type('l\t') - checkResultByFile(getTestName(false) + "_after.java") + checkResultByFileName() } void testFinishMethodReferenceWithColon() { configureByTestName() myFixture.type(':') - checkResultByFile(getTestName(false) + "_after.java") + checkResultByFileName() } void testPreferLocalsOverMethodRefs() { @@ -286,14 +286,22 @@ class Test88 { "}") configureByTestName() myFixture.type('\n') - checkResultByFile(getTestName(false) + "_after.java") + checkResultByFileName() } void testOverrideMethodAsDefault() { configureByTestName() assert LookupElementPresentation.renderElement(myFixture.lookupElements[0]).itemText == 'default void run' myFixture.type('\t') - checkResultByFile(getTestName(false) + "_after.java") + checkResultByFileName() } + void testChainedMethodReference() { + configureByTestName() + checkResultByFileName() + } + + private checkResultByFileName() { + checkResultByFile(getTestName(false) + "_after.java") + } } \ No newline at end of file From b61faedfd66cc41134d6bd922be0e56994840385 Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 5 Jun 2017 19:16:16 +0200 Subject: [PATCH 069/136] support @TypeQualifierNickname (IDEA-173544) --- .../NullableNotNullManagerImpl.java | 77 ++++++++++++++++++- .../codeInsight/NullableNotNullManager.java | 27 +++++-- .../fixture/TypeQualifierNickname.java | 15 ++++ .../DataFlowInspectionTest.java | 30 +++++++- 4 files changed, 136 insertions(+), 13 deletions(-) create mode 100644 java/java-tests/testData/inspection/dataFlow/fixture/TypeQualifierNickname.java diff --git a/java/java-impl/src/com/intellij/codeInsight/NullableNotNullManagerImpl.java b/java/java-impl/src/com/intellij/codeInsight/NullableNotNullManagerImpl.java index db8722c06f12..6332fd8ed0af 100644 --- a/java/java-impl/src/com/intellij/codeInsight/NullableNotNullManagerImpl.java +++ b/java/java-impl/src/com/intellij/codeInsight/NullableNotNullManagerImpl.java @@ -16,22 +16,35 @@ package com.intellij.codeInsight; import com.intellij.codeInspection.dataFlow.HardcodedContracts; +import com.intellij.codeInspection.dataFlow.Nullness; import com.intellij.openapi.components.PersistentStateComponent; import com.intellij.openapi.components.State; +import com.intellij.openapi.project.Project; import com.intellij.openapi.util.DefaultJDOMExternalizer; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.WriteExternalException; -import com.intellij.psi.PsiElement; +import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.CachedValueProvider; +import com.intellij.psi.util.CachedValuesManager; +import com.intellij.psi.util.PsiModificationTracker; +import com.intellij.util.containers.ContainerUtil; +import one.util.streamex.StreamEx; import org.jdom.Element; +import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.model.serialization.java.compiler.JpsJavaCompilerNotNullableSerializer; +import java.util.ArrayList; import java.util.Collections; import java.util.List; @State(name = "NullableNotNullManager") public class NullableNotNullManagerImpl extends NullableNotNullManager implements PersistentStateComponent { - public NullableNotNullManagerImpl() { + public static final String TYPE_QUALIFIER_NICKNAME = "javax.annotation.meta.TypeQualifierNickname"; + + public NullableNotNullManagerImpl(Project project) { + super(project); myNotNulls.addAll(getPredefinedNotNulls()); } @@ -80,4 +93,64 @@ public class NullableNotNullManagerImpl extends NullableNotNullManager implement LOG.error(e); } } + + private List getAllNullabilityNickNames() { + if (!getNotNulls().contains(JAVAX_ANNOTATION_NONNULL)) { + return Collections.emptyList(); + } + return CachedValuesManager.getManager(myProject).getCachedValue(myProject, () -> { + List result = new ArrayList<>(); + GlobalSearchScope scope = GlobalSearchScope.allScope(myProject); + for (PsiClass tqNick : JavaPsiFacade.getInstance(myProject).findClasses(TYPE_QUALIFIER_NICKNAME, scope)) { + result.addAll(ContainerUtil.findAll(MetaAnnotationUtil.getChildren(tqNick, scope), candidate -> { + String qname = candidate.getQualifiedName(); + if (qname == null || qname.startsWith("javax.annotation.")) return false; + return getNickNamedNullability(candidate) != Nullness.UNKNOWN; + })); + } + return CachedValueProvider.Result.create(result, PsiModificationTracker.MODIFICATION_COUNT); + }); + } + + private static Nullness getNickNamedNullability(@NotNull PsiClass psiClass) { + if (AnnotationUtil.findAnnotation(psiClass, TYPE_QUALIFIER_NICKNAME) == null) return Nullness.UNKNOWN; + + PsiAnnotation nonNull = AnnotationUtil.findAnnotation(psiClass, JAVAX_ANNOTATION_NONNULL); + return nonNull != null ? extractNullityFromWhenValue(nonNull) : Nullness.UNKNOWN; + } + + @NotNull + private static Nullness extractNullityFromWhenValue(PsiAnnotation nonNull) { + PsiAnnotationMemberValue when = nonNull.findAttributeValue("when"); + if (when instanceof PsiReferenceExpression) { + String refName = ((PsiReferenceExpression)when).getReferenceName(); + if ("ALWAYS".equals(refName)) { + return Nullness.NOT_NULL; + } + if ("MAYBE".equals(refName) || "NEVER".equals(refName)) { + return Nullness.NULLABLE; + } + } + return Nullness.UNKNOWN; + } + + private List filterNickNames(Nullness nullness) { + return StreamEx.of(getAllNullabilityNickNames()).filter(c -> getNickNamedNullability(c) == nullness).map(PsiClass::getQualifiedName).toList(); + } + + @NotNull + @Override + protected List getNullablesWithNickNames() { + return CachedValuesManager.getManager(myProject).getCachedValue(myProject, () -> + CachedValueProvider.Result.create(ContainerUtil.concat(getNullables(), filterNickNames(Nullness.NULLABLE)), + PsiModificationTracker.MODIFICATION_COUNT)); + } + + @NotNull + @Override + protected List getNotNullsWithNickNames() { + return CachedValuesManager.getManager(myProject).getCachedValue(myProject, () -> + CachedValueProvider.Result.create(ContainerUtil.concat(getNotNulls(), filterNickNames(Nullness.NOT_NULL)), + PsiModificationTracker.MODIFICATION_COUNT)); + } } diff --git a/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java b/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java index b0ee0debbe61..a1ff985f7a1e 100644 --- a/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java +++ b/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java @@ -34,6 +34,7 @@ import java.util.*; */ public abstract class NullableNotNullManager { protected static final Logger LOG = Logger.getInstance(NullableNotNullManager.class); + protected final Project myProject; public String myDefaultNullable = AnnotationUtil.NULLABLE; public String myDefaultNotNull = AnnotationUtil.NOT_NULL; @@ -41,7 +42,7 @@ public abstract class NullableNotNullManager { public final JDOMExternalizableStringList myNotNulls = new JDOMExternalizableStringList(); private static final String JAVAX_ANNOTATION_NULLABLE = "javax.annotation.Nullable"; - private static final String JAVAX_ANNOTATION_NONNULL = "javax.annotation.Nonnull"; + protected static final String JAVAX_ANNOTATION_NONNULL = "javax.annotation.Nonnull"; static final String[] DEFAULT_NULLABLES = {AnnotationUtil.NULLABLE, JAVAX_ANNOTATION_NULLABLE, "javax.annotation.CheckForNull", @@ -49,7 +50,8 @@ public abstract class NullableNotNullManager { "edu.umd.cs.findbugs.annotations.Nullable", "android.support.annotation.Nullable" }; - public NullableNotNullManager() { + public NullableNotNullManager(Project project) { + myProject = project; Collections.addAll(myNullables, DEFAULT_NULLABLES); } @@ -202,7 +204,7 @@ public abstract class NullableNotNullManager { String qName = annotation.getQualifiedName(); if (qName == null) return null; - List contradictory = nullable ? getNotNulls() : getNullables(); + List contradictory = nullable ? getNotNullsWithNickNames() : getNullablesWithNickNames(); if (contradictory.contains(qName)) return null; return annotation; @@ -241,13 +243,24 @@ public abstract class NullableNotNullManager { } private PsiAnnotation findPlainNullabilityAnnotation(@NotNull PsiModifierListOwner owner, boolean checkBases) { - Set qNames = ContainerUtil.newHashSet(getNullables()); - qNames.addAll(getNotNulls()); + Set qNames = ContainerUtil.newHashSet(getNullablesWithNickNames()); + qNames.addAll(getNotNullsWithNickNames()); return checkBases && owner instanceof PsiMethod ? AnnotationUtil.findAnnotationInHierarchy(owner, qNames) : AnnotationUtil.findAnnotation(owner, qNames); } + + @NotNull + protected List getNullablesWithNickNames() { + return getNullables(); + } + + @NotNull + protected List getNotNullsWithNickNames() { + return getNotNulls(); + } + protected boolean hasHardcodedContracts(PsiElement element) { return false; } @@ -360,10 +373,10 @@ public abstract class NullableNotNullManager { public abstract List getPredefinedNotNulls(); public static boolean isNullableAnnotation(@NotNull PsiAnnotation annotation) { - return getInstance(annotation.getProject()).getNullables().contains(annotation.getQualifiedName()); + return getInstance(annotation.getProject()).getNullablesWithNickNames().contains(annotation.getQualifiedName()); } public static boolean isNotNullAnnotation(@NotNull PsiAnnotation annotation) { - return getInstance(annotation.getProject()).getNotNulls().contains(annotation.getQualifiedName()); + return getInstance(annotation.getProject()).getNotNullsWithNickNames().contains(annotation.getQualifiedName()); } } \ No newline at end of file diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/TypeQualifierNickname.java b/java/java-tests/testData/inspection/dataFlow/fixture/TypeQualifierNickname.java new file mode 100644 index 000000000000..c2f20a40f1a3 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/TypeQualifierNickname.java @@ -0,0 +1,15 @@ +import javax.annotation.meta.*; + +@TypeQualifierNickname() +@javax.annotation.Nonnull(when = When.MAYBE) +@interface NullableNick {} + +interface UnknownInterface { + void foo(String s); +} + +class ImplWithNotNull implements UnknownInterface { + public void foo(@NullableNick String s) { + System.out.println(s.hashCode()); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTest.java b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTest.java index ee1d16172c03..062f0c1a91aa 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTest.java @@ -360,6 +360,17 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase { myFixture.enableInspections(inspection); myFixture.testHighlighting(true, false, true, getTestName(false) + ".java"); } + + public void testTypeQualifierNickname() { + addJavaxNullabilityAnnotations(myFixture); + + myFixture.addClass("package bar;" + + "import javax.annotation.meta.*;" + + "@TypeQualifierNickname() @javax.annotation.NonNull(when = Maybe.MAYBE) " + + "public @interface NullableNick {}"); + + doTest(); + } public static void addJavaxDefaultNullabilityAnnotations(final JavaCodeInsightTestFixture fixture) { fixture.addClass("package javax.annotation;" + @@ -371,12 +382,23 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase { } public static void addJavaxNullabilityAnnotations(final JavaCodeInsightTestFixture fixture) { - fixture.addClass("package javax.annotation;" + - "public @interface Nonnull {}"); - fixture.addClass("package javax.annotation;" + - "public @interface Nullable {}"); fixture.addClass("package javax.annotation.meta;" + "public @interface TypeQualifierDefault { java.lang.annotation.ElementType[] value() default {};}"); + fixture.addClass("package javax.annotation.meta;" + + "public enum When { ALWAYS, UNKNOWN, MAYBE, NEVER }"); + fixture.addClass("package javax.annotation.meta;" + + "public @interface TypeQualifierNickname {}"); + + fixture.addClass("package javax.annotation;" + + "import javax.annotation.meta.*;" + + "public @interface Nonnull {" + + " When when() default When.ALWAYS;" + + "}"); + fixture.addClass("package javax.annotation;" + + "import javax.annotation.meta.*;" + + "@TypeQualifierNickname " + + "@Nonnull(when = When.UNKNOWN) " + + "public @interface Nullable {}"); } public void testCustomTypeQualifierDefault() { From f1b5f0c2f14f1371ebafb34f2afb200e7755e120 Mon Sep 17 00:00:00 2001 From: Sergey Simonchik Date: Mon, 5 Jun 2017 20:26:19 +0300 Subject: [PATCH 070/136] simplify hasErrors --- .../src/com/intellij/util/PsiErrorElementUtil.java | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/util/PsiErrorElementUtil.java b/platform/platform-impl/src/com/intellij/util/PsiErrorElementUtil.java index 5e9bc6559751..818c2fbfd728 100644 --- a/platform/platform-impl/src/com/intellij/util/PsiErrorElementUtil.java +++ b/platform/platform-impl/src/com/intellij/util/PsiErrorElementUtil.java @@ -20,11 +20,7 @@ import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiErrorElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.SyntaxTraverser; -import com.intellij.psi.impl.PsiManagerEx; +import com.intellij.psi.*; import com.intellij.psi.util.CachedValue; import com.intellij.psi.util.CachedValueProvider; import com.intellij.psi.util.CachedValuesManager; @@ -39,9 +35,7 @@ public class PsiErrorElementUtil { public static boolean hasErrors(@NotNull Project project, @NotNull VirtualFile virtualFile) { return ReadAction.compute(() -> { if (project.isDisposed() || !virtualFile.isValid()) return false; - - PsiManagerEx psiManager = PsiManagerEx.getInstanceEx(project); - PsiFile psiFile = psiManager.getFileManager().findFile(virtualFile); + PsiFile psiFile = PsiManager.getInstance(project).findFile(virtualFile); return psiFile != null && hasErrors(psiFile); }); } From 8f4d31d4393c86dedd3db1f066b3c2220b26b196 Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Wed, 31 May 2017 22:17:08 +0300 Subject: [PATCH 071/136] PY-23311 Fixed: Postgres Create Extension doesn't get syntax highlighting Add PostgreSQL extensions support. --- python/IntelliLang-python/src/resources/pyInjections.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/python/IntelliLang-python/src/resources/pyInjections.xml b/python/IntelliLang-python/src/resources/pyInjections.xml index 0c04cf7018d4..b9c1d06c4c7d 100644 --- a/python/IntelliLang-python/src/resources/pyInjections.xml +++ b/python/IntelliLang-python/src/resources/pyInjections.xml @@ -80,5 +80,11 @@ + + + + + + From de1a9451866978bc28accf5e8f05e0925aad9689 Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Fri, 14 Apr 2017 15:35:27 +0300 Subject: [PATCH 072/136] Get rid of primitive definition level in PyNamedTupleType and use enum instead. --- .../codeInsight/stdlib/PyNamedTupleType.java | 50 ++++++++++++++----- .../stdlib/PyStdlibTypeProvider.java | 12 ++--- 2 files changed, 43 insertions(+), 19 deletions(-) diff --git a/python/src/com/jetbrains/python/codeInsight/stdlib/PyNamedTupleType.java b/python/src/com/jetbrains/python/codeInsight/stdlib/PyNamedTupleType.java index 2835e373a58c..818790416894 100644 --- a/python/src/com/jetbrains/python/codeInsight/stdlib/PyNamedTupleType.java +++ b/python/src/com/jetbrains/python/codeInsight/stdlib/PyNamedTupleType.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -39,17 +39,25 @@ import java.util.Set; * @author yole */ public class PyNamedTupleType extends PyClassTypeImpl implements PyCallableType { + + @NotNull + private final PsiElement myDeclaration; + + @NotNull private final String myName; - // 2 - namedtuple call itself - // 1 - return type of namedtuple call, aka namedtuple class - // 0 - namedtuple instance - private final int myDefinitionLevel; - private final PsiElement myDeclaration; + @NotNull private final List myFields; - public PyNamedTupleType(PyClass tupleClass, PsiElement declaration, String name, List fields, int definitionLevel) { - super(tupleClass, definitionLevel > 0); + @NotNull + private final DefinitionLevel myDefinitionLevel; + + public PyNamedTupleType(@NotNull PyClass tupleClass, + @NotNull PsiElement declaration, + @NotNull String name, + @NotNull List fields, + @NotNull DefinitionLevel definitionLevel) { + super(tupleClass, definitionLevel != DefinitionLevel.INSTANCE); myDeclaration = declaration; myFields = fields; myName = name; @@ -76,7 +84,7 @@ public class PyNamedTupleType extends PyClassTypeImpl implements PyCallableType @Override public Object[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { - List result = new ArrayList<>(); + final List result = new ArrayList<>(); Collections.addAll(result, super.getCompletionVariants(completionPrefix, location, context)); for (String field : myFields) { result.add(LookupElementBuilder.create(field)); @@ -84,6 +92,7 @@ public class PyNamedTupleType extends PyClassTypeImpl implements PyCallableType return ArrayUtil.toObjectArray(result); } + @NotNull @Override public String getName() { return myName; @@ -97,22 +106,30 @@ public class PyNamedTupleType extends PyClassTypeImpl implements PyCallableType @Nullable @Override public PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyCallSiteExpression callSite) { - if (myDefinitionLevel > 0) { - return new PyNamedTupleType(myClass, myDeclaration, myName, myFields, myDefinitionLevel - 1); + if (myDefinitionLevel == DefinitionLevel.AS_SUPERCLASS) { + return new PyNamedTupleType(myClass, myDeclaration, myName, myFields, DefinitionLevel.NEW_TYPE); } + else if (myDefinitionLevel == DefinitionLevel.NEW_TYPE) { + return new PyNamedTupleType(myClass, myDeclaration, myName, myFields, DefinitionLevel.INSTANCE); + } + return null; } @NotNull @Override public PyClassType toInstance() { - return myDefinitionLevel == 1 ? new PyNamedTupleType(myClass, myDeclaration, myName, myFields, 0) : this; + return myDefinitionLevel == DefinitionLevel.NEW_TYPE + ? new PyNamedTupleType(myClass, myDeclaration, myName, myFields, DefinitionLevel.INSTANCE) + : this; } @NotNull @Override public PyClassLikeType toClass() { - return myDefinitionLevel == 0 ? this : new PyNamedTupleType(myClass, myDeclaration, myName, myFields, 1); + return myDefinitionLevel == DefinitionLevel.INSTANCE + ? this + : new PyNamedTupleType(myClass, myDeclaration, myName, myFields, DefinitionLevel.NEW_TYPE); } @Override @@ -137,4 +154,11 @@ public class PyNamedTupleType extends PyClassTypeImpl implements PyCallableType public List getElementNames() { return Collections.unmodifiableList(myFields); } + + public enum DefinitionLevel { + + AS_SUPERCLASS, + NEW_TYPE, + INSTANCE + } } diff --git a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java index 14fb0f2f313d..be83fb0bb7ed 100644 --- a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java @@ -287,13 +287,13 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase { final PyTargetExpressionStub stub = target.getStub(); if (stub != null) { - return getNamedTupleTypeFromStub(target, stub.getCustomStub(PyNamedTupleStub.class), 1); + return getNamedTupleTypeFromStub(target, stub.getCustomStub(PyNamedTupleStub.class), PyNamedTupleType.DefinitionLevel.NEW_TYPE); } else { - return getNamedTupleTypeFromAST(target, context, 1); + return getNamedTupleTypeFromAST(target, context, PyNamedTupleType.DefinitionLevel.NEW_TYPE); } } else if (referenceTarget instanceof PyFunction && anchor instanceof PyCallExpression) { - return getNamedTupleTypeFromAST((PyCallExpression)anchor, context, 2); + return getNamedTupleTypeFromAST((PyCallExpression)anchor, context, PyNamedTupleType.DefinitionLevel.AS_SUPERCLASS); } return null; } @@ -332,7 +332,7 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase { @Nullable private static PyType getNamedTupleTypeFromStub(@NotNull PsiElement referenceTarget, @Nullable PyNamedTupleStub stub, - int definitionLevel) { + @NotNull PyNamedTupleType.DefinitionLevel definitionLevel) { if (stub == null) { return null; } @@ -349,7 +349,7 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase { @Nullable private static PyType getNamedTupleTypeFromAST(@NotNull PyTargetExpression expression, @NotNull TypeEvalContext context, - int definitionLevel) { + @NotNull PyNamedTupleType.DefinitionLevel definitionLevel) { if (context.maySwitchToAST(expression)) { return getNamedTupleTypeFromStub(expression, PyNamedTupleStubImpl.create(expression), definitionLevel); } @@ -360,7 +360,7 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase { @Nullable private static PyType getNamedTupleTypeFromAST(@NotNull PyCallExpression expression, @NotNull TypeEvalContext context, - int definitionLevel) { + @NotNull PyNamedTupleType.DefinitionLevel definitionLevel) { if (context.maySwitchToAST(expression)) { return getNamedTupleTypeFromStub(expression, PyNamedTupleStubImpl.create(expression), definitionLevel); } From b8ad73fbb9b2587a7b269e12c4030fb93fb444c9 Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Fri, 14 Apr 2017 18:08:59 +0300 Subject: [PATCH 073/136] PY-18246 Fixed: No completion for typing.NamedTuple Support typing.NamedTuple in namedtuple stubs. --- .../typing/PyTypingTypeProvider.java | 9 +- .../python/psi/PyFileElementType.java | 4 +- .../psi/impl/stubs/PyNamedTupleStubImpl.java | 201 +++++++++++++----- .../stubs/FullyQualifiedTypingNamedTuple.py | 3 + .../FullyQualifiedTypingNamedTupleKwargs.py | 3 + ...lyQualifiedTypingNamedTupleKwargsWithAs.py | 3 + .../FullyQualifiedTypingNamedTupleWithAs.py | 3 + .../stubs/ImportedTypingNamedTuple.py | 3 + .../stubs/ImportedTypingNamedTupleFields.py | 4 + .../stubs/ImportedTypingNamedTupleKwargs.py | 3 + .../ImportedTypingNamedTupleKwargsName.py | 4 + .../ImportedTypingNamedTupleKwargsWithAs.py | 3 + .../stubs/ImportedTypingNamedTupleName.py | 4 + .../stubs/ImportedTypingNamedTupleWithAs.py | 3 + .../stubs/TypingNamedTupleFieldsChain.py | 5 + .../stubs/TypingNamedTupleFieldsReference.py | 4 + .../stubs/TypingNamedTupleKwargsNameChain.py | 5 + .../TypingNamedTupleKwargsNameReference.py | 4 + .../stubs/TypingNamedTupleNameChain.py | 5 + .../stubs/TypingNamedTupleNameReference.py | 4 + .../com/jetbrains/python/PyStubsTest.java | 92 ++++++++ .../python/PythonCompletionTest.java | 58 ++++- 22 files changed, 368 insertions(+), 59 deletions(-) create mode 100644 python/testData/stubs/FullyQualifiedTypingNamedTuple.py create mode 100644 python/testData/stubs/FullyQualifiedTypingNamedTupleKwargs.py create mode 100644 python/testData/stubs/FullyQualifiedTypingNamedTupleKwargsWithAs.py create mode 100644 python/testData/stubs/FullyQualifiedTypingNamedTupleWithAs.py create mode 100644 python/testData/stubs/ImportedTypingNamedTuple.py create mode 100644 python/testData/stubs/ImportedTypingNamedTupleFields.py create mode 100644 python/testData/stubs/ImportedTypingNamedTupleKwargs.py create mode 100644 python/testData/stubs/ImportedTypingNamedTupleKwargsName.py create mode 100644 python/testData/stubs/ImportedTypingNamedTupleKwargsWithAs.py create mode 100644 python/testData/stubs/ImportedTypingNamedTupleName.py create mode 100644 python/testData/stubs/ImportedTypingNamedTupleWithAs.py create mode 100644 python/testData/stubs/TypingNamedTupleFieldsChain.py create mode 100644 python/testData/stubs/TypingNamedTupleFieldsReference.py create mode 100644 python/testData/stubs/TypingNamedTupleKwargsNameChain.py create mode 100644 python/testData/stubs/TypingNamedTupleKwargsNameReference.py create mode 100644 python/testData/stubs/TypingNamedTupleNameChain.py create mode 100644 python/testData/stubs/TypingNamedTupleNameReference.py diff --git a/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java b/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java index b27b06811b83..7bf709f60bd9 100644 --- a/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java @@ -23,7 +23,10 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Ref; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiPolyVariantReference; -import com.intellij.psi.util.*; +import com.intellij.psi.util.CachedValueProvider; +import com.intellij.psi.util.CachedValuesManager; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.QualifiedName; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashMap; import com.intellij.util.containers.HashSet; @@ -55,6 +58,8 @@ import static com.jetbrains.python.psi.PyUtil.as; public class PyTypingTypeProvider extends PyTypeProviderBase { private static final Object RECURSION_KEY = new Object(); + public static final String TYPING = "typing"; + public static final String GENERATOR = "typing.Generator"; public static final String ASYNC_GENERATOR = "typing.AsyncGenerator"; public static final String COROUTINE = "typing.Coroutine"; @@ -63,6 +68,8 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { public static final String TYPE = "typing.Type"; public static final String ANY = "typing.Any"; + public static final String NAMEDTUPLE_SIMPLE = "NamedTuple"; + public static final Pattern TYPE_COMMENT_PATTERN = Pattern.compile("# *type: *(.*)"); private static final ImmutableMap COLLECTION_CLASSES = ImmutableMap.builder() diff --git a/python/src/com/jetbrains/python/psi/PyFileElementType.java b/python/src/com/jetbrains/python/psi/PyFileElementType.java index 11cd013273ee..e95590bbb9f3 100644 --- a/python/src/com/jetbrains/python/psi/PyFileElementType.java +++ b/python/src/com/jetbrains/python/psi/PyFileElementType.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -62,7 +62,7 @@ public class PyFileElementType extends IStubFileElementType { @Override public int getStubVersion() { // Don't forget to update versions of indexes that use the updated stub-based elements - return 58; + return 59; } @Nullable diff --git a/python/src/com/jetbrains/python/psi/impl/stubs/PyNamedTupleStubImpl.java b/python/src/com/jetbrains/python/psi/impl/stubs/PyNamedTupleStubImpl.java index c4a3ec6b6e37..e68dd73e5da8 100644 --- a/python/src/com/jetbrains/python/psi/impl/stubs/PyNamedTupleStubImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/stubs/PyNamedTupleStubImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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,17 +15,21 @@ */ package com.jetbrains.python.psi.impl.stubs; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.stubs.StubInputStream; import com.intellij.psi.stubs.StubOutputStream; import com.intellij.psi.util.QualifiedName; +import com.intellij.util.ArrayUtil; import com.intellij.util.io.StringRef; import com.jetbrains.python.PyNames; +import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyPsiUtils; import com.jetbrains.python.psi.resolve.PyResolveUtil; import com.jetbrains.python.psi.stubs.PyNamedTupleStub; +import one.util.streamex.StreamEx; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -70,22 +74,22 @@ public class PyNamedTupleStubImpl implements PyNamedTupleStub { return null; } - final QualifiedName namedTupleQName = getNamedTupleQName(calleeReference); + final Pair calleeNameAndModule = getCalleeNameAndNTModule(calleeReference); - if (namedTupleQName != null) { + if (calleeNameAndModule != null) { final String name = resolveTupleName(expression); if (name == null) { return null; } - final List fields = resolveTupleFields(expression); + final List fields = resolveTupleFields(expression, calleeNameAndModule.getSecond()); if (fields == null) { return null; } - return new PyNamedTupleStubImpl(namedTupleQName, name, fields); + return new PyNamedTupleStubImpl(calleeNameAndModule.getFirst(), name, fields); } return null; @@ -144,14 +148,14 @@ public class PyNamedTupleStubImpl implements PyNamedTupleStub { } @Nullable - private static QualifiedName getNamedTupleQName(@NotNull PyReferenceExpression referenceExpression) { - final QualifiedName name = getFullyQualifiedNamedTupleQName(referenceExpression); + private static Pair getCalleeNameAndNTModule(@NotNull PyReferenceExpression referenceExpression) { + final Pair name = getFullyQCalleeNameAndNTModule(referenceExpression); if (name != null) { return name; } - return getImportedNamedTupleQName(referenceExpression); + return getImportedCalleeNameAndNTModule(referenceExpression); } @Nullable @@ -165,6 +169,13 @@ public class PyNamedTupleStubImpl implements PyNamedTupleStub { // Point = namedtuple(("Point"), ...) + // name = "Point" + // Point = NamedTuple(name, ...) + + // Point = NamedTuple("Point", ...) + + // Point = NamedTuple(("Point"), ...) + final PyExpression nameExpression = PyPsiUtils.flattenParens(callExpression.getArgument(0, PyExpression.class)); if (nameExpression instanceof PyReferenceExpression) { @@ -175,27 +186,15 @@ public class PyNamedTupleStubImpl implements PyNamedTupleStub { } @Nullable - private static List resolveTupleFields(@NotNull PyCallExpression callExpression) { - // SUPPORTED CASES: - - // fields = ["x", "y"] - // Point = namedtuple(..., fields) - - // Point = namedtuple(..., "x y") - - // Point = namedtuple(..., ("x y")) - - // Point = namedtuple(..., "x, y") - - // Point = namedtuple(..., ["x", "y"]) - - final PyExpression fieldsExpression = PyPsiUtils.flattenParens(callExpression.getArgument(1, PyExpression.class)); - - if (fieldsExpression instanceof PyReferenceExpression) { - return extractFields(fullResolveLocally((PyReferenceExpression)fieldsExpression)); + private static List resolveTupleFields(@NotNull PyCallExpression callExpression, @NotNull NamedTupleModule module) { + switch (module) { + case TYPING: + return resolveTypingNTFields(callExpression); + case COLLECTIONS: + return resolveCollectionsNTFields(callExpression); + default: + return null; } - - return extractFields(fieldsExpression); } @NotNull @@ -214,7 +213,7 @@ public class PyNamedTupleStubImpl implements PyNamedTupleStub { } @Nullable - private static QualifiedName getFullyQualifiedNamedTupleQName(@NotNull PyReferenceExpression referenceExpression) { + private static Pair getFullyQCalleeNameAndNTModule(@NotNull PyReferenceExpression referenceExpression) { // SUPPORTED CASES: // import collections @@ -223,14 +222,27 @@ public class PyNamedTupleStubImpl implements PyNamedTupleStub { // import collections as c // Point = c.namedtuple(...) - if (PyNames.NAMEDTUPLE.equals(referenceExpression.getName())) { + // import typing + // ... = typing.NamedTuple(...) + + // import typing as t + // ... = t.NamedTuple(...) + + final String referenceName = referenceExpression.getName(); + final NamedTupleModule module = PyNames.NAMEDTUPLE.equals(referenceName) + ? NamedTupleModule.COLLECTIONS + : PyTypingTypeProvider.NAMEDTUPLE_SIMPLE.equals(referenceName) + ? NamedTupleModule.TYPING + : null; + + if (module != null) { final PyExpression qualifier = referenceExpression.getQualifier(); if (qualifier instanceof PyReferenceExpression) { final PyReferenceExpression qualifierReference = (PyReferenceExpression)qualifier; - if (!qualifierReference.isQualified() && resolvesToCollections(qualifierReference)) { - return QualifiedName.fromComponents(qualifierReference.getName(), referenceExpression.getName()); + if (!qualifierReference.isQualified() && resolvesToModule(qualifierReference, module)) { + return Pair.createNonNull(QualifiedName.fromComponents(qualifierReference.getName(), referenceName), module); } } } @@ -239,7 +251,7 @@ public class PyNamedTupleStubImpl implements PyNamedTupleStub { } @Nullable - private static QualifiedName getImportedNamedTupleQName(@NotNull PyReferenceExpression referenceExpression) { + private static Pair getImportedCalleeNameAndNTModule(@NotNull PyReferenceExpression referenceExpression) { // SUPPORTED CASES: // from collections import namedtuple @@ -248,18 +260,31 @@ public class PyNamedTupleStubImpl implements PyNamedTupleStub { // from collections import namedtuple as NT // Point = NT(...) + // from typing import NamedTuple + // Point = NamedTuple(...) + + // from typing import NamedTuple as NT + // Point = NT(...) + for (PsiElement element : PyResolveUtil.resolveLocally(referenceExpression)) { if (element instanceof PyImportElement) { final PyImportElement importElement = (PyImportElement)element; + final QualifiedName importedQName = importElement.getImportedQName(); - if (equals(importElement.getImportedQName(), PyNames.NAMEDTUPLE)) { + final NamedTupleModule module = equals(importedQName, PyNames.NAMEDTUPLE) + ? NamedTupleModule.COLLECTIONS + : equals(importedQName, PyTypingTypeProvider.NAMEDTUPLE_SIMPLE) + ? NamedTupleModule.TYPING + : null; + + if (module != null) { final PyStatement importStatement = importElement.getContainingImportStatement(); if (importStatement instanceof PyFromImportStatement) { final PyFromImportStatement fromImportStatement = (PyFromImportStatement)importStatement; - if (equals(fromImportStatement.getImportSourceQName(), PyNames.COLLECTIONS)) { - return QualifiedName.fromComponents(referenceExpression.getName()); + if (equals(fromImportStatement.getImportSourceQName(), module.getModuleName())) { + return Pair.createNonNull(QualifiedName.fromComponents(referenceExpression.getName()), module); } } } @@ -269,12 +294,12 @@ public class PyNamedTupleStubImpl implements PyNamedTupleStub { return null; } - private static boolean resolvesToCollections(@NotNull PyReferenceExpression referenceExpression) { + private static boolean resolvesToModule(@NotNull PyReferenceExpression referenceExpression, @NotNull NamedTupleModule module) { for (PsiElement element : PyResolveUtil.resolveLocally(referenceExpression)) { if (element instanceof PyImportElement) { final PyImportElement importElement = (PyImportElement)element; - if (equals(importElement.getImportedQName(), PyNames.COLLECTIONS)) { + if (equals(importElement.getImportedQName(), module.getModuleName())) { return true; } } @@ -305,32 +330,98 @@ public class PyNamedTupleStubImpl implements PyNamedTupleStub { } @Nullable - private static List extractFields(@Nullable PyExpression expression) { - if (expression == null) { - return null; - } + private static List resolveCollectionsNTFields(@NotNull PyCallExpression callExpression) { + // SUPPORTED CASES: - final List listValue = PyUtil.strListValue(expression); + // fields = ["x", "y"] + // Point = namedtuple(..., fields) - if (listValue != null) { - return listValue; - } + // Point = namedtuple(..., "x y") - return extractFields(PyPsiUtils.strValue(expression)); + // Point = namedtuple(..., ("x y")) + + // Point = namedtuple(..., "x, y") + + // Point = namedtuple(..., ["x", "y"]) + + final PyExpression fields = PyPsiUtils.flattenParens(callExpression.getArgument(1, PyExpression.class)); + + final PyExpression resolvedFields = fields instanceof PyReferenceExpression + ? fullResolveLocally((PyReferenceExpression)fields) + : fields; + + final List listValue = PyUtil.strListValue(resolvedFields); + if (listValue != null) return listValue; + + final String resolvedFieldsValue = PyPsiUtils.strValue(resolvedFields); + if (resolvedFieldsValue == null) return null; + + return StreamEx + .of(StringUtil.tokenize(resolvedFieldsValue, ", ").iterator()) + .toList(); } @Nullable - private static List extractFields(@Nullable String fieldsString) { - if (fieldsString == null) { - return null; + private static List resolveTypingNTFields(@NotNull PyCallExpression callExpression) { + // SUPPORTED CASES: + + // fields = [("x", str), ("y", int)] + // Point = NamedTuple(..., fields) + + // Point = NamedTuple(..., [("x", str), ("y", int)]) + + // Point = NamedTuple(..., x=str, y=int) + + final PyExpression secondArgument = PyPsiUtils.flattenParens(callExpression.getArgument(1, PyExpression.class)); + + if (secondArgument instanceof PyKeywordArgument) { + final PyExpression[] arguments = callExpression.getArguments(); + return StreamEx + .of(arguments, 1, arguments.length) + .select(PyKeywordArgument.class) + .map(PyKeywordArgument::getKeyword) + .toList(); + } else { + final PyExpression resolvedFields = secondArgument instanceof PyReferenceExpression + ? fullResolveLocally((PyReferenceExpression)secondArgument) + : secondArgument; + if (!(resolvedFields instanceof PySequenceExpression)) return null; + + final List result = new ArrayList<>(); + + for (PyExpression element : ((PySequenceExpression)resolvedFields).getElements()) { + if (!(element instanceof PyParenthesizedExpression)) return null; + + final PyExpression contained = ((PyParenthesizedExpression)element).getContainedExpression(); + if (!(contained instanceof PyTupleExpression)) return null; + + final PyExpression[] nameAndType = ((PyTupleExpression)contained).getElements(); + final PyExpression name = ArrayUtil.getFirstElement(nameAndType); + if (nameAndType.length != 2 || !(name instanceof PyStringLiteralExpression)) return null; + + result.add(((PyStringLiteralExpression)name).getStringValue()); + } + + return result; } + } - final List result = new ArrayList<>(); + private enum NamedTupleModule { - for (String name : StringUtil.tokenize(fieldsString, ", ")) { - result.add(name); - } + COLLECTIONS { + @Override + public String getModuleName() { + return PyNames.COLLECTIONS; + } + }, - return result; + TYPING { + @Override + public String getModuleName() { + return PyTypingTypeProvider.TYPING; + } + }; + + public abstract String getModuleName(); } } diff --git a/python/testData/stubs/FullyQualifiedTypingNamedTuple.py b/python/testData/stubs/FullyQualifiedTypingNamedTuple.py new file mode 100644 index 000000000000..0a0c72972354 --- /dev/null +++ b/python/testData/stubs/FullyQualifiedTypingNamedTuple.py @@ -0,0 +1,3 @@ +import typing + +nt = typing.NamedTuple("name", [("field", str)]) \ No newline at end of file diff --git a/python/testData/stubs/FullyQualifiedTypingNamedTupleKwargs.py b/python/testData/stubs/FullyQualifiedTypingNamedTupleKwargs.py new file mode 100644 index 000000000000..b968fb3ec634 --- /dev/null +++ b/python/testData/stubs/FullyQualifiedTypingNamedTupleKwargs.py @@ -0,0 +1,3 @@ +import typing + +nt = typing.NamedTuple("name", field=str) \ No newline at end of file diff --git a/python/testData/stubs/FullyQualifiedTypingNamedTupleKwargsWithAs.py b/python/testData/stubs/FullyQualifiedTypingNamedTupleKwargsWithAs.py new file mode 100644 index 000000000000..2d9e52c41d7b --- /dev/null +++ b/python/testData/stubs/FullyQualifiedTypingNamedTupleKwargsWithAs.py @@ -0,0 +1,3 @@ +import typing as T + +nt = T.NamedTuple("name", field=str) \ No newline at end of file diff --git a/python/testData/stubs/FullyQualifiedTypingNamedTupleWithAs.py b/python/testData/stubs/FullyQualifiedTypingNamedTupleWithAs.py new file mode 100644 index 000000000000..98b805441fb2 --- /dev/null +++ b/python/testData/stubs/FullyQualifiedTypingNamedTupleWithAs.py @@ -0,0 +1,3 @@ +import typing as T + +nt = T.NamedTuple("name", [("field", str)]) \ No newline at end of file diff --git a/python/testData/stubs/ImportedTypingNamedTuple.py b/python/testData/stubs/ImportedTypingNamedTuple.py new file mode 100644 index 000000000000..f123cc049321 --- /dev/null +++ b/python/testData/stubs/ImportedTypingNamedTuple.py @@ -0,0 +1,3 @@ +from typing import NamedTuple + +nt = NamedTuple("name", [("field", str)]) \ No newline at end of file diff --git a/python/testData/stubs/ImportedTypingNamedTupleFields.py b/python/testData/stubs/ImportedTypingNamedTupleFields.py new file mode 100644 index 000000000000..649fa3cf576a --- /dev/null +++ b/python/testData/stubs/ImportedTypingNamedTupleFields.py @@ -0,0 +1,4 @@ +from typing import NamedTuple +from b import fields + +nt = NamedTuple("name", fields) \ No newline at end of file diff --git a/python/testData/stubs/ImportedTypingNamedTupleKwargs.py b/python/testData/stubs/ImportedTypingNamedTupleKwargs.py new file mode 100644 index 000000000000..7ee5acf92e5f --- /dev/null +++ b/python/testData/stubs/ImportedTypingNamedTupleKwargs.py @@ -0,0 +1,3 @@ +from typing import NamedTuple + +nt = NamedTuple("name", field=str) \ No newline at end of file diff --git a/python/testData/stubs/ImportedTypingNamedTupleKwargsName.py b/python/testData/stubs/ImportedTypingNamedTupleKwargsName.py new file mode 100644 index 000000000000..de907d47e64b --- /dev/null +++ b/python/testData/stubs/ImportedTypingNamedTupleKwargsName.py @@ -0,0 +1,4 @@ +from typing import NamedTuple +from b import name + +nt = NamedTuple(name, x=str, y=int) \ No newline at end of file diff --git a/python/testData/stubs/ImportedTypingNamedTupleKwargsWithAs.py b/python/testData/stubs/ImportedTypingNamedTupleKwargsWithAs.py new file mode 100644 index 000000000000..43824d8ccfba --- /dev/null +++ b/python/testData/stubs/ImportedTypingNamedTupleKwargsWithAs.py @@ -0,0 +1,3 @@ +from typing import NamedTuple as NT + +nt = NT("name", field=str) \ No newline at end of file diff --git a/python/testData/stubs/ImportedTypingNamedTupleName.py b/python/testData/stubs/ImportedTypingNamedTupleName.py new file mode 100644 index 000000000000..e0eb71d471bc --- /dev/null +++ b/python/testData/stubs/ImportedTypingNamedTupleName.py @@ -0,0 +1,4 @@ +from typing import NamedTuple +from b import name + +nt = NamedTuple(name, [("x", str), ("y", int)]) \ No newline at end of file diff --git a/python/testData/stubs/ImportedTypingNamedTupleWithAs.py b/python/testData/stubs/ImportedTypingNamedTupleWithAs.py new file mode 100644 index 000000000000..ef85b30618a3 --- /dev/null +++ b/python/testData/stubs/ImportedTypingNamedTupleWithAs.py @@ -0,0 +1,3 @@ +from typing import NamedTuple as NT + +nt = NT("name", [("field", str)]) \ No newline at end of file diff --git a/python/testData/stubs/TypingNamedTupleFieldsChain.py b/python/testData/stubs/TypingNamedTupleFieldsChain.py new file mode 100644 index 000000000000..08f97f5c22e1 --- /dev/null +++ b/python/testData/stubs/TypingNamedTupleFieldsChain.py @@ -0,0 +1,5 @@ +from typing import NamedTuple + +fields0 = [("x", str), ("y", int)] +fields = fields0 +nt = NamedTuple("name", fields) \ No newline at end of file diff --git a/python/testData/stubs/TypingNamedTupleFieldsReference.py b/python/testData/stubs/TypingNamedTupleFieldsReference.py new file mode 100644 index 000000000000..de9e4629f175 --- /dev/null +++ b/python/testData/stubs/TypingNamedTupleFieldsReference.py @@ -0,0 +1,4 @@ +from typing import NamedTuple + +fields = [("x", str), ("y", int)] +nt = NamedTuple("name", fields) \ No newline at end of file diff --git a/python/testData/stubs/TypingNamedTupleKwargsNameChain.py b/python/testData/stubs/TypingNamedTupleKwargsNameChain.py new file mode 100644 index 000000000000..af123e5b88b2 --- /dev/null +++ b/python/testData/stubs/TypingNamedTupleKwargsNameChain.py @@ -0,0 +1,5 @@ +from typing import NamedTuple + +name0 = "name" +name = name0 +nt = NamedTuple(name, x=str, y=int) \ No newline at end of file diff --git a/python/testData/stubs/TypingNamedTupleKwargsNameReference.py b/python/testData/stubs/TypingNamedTupleKwargsNameReference.py new file mode 100644 index 000000000000..781aedeb074c --- /dev/null +++ b/python/testData/stubs/TypingNamedTupleKwargsNameReference.py @@ -0,0 +1,4 @@ +from typing import NamedTuple + +name = "name" +nt = NamedTuple(name, x=str, y=int) \ No newline at end of file diff --git a/python/testData/stubs/TypingNamedTupleNameChain.py b/python/testData/stubs/TypingNamedTupleNameChain.py new file mode 100644 index 000000000000..338bc79f7077 --- /dev/null +++ b/python/testData/stubs/TypingNamedTupleNameChain.py @@ -0,0 +1,5 @@ +from typing import NamedTuple + +name0 = "name" +name = name0 +nt = NamedTuple(name, [("x", str), ("y", int)]) \ No newline at end of file diff --git a/python/testData/stubs/TypingNamedTupleNameReference.py b/python/testData/stubs/TypingNamedTupleNameReference.py new file mode 100644 index 000000000000..3437d9e5db67 --- /dev/null +++ b/python/testData/stubs/TypingNamedTupleNameReference.py @@ -0,0 +1,4 @@ +from typing import NamedTuple + +name = "name" +nt = NamedTuple(name, [("x", str), ("y", int)]) \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyStubsTest.java b/python/testSrc/com/jetbrains/python/PyStubsTest.java index d6b65743325c..5a055a1fe49c 100644 --- a/python/testSrc/com/jetbrains/python/PyStubsTest.java +++ b/python/testSrc/com/jetbrains/python/PyStubsTest.java @@ -490,6 +490,94 @@ public class PyStubsTest extends PyTestCase { doTestUnsupportedNamedTuple(); } + public void testFullyQualifiedTypingNamedTuple() { + doTestNamedTuple( + QualifiedName.fromDottedString("typing.NamedTuple") + ); + } + + public void testFullyQualifiedTypingNamedTupleWithAs() { + doTestNamedTuple( + QualifiedName.fromDottedString("T.NamedTuple") + ); + } + + public void testImportedTypingNamedTuple() { + doTestNamedTuple( + QualifiedName.fromComponents("NamedTuple") + ); + } + + public void testImportedTypingNamedTupleWithAs() { + doTestNamedTuple( + QualifiedName.fromComponents("NT") + ); + } + + public void testTypingNamedTupleNameReference() { + doTestNamedTypingTupleArguments(); + } + + public void testTypingNamedTupleFieldsReference() { + doTestNamedTypingTupleArguments(); + } + + public void testTypingNamedTupleNameChain() { + doTestNamedTypingTupleArguments(); + } + + public void testTypingNamedTupleFieldsChain() { + doTestNamedTypingTupleArguments(); + } + + public void _testImportedTypingNamedTupleName() { + doTestUnsupportedNamedTuple(); + } + + public void _testImportedTypingNamedTupleFields() { + doTestUnsupportedNamedTuple(); + } + + public void testFullyQualifiedTypingNamedTupleKwargs() { + doTestNamedTuple( + QualifiedName.fromDottedString("typing.NamedTuple") + ); + } + + public void testFullyQualifiedTypingNamedTupleKwargsWithAs() { + doTestNamedTuple( + QualifiedName.fromDottedString("T.NamedTuple") + ); + } + + public void testImportedTypingNamedTupleKwargs() { + doTestNamedTuple( + QualifiedName.fromComponents("NamedTuple") + ); + } + + public void testImportedTypingNamedTupleKwargsWithAs() { + doTestNamedTuple( + QualifiedName.fromComponents("NT") + ); + } + + public void testTypingNamedTupleKwargsNameReference() { + doTestNamedTypingTupleArguments(); + } + + public void testTypingNamedTupleKwargsNameChain() { + doTestNamedTypingTupleArguments(); + } + + public void _testImportedTypingNamedTupleKwargsName() { + doTestUnsupportedNamedTuple(); + } + + public void _testImportedTypingNamedTupleKwargsFields() { + doTestUnsupportedNamedTuple(); + } + private void doTestNamedTuple(@NotNull QualifiedName expectedCalleeName) { doTestNamedTuple("name", Collections.singletonList("field"), expectedCalleeName); } @@ -498,6 +586,10 @@ public class PyStubsTest extends PyTestCase { doTestNamedTuple("name", Arrays.asList("x", "y"), QualifiedName.fromComponents("namedtuple")); } + private void doTestNamedTypingTupleArguments() { + doTestNamedTuple("name", Arrays.asList("x", "y"), QualifiedName.fromComponents("NamedTuple")); + } + private void doTestNamedTuple(@NotNull String expectedName, @NotNull List expectedFields, @NotNull QualifiedName expectedCalleeName) { diff --git a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java index 480437f318ce..7496bec64430 100644 --- a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java +++ b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -1136,6 +1136,62 @@ public class PythonCompletionTest extends PyTestCase { assertDoesntContain(suggested, "_T", "_KT"); } + // PY-18246 + public void testTypingNamedTupleCreatedViaCallInstance() { + myFixture.copyDirectoryToProject("../typing", ""); + + final List suggested = doTestByText( + "from typing import NamedTuple\n" + + "EmployeeRecord = NamedTuple('EmployeeRecord', [\n" + + " ('name', str),\n" + + " ('age', int),\n" + + " ('title', str),\n" + + " ('department', str)\n" + + "])\n" + + "e = EmployeeRecord('n', 'a', 't', 'd')\n" + + "e." + ); + assertNotNull(suggested); + assertContainsElements(suggested, "name", "age", "title", "department"); + } + + // PY-18246 + public void testTypingNamedTupleCreatedViaKwargsCallInstance() { + myFixture.copyDirectoryToProject("../typing", ""); + + final List suggested = doTestByText( + "from typing import NamedTuple\n" + + "EmployeeRecord = NamedTuple('EmployeeRecord', name=str, age=int, title=str, department=str)\n" + + "e = EmployeeRecord('n', 'a', 't', 'd')\n" + + "e." + ); + assertNotNull(suggested); + assertContainsElements(suggested, "name", "age", "title", "department"); + } + + // PY-18246 + public void testTypingNamedTupleCreatedViaInheritanceInstance() { + runWithLanguageLevel( + LanguageLevel.PYTHON36, + () -> { + myFixture.copyDirectoryToProject("../typing", ""); + + final List suggested = doTestByText( + "from typing import NamedTuple\n" + + "class EmployeeRecord(NamedTuple):\n" + + " name: str\n" + + " age: int\n" + + " title: str\n" + + " department: str\n" + + "e = EmployeeRecord('n', 'a', 't', 'd')\n" + + "e." + ); + assertNotNull(suggested); + assertContainsElements(suggested, "name", "age", "title", "department"); + } + ); + } + // PY-21519 public void testTypeComment() { myFixture.copyFileToProject("../typing/typing.py"); From e953f46acdd81a92cef0ea6512a59159ef430209 Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Tue, 16 May 2017 15:09:03 +0300 Subject: [PATCH 074/136] Don't analyze calls to `typing.NamedTuple` while inferring call type in PyCallExpressionHelper. They will be processed in `PyStdlibTypeProvider.getReferenceType`. --- .../psi/impl/PyOverridingTypeProvider.java | 19 ++++++++++ python/src/META-INF/python-core-common.xml | 1 + .../stdlib/PyStdlibOverridingTypeProvider.kt | 38 +++++++++++++++++++ .../stdlib/PyStdlibTypeProvider.java | 6 +-- .../psi/impl/PyCallExpressionHelper.java | 5 +++ .../psi/impl/PyReferenceExpressionImpl.java | 13 +++++++ .../TypingNamedTupleAsParameter.py | 12 ++++++ .../Py3TypeCheckerInspectionTest.java | 4 ++ 8 files changed, 95 insertions(+), 3 deletions(-) create mode 100644 python/psi-api/src/com/jetbrains/python/psi/impl/PyOverridingTypeProvider.java create mode 100644 python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibOverridingTypeProvider.kt create mode 100644 python/testData/inspections/PyTypeCheckerInspection/TypingNamedTupleAsParameter.py diff --git a/python/psi-api/src/com/jetbrains/python/psi/impl/PyOverridingTypeProvider.java b/python/psi-api/src/com/jetbrains/python/psi/impl/PyOverridingTypeProvider.java new file mode 100644 index 000000000000..ca59095b042c --- /dev/null +++ b/python/psi-api/src/com/jetbrains/python/psi/impl/PyOverridingTypeProvider.java @@ -0,0 +1,19 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.jetbrains.python.psi.impl; + +public interface PyOverridingTypeProvider extends PyTypeProvider { +} diff --git a/python/src/META-INF/python-core-common.xml b/python/src/META-INF/python-core-common.xml index 1fcd94f60f08..eb4d7a1905f1 100644 --- a/python/src/META-INF/python-core-common.xml +++ b/python/src/META-INF/python-core-common.xml @@ -680,6 +680,7 @@ + diff --git a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibOverridingTypeProvider.kt b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibOverridingTypeProvider.kt new file mode 100644 index 000000000000..90b7b71ad7b6 --- /dev/null +++ b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibOverridingTypeProvider.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.jetbrains.python.codeInsight.stdlib + +import com.intellij.psi.PsiElement +import com.jetbrains.python.PyNames +import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider +import com.jetbrains.python.psi.PyFunction +import com.jetbrains.python.psi.impl.PyOverridingTypeProvider +import com.jetbrains.python.psi.types.PyType +import com.jetbrains.python.psi.types.PyTypeProviderBase +import com.jetbrains.python.psi.types.TypeEvalContext + +class PyStdlibOverridingTypeProvider : PyTypeProviderBase(), PyOverridingTypeProvider { + + override fun getReferenceType(referenceTarget: PsiElement, context: TypeEvalContext, anchor: PsiElement?): PyType? { + return if (isTypingNamedTupleInit(referenceTarget)) PyStdlibTypeProvider.getNamedTupleType(referenceTarget, context, anchor) else null + } + + private fun isTypingNamedTupleInit(referenceTarget: PsiElement): Boolean { + return referenceTarget is PyFunction && + PyNames.INIT == referenceTarget.name && + PyTypingTypeProvider.NAMEDTUPLE == referenceTarget.containingClass?.qualifiedName + } +} \ No newline at end of file diff --git a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java index be83fb0bb7ed..0f02f2196117 100644 --- a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java @@ -279,9 +279,9 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase { } @Nullable - private static PyType getNamedTupleType(@NotNull PsiElement referenceTarget, - @NotNull TypeEvalContext context, - @Nullable PsiElement anchor) { + static PyType getNamedTupleType(@NotNull PsiElement referenceTarget, + @NotNull TypeEvalContext context, + @Nullable PsiElement anchor) { if (referenceTarget instanceof PyTargetExpression) { final PyTargetExpression target = (PyTargetExpression)referenceTarget; final PyTargetExpressionStub stub = target.getStub(); diff --git a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java index 7e734531e9af..43ec7333f878 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java +++ b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java @@ -473,6 +473,11 @@ public class PyCallExpressionHelper { @Nullable private static Ref getCallTargetReturnType(@NotNull PyCallExpression call, @NotNull PsiElement target, @NotNull TypeEvalContext context) { + final PyType providedOverridingType = PyReferenceExpressionImpl.getReferenceTypeFromOverridingProviders(target, context, call); + if (providedOverridingType instanceof PyCallableType) { + return Ref.create(((PyCallableType)providedOverridingType).getCallType(context, call)); + } + PyClass cls = null; PyFunction init = null; if (target instanceof PyClass) { diff --git a/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java index 6b363f4e636b..50964690da49 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java @@ -41,6 +41,7 @@ import com.jetbrains.python.psi.impl.references.PyReferenceImpl; import com.jetbrains.python.psi.resolve.*; import com.jetbrains.python.psi.types.*; import com.jetbrains.python.refactoring.PyDefUseUtil; +import one.util.streamex.StreamEx; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -488,6 +489,18 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere return null; } + @Nullable + public static PyType getReferenceTypeFromOverridingProviders(@NotNull PsiElement target, + @NotNull TypeEvalContext context, + @Nullable PsiElement anchor) { + return StreamEx + .of(Extensions.getExtensions(PyTypeProvider.EP_NAME)) + .select(PyOverridingTypeProvider.class) + .map(provider -> provider.getReferenceType(target, context, anchor)) + .findFirst(Objects::nonNull) + .orElse(null); + } + @Nullable public static PyType getReferenceTypeFromProviders(@NotNull PsiElement target, @NotNull TypeEvalContext context, diff --git a/python/testData/inspections/PyTypeCheckerInspection/TypingNamedTupleAsParameter.py b/python/testData/inspections/PyTypeCheckerInspection/TypingNamedTupleAsParameter.py new file mode 100644 index 000000000000..c638e5985517 --- /dev/null +++ b/python/testData/inspections/PyTypeCheckerInspection/TypingNamedTupleAsParameter.py @@ -0,0 +1,12 @@ +from typing import NamedTuple + + +nt = NamedTuple("name", [("field", str)]) + + +def foo(x: nt): + pass + + +foo(5) +foo(nt(field = "f")) \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java index cbaef8617e4e..c118d3effe03 100644 --- a/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java @@ -233,4 +233,8 @@ public class Py3TypeCheckerInspectionTest extends PyTestCase { public void testGenericKwargs() { doTest(); } + + public void testTypingNamedTupleAsParameter() { + doTest(); + } } From 4c8d424207777fcf93f2982d212f3bd5028ddb7c Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Mon, 17 Apr 2017 19:09:17 +0300 Subject: [PATCH 075/136] PY-7322 Fixed: namedtuple types are not detected with reST :type : Pass `TypeEvalContext` to `PyTypeParser.getTypeByName` and `PyTypeParser.parse`. This allows to infer named tuple type from doc string when file was unstubbed. --- .../numpy/codeInsight/NumpyResolveRater.java | 4 +- .../controlflow/PyTypeAssertionEvaluator.java | 2 +- .../stdlib/PyStdlibTypeProvider.java | 14 ++-- .../typing/PyTypingTypeProvider.java | 6 +- .../debugger/PyCallSignatureTypeProvider.java | 6 +- .../python/debugger/PySignatureUtil.java | 75 ------------------- .../documentation/PyDocumentationBuilder.java | 11 +-- .../docstrings/PyDocStringTypeProvider.java | 10 +-- .../PyDocstringTypesInspection.java | 46 ++++++++++-- .../inspections/PyStringFormatInspection.java | 10 +-- .../python/psi/types/PyTypeParser.java | 47 ++++++++---- .../refactoring/PyReplaceExpressionUtil.java | 10 +-- .../com/jetbrains/python/PyTypeTest.java | 12 +++ 13 files changed, 121 insertions(+), 132 deletions(-) delete mode 100644 python/src/com/jetbrains/python/debugger/PySignatureUtil.java diff --git a/python/src/com/jetbrains/numpy/codeInsight/NumpyResolveRater.java b/python/src/com/jetbrains/numpy/codeInsight/NumpyResolveRater.java index 1ab734ab0dde..6bf899f43c9c 100644 --- a/python/src/com/jetbrains/numpy/codeInsight/NumpyResolveRater.java +++ b/python/src/com/jetbrains/numpy/codeInsight/NumpyResolveRater.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -29,7 +29,7 @@ public class NumpyResolveRater extends PyResolveResultRaterBase { @Override public int getMemberRate(PsiElement member, PyType type, TypeEvalContext context) { if (member instanceof PsiNamedElement) { - final PyType ndArray = PyTypeParser.getTypeByName(member, NumpyDocStringTypeProvider.NDARRAY); + final PyType ndArray = PyTypeParser.getTypeByName(member, NumpyDocStringTypeProvider.NDARRAY, context); if (ndArray != null && PyTypeChecker.match(ndArray, type, context) && PyNames.isRightOperatorName(((PsiNamedElement)member).getName())) { return 100; diff --git a/python/src/com/jetbrains/python/codeInsight/controlflow/PyTypeAssertionEvaluator.java b/python/src/com/jetbrains/python/codeInsight/controlflow/PyTypeAssertionEvaluator.java index 2eea3ed647ff..44489ad8a501 100644 --- a/python/src/com/jetbrains/python/codeInsight/controlflow/PyTypeAssertionEvaluator.java +++ b/python/src/com/jetbrains/python/codeInsight/controlflow/PyTypeAssertionEvaluator.java @@ -76,7 +76,7 @@ public class PyTypeAssertionEvaluator extends PyRecursiveElementVisitor { if (args.length == 1 && args[0] instanceof PyReferenceExpression) { final PyReferenceExpression target = (PyReferenceExpression)args[0]; - pushAssertion(target, myPositive, context -> PyTypeParser.getTypeByName(target, "collections." + PyNames.CALLABLE)); + pushAssertion(target, myPositive, context -> PyTypeParser.getTypeByName(target, "collections." + PyNames.CALLABLE, context)); } } } diff --git a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java index 0f02f2196117..a11685feaa9d 100644 --- a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java @@ -151,7 +151,7 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase { } } else if ("enum.EnumMeta.__members__".equals(name)) { - return PyTypeParser.getTypeByName(referenceTarget, "dict[str, unknown]"); + return PyTypeParser.getTypeByName(referenceTarget, "dict[str, unknown]", context); } } return null; @@ -163,7 +163,8 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase { final String qname = function.getQualifiedName(); if (qname != null) { if (OPEN_FUNCTIONS.contains(qname) && callSite instanceof PyCallExpression) { - return getOpenFunctionType(qname, PyCallExpressionHelper.mapArguments(callSite, function, context).getMappedParameters(), callSite); + final PyCallExpressionHelper.ArgumentMappingResults mapping = PyCallExpressionHelper.mapArguments(callSite, function, context); + return getOpenFunctionType(qname, mapping.getMappedParameters(), callSite, context); } else if ("tuple.__init__".equals(qname) && callSite instanceof PyCallExpression) { return getTupleInitializationType((PyCallExpression)callSite, context); @@ -301,7 +302,8 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase { @NotNull private static Ref getOpenFunctionType(@NotNull String callQName, @NotNull Map arguments, - @NotNull PsiElement anchor) { + @NotNull PsiElement anchor, + @NotNull TypeEvalContext context) { String mode = "r"; for (Map.Entry entry : arguments.entrySet()) { final PyNamedParameter parameter = entry.getValue(); @@ -319,14 +321,14 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase { if (LanguageLevel.forElement(anchor).isAtLeast(LanguageLevel.PYTHON30) || "io.open".equals(callQName) || "_io.open".equals(callQName)) { if (mode.contains("b")) { - return Ref.create(PyTypeParser.getTypeByName(anchor, PY3K_BINARY_FILE_TYPE)); + return Ref.create(PyTypeParser.getTypeByName(anchor, PY3K_BINARY_FILE_TYPE, context)); } else { - return Ref.create(PyTypeParser.getTypeByName(anchor, PY3K_TEXT_FILE_TYPE)); + return Ref.create(PyTypeParser.getTypeByName(anchor, PY3K_TEXT_FILE_TYPE, context)); } } - return Ref.create(PyTypeParser.getTypeByName(anchor, PY2K_FILE_TYPE)); + return Ref.create(PyTypeParser.getTypeByName(anchor, PY2K_FILE_TYPE, context)); } @Nullable diff --git a/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java b/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java index 7bf709f60bd9..d08af5e4b638 100644 --- a/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java @@ -486,7 +486,7 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { if (parameterizedType != null) { return Ref.create(parameterizedType); } - final PyType builtinCollection = getBuiltinCollection(resolved); + final PyType builtinCollection = getBuiltinCollection(resolved, context.getTypeContext()); if (builtinCollection != null) { return Ref.create(builtinCollection); } @@ -750,10 +750,10 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { } @Nullable - private static PyType getBuiltinCollection(@NotNull PsiElement element) { + private static PyType getBuiltinCollection(@NotNull PsiElement element, @NotNull TypeEvalContext context) { final String collectionName = getQualifiedName(element); final String builtinName = COLLECTION_CLASSES.get(collectionName); - return builtinName != null ? PyTypeParser.getTypeByName(element, builtinName) : null; + return builtinName != null ? PyTypeParser.getTypeByName(element, builtinName, context) : null; } @NotNull diff --git a/python/src/com/jetbrains/python/debugger/PyCallSignatureTypeProvider.java b/python/src/com/jetbrains/python/debugger/PyCallSignatureTypeProvider.java index 41734caedd8a..335c9dc4e503 100644 --- a/python/src/com/jetbrains/python/debugger/PyCallSignatureTypeProvider.java +++ b/python/src/com/jetbrains/python/debugger/PyCallSignatureTypeProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -34,7 +34,7 @@ public class PyCallSignatureTypeProvider extends PyTypeProviderBase { if (name != null) { final String typeName = PySignatureCacheManager.getInstance(param.getProject()).findParameterType(func, name); if (typeName != null) { - final PyType type = PyTypeParser.getTypeByName(param, typeName); + final PyType type = PyTypeParser.getTypeByName(param, typeName, context); if (type != null) { return Ref.create(PyDynamicallyEvaluatedType.create(type)); } @@ -51,7 +51,7 @@ public class PyCallSignatureTypeProvider extends PyTypeProviderBase { if (signature != null && signature.getReturnType() != null) { final String typeName = signature.getReturnType().getTypeQualifiedName(); if (typeName != null) { - final PyType type = PyTypeParser.getTypeByName(function, typeName); + final PyType type = PyTypeParser.getTypeByName(function, typeName, context); if (type != null) { return Ref.create(PyDynamicallyEvaluatedType.create(type)); } diff --git a/python/src/com/jetbrains/python/debugger/PySignatureUtil.java b/python/src/com/jetbrains/python/debugger/PySignatureUtil.java deleted file mode 100644 index 21094b579750..000000000000 --- a/python/src/com/jetbrains/python/debugger/PySignatureUtil.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.jetbrains.python.debugger; - -import com.google.common.collect.Collections2; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.PsiElement; -import com.jetbrains.python.PyNames; -import com.jetbrains.python.psi.PyClass; -import com.jetbrains.python.psi.PyFunction; -import com.jetbrains.python.psi.types.PyClassType; -import com.jetbrains.python.psi.types.PyType; -import com.jetbrains.python.psi.types.PyTypeParser; -import com.jetbrains.python.psi.types.PyUnionType; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * @author traff - */ -public class PySignatureUtil { - private PySignatureUtil() { - } - - @Nullable - public static String getShortestImportableName(@Nullable PsiElement anchor, @NotNull String type) { - final PyType pyType = PyTypeParser.getTypeByName(anchor, type); - if (pyType instanceof PyClassType) { - PyClass c = ((PyClassType)pyType).getPyClass(); - return c.getQualifiedName(); - } - - if (pyType != null) { - return getPrintableName(pyType); - } - else { - return type; - } - } - - private static String getPrintableName(PyType type) { - if (type instanceof PyUnionType) { - return StringUtil.join(Collections2.transform(((PyUnionType)type).getMembers(), input -> getPrintableName(input)), " or "); - } - else if (type != null) { - return type.getName(); - } - else { - return PyNames.UNKNOWN_TYPE; - } - } - - @Nullable - public static String getArgumentType(@NotNull PyFunction function, @NotNull String name) { - PySignatureCacheManager cacheManager = PySignatureCacheManager.getInstance(function.getProject()); - PySignature signature = cacheManager.findSignature(function); - if (signature != null) { - return getShortestImportableName(function, signature.getArgTypeQualifiedName(name)); - } - return null; - } -} diff --git a/python/src/com/jetbrains/python/documentation/PyDocumentationBuilder.java b/python/src/com/jetbrains/python/documentation/PyDocumentationBuilder.java index e3c2fae4e871..952e80035be6 100644 --- a/python/src/com/jetbrains/python/documentation/PyDocumentationBuilder.java +++ b/python/src/com/jetbrains/python/documentation/PyDocumentationBuilder.java @@ -163,7 +163,7 @@ public class PyDocumentationBuilder { private void buildFromParameter(@NotNull final TypeEvalContext context, @Nullable final PsiElement outerElement, @NotNull final PsiElement elementDefinition) { myBody.addItem(combUp("Parameter " + PyUtil.getReadableRepr(elementDefinition, false))); - final boolean typeFromDocstringAdded = addTypeAndDescriptionFromDocstring((PyNamedParameter)elementDefinition); + final boolean typeFromDocstringAdded = addTypeAndDescriptionFromDocstring((PyNamedParameter)elementDefinition, context); if (outerElement instanceof PyExpression) { final PyType type = context.getType((PyExpression)outerElement); if (type != null) { @@ -409,11 +409,11 @@ public class PyDocumentationBuilder { } } - private void addPredefinedMethodDoc(PyFunction fun, String mothodName) { + private void addPredefinedMethodDoc(PyFunction fun, String methodName) { final PyClassType objectType = PyBuiltinCache.getInstance(fun).getObjectType(); // old- and new-style classes share the __xxx__ stuff if (objectType != null) { final PyClass objectClass = objectType.getPyClass(); - final PyFunction predefinedMethod = objectClass.findMethodByName(mothodName, false, null); + final PyFunction predefinedMethod = objectClass.findMethodByName(methodName, false, null); if (predefinedMethod != null) { final PyStringLiteralExpression predefinedDocstring = getEffectiveDocStringExpression(predefinedMethod); final String predefinedDoc = predefinedDocstring != null ? predefinedDocstring.getStringValue() : null; @@ -468,9 +468,10 @@ public class PyDocumentationBuilder { * Adds type and description representation from function docstring * * @param parameter parameter of a function + * @param context type evaluation context * @return true if type from docstring was added */ - private boolean addTypeAndDescriptionFromDocstring(@NotNull final PyNamedParameter parameter) { + private boolean addTypeAndDescriptionFromDocstring(@NotNull PyNamedParameter parameter, @NotNull TypeEvalContext context) { final PyFunction function = PsiTreeUtil.getParentOfType(parameter, PyFunction.class); if (function != null) { final String docString = PyPsiUtils.strValue(getEffectiveDocStringExpression(function)); @@ -480,7 +481,7 @@ public class PyDocumentationBuilder { final String description = typeAndDescr.second; if (type != null) { - final PyType pyType = PyTypeParser.getTypeByName(parameter, type); + final PyType pyType = PyTypeParser.getTypeByName(parameter, type, context); if (pyType instanceof PyClassType) { myBody.addItem(": ").addWith(new LinkWrapper(PythonDocumentationProvider.LINK_TYPE_PARAM), $(pyType.getName())); } diff --git a/python/src/com/jetbrains/python/documentation/docstrings/PyDocStringTypeProvider.java b/python/src/com/jetbrains/python/documentation/docstrings/PyDocStringTypeProvider.java index 2162d63865cb..a43bfa5936ae 100644 --- a/python/src/com/jetbrains/python/documentation/docstrings/PyDocStringTypeProvider.java +++ b/python/src/com/jetbrains/python/documentation/docstrings/PyDocStringTypeProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -39,7 +39,7 @@ public class PyDocStringTypeProvider extends PyTypeProviderBase { if (docString != null) { final String typeText = docString.getParamType(param.getName()); if (StringUtil.isNotEmpty(typeText)) { - final Ref typeRef = parseType(func, typeText); + final Ref typeRef = parseType(func, typeText, context); if (param.isPositionalContainer()) { return Ref.create(PyTypeUtil.toPositionalContainerType(param, typeRef.get())); @@ -63,7 +63,7 @@ public class PyDocStringTypeProvider extends PyTypeProviderBase { if (docString != null) { final String typeText = docString.getReturnType(); if (StringUtil.isNotEmpty(typeText)) { - return parseType(callable, typeText); + return parseType(callable, typeText, context); } } } @@ -71,8 +71,8 @@ public class PyDocStringTypeProvider extends PyTypeProviderBase { } @NotNull - private static Ref parseType(@NotNull PyCallable callable, String typeText) { - final PyType type = PyTypeParser.getTypeByName(callable, typeText); + private static Ref parseType(@NotNull PyCallable callable, @NotNull String typeText, @NotNull TypeEvalContext context) { + final PyType type = PyTypeParser.getTypeByName(callable, typeText, context); if (type != null) { type.assertValid("from docstring"); } diff --git a/python/src/com/jetbrains/python/inspections/PyDocstringTypesInspection.java b/python/src/com/jetbrains/python/inspections/PyDocstringTypesInspection.java index 13fab23dce28..58b69113132f 100644 --- a/python/src/com/jetbrains/python/inspections/PyDocstringTypesInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyDocstringTypesInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,19 +20,18 @@ import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; import com.jetbrains.python.PyBundle; +import com.jetbrains.python.PyNames; import com.jetbrains.python.debugger.PySignature; import com.jetbrains.python.debugger.PySignatureCacheManager; -import com.jetbrains.python.debugger.PySignatureUtil; import com.jetbrains.python.documentation.docstrings.DocStringUtil; import com.jetbrains.python.documentation.docstrings.PlainDocString; import com.jetbrains.python.psi.PyElementGenerator; import com.jetbrains.python.psi.PyFunction; import com.jetbrains.python.psi.PyStringLiteralExpression; import com.jetbrains.python.psi.StructuredDocString; -import com.jetbrains.python.psi.types.PyType; -import com.jetbrains.python.psi.types.PyTypeChecker; -import com.jetbrains.python.psi.types.PyTypeParser; +import com.jetbrains.python.psi.types.*; import com.jetbrains.python.toolbox.Substring; +import one.util.streamex.StreamEx; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -94,7 +93,7 @@ public class PyDocstringTypesInspection extends PyInspection { if (type != null) { String dynamicType = signature.getArgTypeQualifiedName(param); if (dynamicType != null) { - String dynamicTypeShortName = PySignatureUtil.getShortestImportableName(function, dynamicType); + String dynamicTypeShortName = getShortestImportableName(function, dynamicType); if (!match(function, dynamicType, type.getValue())) { registerProblem(node, "Dynamically inferred type '" + dynamicTypeShortName + @@ -109,9 +108,40 @@ public class PyDocstringTypesInspection extends PyInspection { } } + @Nullable + private String getShortestImportableName(@Nullable PsiElement anchor, @NotNull String type) { + final PyType pyType = PyTypeParser.getTypeByName(anchor, type, myTypeEvalContext); + if (pyType instanceof PyClassType) { + return ((PyClassType)pyType).getPyClass().getQualifiedName(); + } + + if (pyType != null) { + return getPrintableName(pyType); + } + else { + return type; + } + } + + @Nullable + private static String getPrintableName(@Nullable PyType type) { + if (type instanceof PyUnionType) { + return StreamEx + .of(((PyUnionType)type).getMembers()) + .map(Visitor::getPrintableName) + .joining(" or "); + } + else if (type != null) { + return type.getName(); + } + else { + return PyNames.UNKNOWN_TYPE; + } + } + private boolean match(PsiElement anchor, String dynamicTypeName, String specifiedTypeName) { - final PyType dynamicType = PyTypeParser.getTypeByName(anchor, dynamicTypeName); - final PyType specifiedType = PyTypeParser.getTypeByName(anchor, specifiedTypeName); + final PyType dynamicType = PyTypeParser.getTypeByName(anchor, dynamicTypeName, myTypeEvalContext); + final PyType specifiedType = PyTypeParser.getTypeByName(anchor, specifiedTypeName, myTypeEvalContext); return PyTypeChecker.match(specifiedType, dynamicType, myTypeEvalContext); } } diff --git a/python/src/com/jetbrains/python/inspections/PyStringFormatInspection.java b/python/src/com/jetbrains/python/inspections/PyStringFormatInspection.java index e942a9755639..4977c1495d19 100644 --- a/python/src/com/jetbrains/python/inspections/PyStringFormatInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyStringFormatInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -118,7 +118,7 @@ public class PyStringFormatInspection extends PyInspection { final PyType elementType = tupleType.getElementType(i); if (elementType != null) { final String typeName = myFormatSpec.get(String.valueOf(i + 1)); - final PyType type = typeName != null ? PyTypeParser.getTypeByName(problemTarget, typeName) : null; + final PyType type = typeName != null ? PyTypeParser.getTypeByName(problemTarget, typeName, myTypeEvalContext) : null; checkTypeCompatible(problemTarget, elementType, type); } } @@ -171,7 +171,7 @@ public class PyStringFormatInspection extends PyInspection { else if (PyUtil.instanceOf(rightExpression, PySequenceExpression.class, PyComprehensionElement.class)) { if (s != null) { checkTypeCompatible(problemTarget, builtinCache.getStrType(), - PyTypeParser.getTypeByName(problemTarget, s)); + PyTypeParser.getTypeByName(problemTarget, s, myTypeEvalContext)); return 1; } } @@ -184,7 +184,7 @@ public class PyStringFormatInspection extends PyInspection { if (PyTypeChecker.match(listType, type, myTypeEvalContext) || PyTypeChecker.match(stringType, type, myTypeEvalContext)) { checkTypeCompatible(problemTarget, builtinCache.getStrType(), - PyTypeParser.getTypeByName(problemTarget, s)); + PyTypeParser.getTypeByName(problemTarget, s, myTypeEvalContext)); return 1; } PySliceItem sliceItem = ((PySliceExpression)rightExpression).getSliceItem(); @@ -328,7 +328,7 @@ public class PyStringFormatInspection extends PyInspection { @NotNull final String expectedTypeName, @NotNull PsiElement problemTarget) { final PyType actual = myTypeEvalContext.getType(expression); - final PyType expected = PyTypeParser.getTypeByName(problemTarget, expectedTypeName); + final PyType expected = PyTypeParser.getTypeByName(problemTarget, expectedTypeName, myTypeEvalContext); if (actual != null) { checkTypeCompatible(problemTarget, actual, expected); } diff --git a/python/src/com/jetbrains/python/psi/types/PyTypeParser.java b/python/src/com/jetbrains/python/psi/types/PyTypeParser.java index f82c0310924b..1499e771c542 100644 --- a/python/src/com/jetbrains/python/psi/types/PyTypeParser.java +++ b/python/src/com/jetbrains/python/psi/types/PyTypeParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -116,25 +116,45 @@ public class PyTypeParser { * @return null either if there was an error during parsing or if extracted type is equivalent to Any or undefined */ @Nullable - public static PyType getTypeByName(@Nullable final PsiElement anchor, @NotNull String type) { + public static PyType getTypeByName(@Nullable PsiElement anchor, @NotNull String type) { + if (anchor == null) return EMPTY_RESULT.getType(); return parse(anchor, type).getType(); } /** - * @param anchor should never be null or null will be returned + * @param anchor should never be null or null will be returned + * @param context type evaluation context + * @return null either if there was an error during parsing or if extracted type is equivalent to Any or undefined + */ + @Nullable + public static PyType getTypeByName(@Nullable PsiElement anchor, @NotNull String type, @NotNull TypeEvalContext context) { + if (anchor == null) return EMPTY_RESULT.getType(); + return parse(anchor, type, context).getType(); + } + + /** + * @param anchor should never be null or {@link PyTypeParser#EMPTY_RESULT} will be returned + * @param type representation of the type to parse */ @NotNull - public static ParseResult parse(@Nullable final PsiElement anchor, @NotNull String type) { + public static ParseResult parse(@NotNull PsiElement anchor, @NotNull String type) { + return parse(anchor, type, TypeEvalContext.codeInsightFallback(anchor.getProject())); + } + + /** + * @param anchor should never be null or {@link PyTypeParser#EMPTY_RESULT} will be returned + * @param type representation of the type to parse + * @param context type evaluation context + */ + @NotNull + public static ParseResult parse(@NotNull PsiElement anchor, @NotNull String type, @NotNull TypeEvalContext context) { PyPsiUtils.assertValid(anchor); - if (anchor == null) { - return EMPTY_RESULT; - } final ForwardDeclaration typeExpr = ForwardDeclaration.create(); final FunctionalParser classType = token(IDENTIFIER).then(many(op(".").skipThen(token(IDENTIFIER)))) - .map(new MakeSimpleType(anchor)) + .map(new MakeSimpleType(anchor, context)) .cached() .named("class-type"); @@ -304,9 +324,11 @@ public class PyTypeParser { private static class MakeSimpleType implements Function, List>>, ParseResult> { @NotNull private final PsiElement myAnchor; + @NotNull private final TypeEvalContext myContext; - public MakeSimpleType(@NotNull PsiElement anchor) { + public MakeSimpleType(@NotNull PsiElement anchor, @NotNull TypeEvalContext context) { myAnchor = anchor; + myContext = context; } @Nullable @@ -331,15 +353,14 @@ public class PyTypeParser { if (file instanceof PyFile) { final PyFile pyFile = (PyFile)file; - final TypeEvalContext context = TypeEvalContext.codeInsightFallback(file.getProject()); final Map types = new HashMap<>(); final Map fullRanges = new HashMap<>(); final Map imports = new HashMap<>(); - PyType type = resolveQualifierType(tokens, pyFile, context, types, fullRanges, imports); + PyType type = resolveQualifierType(tokens, pyFile, myContext, types, fullRanges, imports); if (type != null) { - final PyResolveContext resolveContext = PyResolveContext.defaultContext().withTypeEvalContext(context); + final PyResolveContext resolveContext = PyResolveContext.defaultContext().withTypeEvalContext(myContext); final PyExpression expression = myAnchor instanceof PyExpression ? (PyExpression)myAnchor : null; for (Token token : tokens) { @@ -350,7 +371,7 @@ public class PyTypeParser { if (results != null && !results.isEmpty()) { final PsiElement resolved = results.get(0).getElement(); if (resolved instanceof PyTypedElement) { - type = context.getType((PyTypedElement)resolved); + type = myContext.getType((PyTypedElement)resolved); if (type != null && !allowResolveToType(type)) { type = null; break; diff --git a/python/src/com/jetbrains/python/refactoring/PyReplaceExpressionUtil.java b/python/src/com/jetbrains/python/refactoring/PyReplaceExpressionUtil.java index 31017cda1cc6..13365921cf80 100644 --- a/python/src/com/jetbrains/python/refactoring/PyReplaceExpressionUtil.java +++ b/python/src/com/jetbrains/python/refactoring/PyReplaceExpressionUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 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,8 +22,6 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.tree.IElementType; import com.jetbrains.python.PyElementTypes; -import com.jetbrains.python.PythonStringUtil; -import com.jetbrains.python.inspections.PyStringFormatParser; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyBuiltinCache; import com.jetbrains.python.psi.impl.PyPsiUtils; @@ -154,7 +152,7 @@ public class PyReplaceExpressionUtil implements PyElementTypes { final PyType valueType = context.getType(formatValue); final PyBuiltinCache builtinCache = PyBuiltinCache.getInstance(oldExpression); final PyType tupleType = builtinCache.getTupleType(); - final PyType mappingType = PyTypeParser.getTypeByName(null, "collections.Mapping"); + final PyType mappingType = PyTypeParser.getTypeByName(null, "collections.Mapping", context); if (!PyTypeChecker.match(tupleType, valueType, context) || (mappingType != null && !PyTypeChecker.match(mappingType, valueType, context))) { return replaceSubstringWithSingleValueFormatting(oldExpression, textRange, prefix, suffix, formatValue, newText, substitutions); @@ -322,12 +320,12 @@ public class PyReplaceExpressionUtil implements PyElementTypes { builder.append("("); } if (!leftQuote.endsWith(prefix)) { - builder.append(prefix + rightQuote + " + "); + builder.append(prefix).append(rightQuote).append(" + "); } final int pos = builder.toString().length(); builder.append(newText); if (!rightQuote.startsWith(suffix)) { - builder.append(" + " + leftQuote + suffix); + builder.append(" + ").append(leftQuote).append(suffix); } if (hasSubstitutions) { builder.append(")"); diff --git a/python/testSrc/com/jetbrains/python/PyTypeTest.java b/python/testSrc/com/jetbrains/python/PyTypeTest.java index 85fa183469b9..ddfea706620b 100644 --- a/python/testSrc/com/jetbrains/python/PyTypeTest.java +++ b/python/testSrc/com/jetbrains/python/PyTypeTest.java @@ -1677,6 +1677,18 @@ public class PyTypeTest extends PyTestCase { " return get_class()"); } + // PY-7322 + public void testNamedTupleParameterInDocString() { + doTest("Point", + "from collections import namedtuple\n" + + "Point = namedtuple('Point', ('x', 'y'))\n" + + "def takes_a_point(point):\n" + + " \"\"\"\n" + + " :type point: Point\n" + + " \"\"\"\n" + + " expr = point"); + } + // PY-22919 public void testMaxListKnownElements() { doTest("int", From dd8cd1a094d1869b5002d398dcf7455afeb6c03c Mon Sep 17 00:00:00 2001 From: "Gregory.Shrago" Date: Mon, 5 Jun 2017 21:10:38 +0300 Subject: [PATCH 076/136] EA-98972 - IOE: VirtualDirectoryImpl.getInputStream --- .../org/intellij/plugins/intelliLang/InjectionsSettingsUI.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/InjectionsSettingsUI.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/InjectionsSettingsUI.java index 40649feaf573..9c2e48b07843 100644 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/InjectionsSettingsUI.java +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/InjectionsSettingsUI.java @@ -709,7 +709,7 @@ public class InjectionsSettingsUI extends SearchableConfigurable.Parent.Abstract } private void doImportAction(final DataContext dataContext) { - final FileChooserDescriptor descriptor = new FileChooserDescriptor(true, false, true, false, true, false) { + final FileChooserDescriptor descriptor = new FileChooserDescriptor(true, false, false, false, true, false) { @Override public boolean isFileVisible(VirtualFile file, boolean showHiddenFiles) { return super.isFileVisible(file, showHiddenFiles) && From 0ad3340f78f03fd79ed417d5fb07236513c33e84 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Thu, 4 May 2017 15:16:02 +0300 Subject: [PATCH 077/136] PY-8174 Cleanup in PyInitNewSignatureInspection: reformat + TypeEvalContext --- .../python/inspections/PyInitNewSignatureInspection.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/python/src/com/jetbrains/python/inspections/PyInitNewSignatureInspection.java b/python/src/com/jetbrains/python/inspections/PyInitNewSignatureInspection.java index ed5a883e9e7c..fdec3cde1bc5 100644 --- a/python/src/com/jetbrains/python/inspections/PyInitNewSignatureInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyInitNewSignatureInspection.java @@ -61,15 +61,15 @@ public class PyInitNewSignatureInspection extends PyInspection { if (!PyNames.NEW.equals(functionName) && !PyNames.INIT.equals(functionName)) return; final PyClass cls = node.getContainingClass(); if (cls == null) return; - if (!cls.isNewStyleClass(null)) return; + if (!cls.isNewStyleClass(myTypeEvalContext)) return; final String complementaryName = PyNames.NEW.equals(functionName) ? PyNames.INIT : PyNames.NEW; - final PyFunction complementaryMethod = cls.findMethodByName(complementaryName, true, null); + final PyFunction complementaryMethod = cls.findMethodByName(complementaryName, true, myTypeEvalContext); if (complementaryMethod == null || PyUtil.isObjectClass(assertNotNull(complementaryMethod.getContainingClass()))) return; if (!PyUtil.isSignatureCompatibleTo(complementaryMethod, node, myTypeEvalContext) && !PyUtil.isSignatureCompatibleTo(node, complementaryMethod, myTypeEvalContext) && node.getContainingFile() == cls.getContainingFile()) { - registerProblem(node.getParameterList(), PyNames.NEW.equals(node.getName()) ? PyBundle.message("INSP.new.incompatible.to.init") : - PyBundle.message("INSP.init.incompatible.to.new"), + registerProblem(node.getParameterList(), PyBundle.message(PyNames.NEW.equals(node.getName()) ? "INSP.new.incompatible.to.init" + : "INSP.init.incompatible.to.new"), new PyChangeSignatureQuickFix(false)); } } From 48a0d643e2b21bec8a5c9bead40de57cc7ba8166 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Thu, 4 May 2017 17:39:33 +0300 Subject: [PATCH 078/136] PY-8174 Refactor PyChangeSignatureQuickfix to allow passing extra parameters --- .../PyInitNewSignatureInspection.java | 2 +- .../PyMethodOverridingInspection.java | 2 +- .../quickfix/PyChangeSignatureQuickFix.java | 93 ++++++++++++------- 3 files changed, 60 insertions(+), 37 deletions(-) diff --git a/python/src/com/jetbrains/python/inspections/PyInitNewSignatureInspection.java b/python/src/com/jetbrains/python/inspections/PyInitNewSignatureInspection.java index fdec3cde1bc5..ace34764820b 100644 --- a/python/src/com/jetbrains/python/inspections/PyInitNewSignatureInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyInitNewSignatureInspection.java @@ -70,7 +70,7 @@ public class PyInitNewSignatureInspection extends PyInspection { node.getContainingFile() == cls.getContainingFile()) { registerProblem(node.getParameterList(), PyBundle.message(PyNames.NEW.equals(node.getName()) ? "INSP.new.incompatible.to.init" : "INSP.init.incompatible.to.new"), - new PyChangeSignatureQuickFix(false)); + PyChangeSignatureQuickFix.forMismatchingMethods(node, complementaryMethod)); } } } diff --git a/python/src/com/jetbrains/python/inspections/PyMethodOverridingInspection.java b/python/src/com/jetbrains/python/inspections/PyMethodOverridingInspection.java index 9540013c42e0..cf335472c09f 100644 --- a/python/src/com/jetbrains/python/inspections/PyMethodOverridingInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyMethodOverridingInspection.java @@ -69,7 +69,7 @@ public class PyMethodOverridingInspection extends PyInspection { final String msg = PyBundle.message("INSP.signature.mismatch", cls.getName() + "." + name + "()", baseClass != null ? baseClass.getName() : ""); - registerProblem(function.getParameterList(), msg, new PyChangeSignatureQuickFix(true)); + registerProblem(function.getParameterList(), msg, PyChangeSignatureQuickFix.forMismatchingMethods(function, baseMethod)); } } } diff --git a/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java index 1acbd0d2bcd0..adeb3ff355a5 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java @@ -15,30 +15,46 @@ */ package com.jetbrains.python.inspections.quickfix; -import com.intellij.codeInspection.LocalQuickFix; -import com.intellij.codeInspection.ProblemDescriptor; +import com.google.common.collect.Iterators; +import com.google.common.collect.PeekingIterator; +import com.intellij.codeInspection.LocalQuickFixOnPsiElement; import com.intellij.openapi.project.Project; -import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.openapi.util.Pair; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.util.containers.ContainerUtil; import com.jetbrains.python.PyBundle; -import com.jetbrains.python.PyNames; -import com.jetbrains.python.psi.PyClass; import com.jetbrains.python.psi.PyFunction; -import com.jetbrains.python.psi.search.PySuperMethodsSearch; -import com.jetbrains.python.psi.types.TypeEvalContext; import com.jetbrains.python.refactoring.changeSignature.PyChangeSignatureDialog; import com.jetbrains.python.refactoring.changeSignature.PyMethodDescriptor; import com.jetbrains.python.refactoring.changeSignature.PyParameterInfo; -import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.List; -public class PyChangeSignatureQuickFix implements LocalQuickFix { +public class PyChangeSignatureQuickFix extends LocalQuickFixOnPsiElement { - private final boolean myOverridenMethod; + @NotNull private final List> myExtraParameters; - public PyChangeSignatureQuickFix(boolean overriddenMethod) { - myOverridenMethod = overriddenMethod; + @NotNull + public static PyChangeSignatureQuickFix forMismatchingMethods(@NotNull PyFunction function, @NotNull PyFunction complementary) { + final int paramLength = function.getParameterList().getParameters().length; + final int complementaryParamLength = complementary.getParameterList().getParameters().length; + if (complementaryParamLength > paramLength) { + return new PyChangeSignatureQuickFix(function, + Collections.singletonList(Pair.create(paramLength - 1, + new PyParameterInfo(-1, "**kwargs", "", false)))); + } + return new PyChangeSignatureQuickFix(function, Collections.emptyList()); + } + + + public PyChangeSignatureQuickFix(@NotNull PyFunction function, @NotNull List> extraParameters) { + super(function); + myExtraParameters = ContainerUtil.sorted(extraParameters, Comparator.comparingInt(p -> p.getFirst())); } @NotNull @@ -46,33 +62,40 @@ public class PyChangeSignatureQuickFix implements LocalQuickFix { return PyBundle.message("QFIX.NAME.change.signature"); } - public void applyFix(@NotNull final Project project, @NotNull final ProblemDescriptor descriptor) { - final PyFunction function = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PyFunction.class); - if (function == null) return; - final PyClass cls = function.getContainingClass(); - assert cls != null; - final String functionName = function.getName(); - final String complementaryName = PyNames.NEW.equals(functionName) ? PyNames.INIT : PyNames.NEW; - final TypeEvalContext context = TypeEvalContext.userInitiated(project, descriptor.getEndElement().getContainingFile()); - final PyFunction complementaryMethod = myOverridenMethod ? (PyFunction)PySuperMethodsSearch.search(function, context).findFirst() - : cls.findMethodByName(complementaryName, true, null); + @NotNull + @Override + public String getText() { + return getFamilyName(); + } - assert complementaryMethod != null; - final PyMethodDescriptor methodDescriptor = new PyMethodDescriptor(function) { - @Override - public List getParameters() { - final List parameterInfos = super.getParameters(); - final int paramLength = function.getParameterList().getParameters().length; - final int complementaryParamLength = complementaryMethod.getParameterList().getParameters().length; - if (complementaryParamLength > paramLength) - parameterInfos.add(new PyParameterInfo(-1, "**kwargs", "", false)); - return parameterInfos; - } - }; - final PyChangeSignatureDialog dialog = new PyChangeSignatureDialog(project, methodDescriptor); + @Override + public void invoke(@NotNull Project project, @NotNull PsiFile file, @NotNull PsiElement startElement, @NotNull PsiElement endElement) { + final PyChangeSignatureDialog dialog = new PyChangeSignatureDialog(project, createMethodDescriptor((PyFunction)startElement)); dialog.show(); } + @NotNull + private PyMethodDescriptor createMethodDescriptor(final PyFunction function) { + return new PyMethodDescriptor(function) { + @Override + public List getParameters() { + final List result = new ArrayList<>(); + final List originalParams = super.getParameters(); + final PeekingIterator> extra = Iterators.peekingIterator(myExtraParameters.iterator()); + while (extra.hasNext() && extra.peek().getFirst() < 0) { + result.add(extra.next().getSecond()); + } + for (int i = 0; i < originalParams.size(); i++) { + result.add(originalParams.get(i)); + while (extra.hasNext() && extra.peek().getFirst() == i) { + result.add(extra.next().getSecond()); + } + } + return result; + } + }; + } + @Override public boolean startInWriteAction() { return false; From fcd8262c10d3248efc30cbd21f06264a9114752a Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Thu, 4 May 2017 19:07:26 +0300 Subject: [PATCH 079/136] PY-8174 Initial version of the quickfix --- .../com/jetbrains/python/PyBundle.properties | 1 + .../inspections/PyArgumentListInspection.java | 16 ++++- .../quickfix/PyChangeSignatureQuickFix.java | 64 +++++++++++++++++-- 3 files changed, 75 insertions(+), 6 deletions(-) diff --git a/python/src/com/jetbrains/python/PyBundle.properties b/python/src/com/jetbrains/python/PyBundle.properties index eea21e952154..f8f65fa4930b 100644 --- a/python/src/com/jetbrains/python/PyBundle.properties +++ b/python/src/com/jetbrains/python/PyBundle.properties @@ -150,6 +150,7 @@ QFIX.NAME.add.specifier=Add format specifier character QFIX.NAME.add.exception.base=Add Exception base class QFIX.NAME.change.signature=Change signature +QFIX.change.signature.of=Change signature of {0} QFIX.NAME.remove.argument=Remove argument diff --git a/python/src/com/jetbrains/python/inspections/PyArgumentListInspection.java b/python/src/com/jetbrains/python/inspections/PyArgumentListInspection.java index d3f80dc20f00..9a1e815cf0e5 100644 --- a/python/src/com/jetbrains/python/inspections/PyArgumentListInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyArgumentListInspection.java @@ -28,6 +28,7 @@ import com.intellij.xml.util.XmlStringUtil; import com.jetbrains.python.PyBundle; import com.jetbrains.python.PyNames; import com.jetbrains.python.PyTokenTypes; +import com.jetbrains.python.inspections.quickfix.PyChangeSignatureQuickFix; import com.jetbrains.python.inspections.quickfix.PyRemoveArgumentQuickFix; import com.jetbrains.python.inspections.quickfix.PyRenameArgumentQuickFix; import com.jetbrains.python.psi.*; @@ -207,7 +208,18 @@ public class PyArgumentListInspection extends PyInspection { // if there is only one mapping, we could suggest quick fixes final Set duplicateKeywords = getDuplicateKeywordArguments(node); - for (PyExpression argument : mappings.get(0).getUnmappedArguments()) { + final PyCallExpression.PyArgumentsMapping mapping = mappings.get(0); + if (!mapping.getUnmappedArguments().isEmpty() && mapping.getUnmappedParameters().isEmpty()) { + final PyCallExpression.PyMarkedCallee markedCallee = mapping.getMarkedCallee(); + if (markedCallee != null && markedCallee.getCallable() instanceof PyFunction) { + holder.registerProblem(node, + PyBundle.message("INSP.unexpected.arg(s)"), + PyChangeSignatureQuickFix.forMismatchedCall(mapping)); + } + } + + + for (PyExpression argument : mapping.getUnmappedArguments()) { final List quickFixes = Lists.newArrayList(new PyRemoveArgumentQuickFix()); if (argument instanceof PyKeywordArgument) { if (duplicateKeywords.contains(((PyKeywordArgument)argument).getKeyword())) { @@ -225,7 +237,7 @@ public class PyArgumentListInspection extends PyInspection { holder.registerProblem(node, addPossibleCalleesRepresentationAndWrapInHtml(PyBundle.message("INSP.unexpected.arg(s)"), mappings, context)); } } - + private static void highlightUnfilledParameters(@NotNull PyArgumentList node, @NotNull ProblemsHolder holder, @NotNull List mappings, diff --git a/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java index adeb3ff355a5..d92fdece4a43 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java @@ -20,24 +20,66 @@ import com.google.common.collect.PeekingIterator; import com.intellij.codeInspection.LocalQuickFixOnPsiElement; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.util.containers.ContainerUtil; import com.jetbrains.python.PyBundle; -import com.jetbrains.python.psi.PyFunction; +import com.jetbrains.python.PyNames; +import com.jetbrains.python.psi.*; +import com.jetbrains.python.psi.PyCallExpression.PyArgumentsMapping; +import com.jetbrains.python.psi.types.PyType; +import com.jetbrains.python.psi.types.TypeEvalContext; +import com.jetbrains.python.refactoring.PyRefactoringUtil; import com.jetbrains.python.refactoring.changeSignature.PyChangeSignatureDialog; import com.jetbrains.python.refactoring.changeSignature.PyMethodDescriptor; import com.jetbrains.python.refactoring.changeSignature.PyParameterInfo; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; +import static com.jetbrains.python.psi.PyUtil.as; + public class PyChangeSignatureQuickFix extends LocalQuickFixOnPsiElement { - @NotNull private final List> myExtraParameters; + private final List> myExtraParameters; + + @NotNull + public static PyChangeSignatureQuickFix forMismatchedCall(@NotNull PyArgumentsMapping mapping) { + assert mapping.getMarkedCallee() != null; + final PyFunction function = as(mapping.getMarkedCallee().getCallable(), PyFunction.class); + final PyCallExpression callExpression = mapping.getCallExpression(); + assert function != null; + int positionalParamAnchor = -1; + final PyParameter[] parameters = function.getParameterList().getParameters(); + for (PyParameter parameter : parameters) { + final PyNamedParameter namedParam = parameter.getAsNamed(); + final boolean isVararg = namedParam != null && (namedParam.isPositionalContainer() || namedParam.isKeywordContainer()); + if (parameter instanceof PySingleStarParameter || parameter.hasDefaultValue() || isVararg) { + break; + } + positionalParamAnchor++; + } + final List> newParameters = new ArrayList<>(); + for (PyExpression arg : mapping.getUnmappedArguments()) { + if (arg instanceof PyKeywordArgument) { + newParameters.add(Pair.create(parameters.length - 1, new PyParameterInfo(-1, ((PyKeywordArgument)arg).getKeyword(), "", false))); + } + else { + final TypeEvalContext context = TypeEvalContext.userInitiated(function.getProject(), callExpression.getContainingFile()); + final PyType type = context.getType(arg); + final String typeName = type != null && type.getName() != null ? type.getName() : PyNames.OBJECT; + final String paramName = PyRefactoringUtil.selectUniqueNameFromType(typeName, function.getStatementList()); + newParameters.add(Pair.create(positionalParamAnchor, new PyParameterInfo(-1, paramName, "", false))); + } + } + return new PyChangeSignatureQuickFix(function, newParameters); + } + @NotNull public static PyChangeSignatureQuickFix forMismatchingMethods(@NotNull PyFunction function, @NotNull PyFunction complementary) { @@ -65,12 +107,26 @@ public class PyChangeSignatureQuickFix extends LocalQuickFixOnPsiElement { @NotNull @Override public String getText() { - return getFamilyName(); + final PyFunction function = getFunction(); + if (function == null) { + return getFamilyName(); + } + final String params = StringUtil.join(createMethodDescriptor(function).getParameters(), info -> { + return info.getOldIndex() == -1 ? "" + info.getName() + "" : info.getName(); + }, ", "); + return "" + + PyBundle.message("QFIX.change.signature.of", StringUtil.notNullize(function.getName()) + "(" + params + ")") + + ""; + } + + @Nullable + private PyFunction getFunction() { + return (PyFunction)getStartElement(); } @Override public void invoke(@NotNull Project project, @NotNull PsiFile file, @NotNull PsiElement startElement, @NotNull PsiElement endElement) { - final PyChangeSignatureDialog dialog = new PyChangeSignatureDialog(project, createMethodDescriptor((PyFunction)startElement)); + final PyChangeSignatureDialog dialog = new PyChangeSignatureDialog(project, createMethodDescriptor(getFunction())); dialog.show(); } From 7837c5a19d30320239260d048a78dba4ab6a62d4 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Fri, 5 May 2017 14:54:53 +0300 Subject: [PATCH 080/136] PY-8174 Check that the file containing the function to be changed is writable --- .../inspections/quickfix/PyChangeSignatureQuickFix.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java index d92fdece4a43..244226f43322 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java @@ -52,8 +52,8 @@ public class PyChangeSignatureQuickFix extends LocalQuickFixOnPsiElement { public static PyChangeSignatureQuickFix forMismatchedCall(@NotNull PyArgumentsMapping mapping) { assert mapping.getMarkedCallee() != null; final PyFunction function = as(mapping.getMarkedCallee().getCallable(), PyFunction.class); - final PyCallExpression callExpression = mapping.getCallExpression(); assert function != null; + final PyCallExpression callExpression = mapping.getCallExpression(); int positionalParamAnchor = -1; final PyParameter[] parameters = function.getParameterList().getParameters(); for (PyParameter parameter : parameters) { @@ -152,6 +152,12 @@ public class PyChangeSignatureQuickFix extends LocalQuickFixOnPsiElement { }; } + @Nullable + @Override + public PsiElement getElementToMakeWritable(@NotNull PsiFile currentFile) { + return getFunction(); + } + @Override public boolean startInWriteAction() { return false; From b05653ccc85f218cb7e072c2c96da3d0bcdfcab0 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Fri, 5 May 2017 17:12:36 +0300 Subject: [PATCH 081/136] PY-8174 Don't show the quickfix where Change Signature is not possible Mostly, because we strictly prohibit changing signatures of functions in excluded directories and in library roots outside of the project root even if they can be made writable as in case with a common virtual env directory in user's home. --- .../python/inspections/PyArgumentListInspection.java | 12 ++++++++---- .../changeSignature/PyChangeSignatureHandler.java | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/python/src/com/jetbrains/python/inspections/PyArgumentListInspection.java b/python/src/com/jetbrains/python/inspections/PyArgumentListInspection.java index 9a1e815cf0e5..1d5c432431a3 100644 --- a/python/src/com/jetbrains/python/inspections/PyArgumentListInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyArgumentListInspection.java @@ -20,6 +20,7 @@ import com.intellij.codeInspection.LocalInspectionToolSession; import com.intellij.codeInspection.LocalQuickFix; import com.intellij.codeInspection.ProblemsHolder; import com.intellij.lang.ASTNode; +import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElementVisitor; import com.intellij.psi.PsiPolyVariantReference; import com.intellij.psi.ResolveResult; @@ -37,6 +38,7 @@ import com.jetbrains.python.psi.types.PyABCUtil; import com.jetbrains.python.psi.types.PyType; import com.jetbrains.python.psi.types.PyTypeChecker; import com.jetbrains.python.psi.types.TypeEvalContext; +import com.jetbrains.python.refactoring.changeSignature.PyChangeSignatureHandler; import one.util.streamex.StreamEx; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; @@ -211,10 +213,12 @@ public class PyArgumentListInspection extends PyInspection { final PyCallExpression.PyArgumentsMapping mapping = mappings.get(0); if (!mapping.getUnmappedArguments().isEmpty() && mapping.getUnmappedParameters().isEmpty()) { final PyCallExpression.PyMarkedCallee markedCallee = mapping.getMarkedCallee(); - if (markedCallee != null && markedCallee.getCallable() instanceof PyFunction) { - holder.registerProblem(node, - PyBundle.message("INSP.unexpected.arg(s)"), - PyChangeSignatureQuickFix.forMismatchedCall(mapping)); + if (markedCallee != null) { + final PyCallable callable = markedCallee.getCallable(); + final Project project = node.getProject(); + if (callable instanceof PyFunction && !PyChangeSignatureHandler.isNotUnderSourceRoot(project, callable.getContainingFile())) { + holder.registerProblem(node, PyBundle.message("INSP.unexpected.arg(s)"), PyChangeSignatureQuickFix.forMismatchedCall(mapping)); + } } } diff --git a/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureHandler.java b/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureHandler.java index 6b67be8045df..3fac20e8f0b4 100644 --- a/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureHandler.java +++ b/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureHandler.java @@ -139,7 +139,7 @@ public class PyChangeSignatureHandler implements ChangeSignatureHandler { CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, "refactoring.renameRefactorings"); } - private static boolean isNotUnderSourceRoot(@NotNull final Project project, @Nullable final PsiFile psiFile) { + public static boolean isNotUnderSourceRoot(@NotNull final Project project, @Nullable final PsiFile psiFile) { if (psiFile == null) { return true; } From 58d6406e07bcc7825bbae02f48e3a18ade67bfcf Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Fri, 5 May 2017 17:55:01 +0300 Subject: [PATCH 082/136] PY-24149 Focus on the first function parameter by default in Change Signature dialog --- .../refactoring/changeSignature/PyChangeSignatureDialog.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureDialog.java b/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureDialog.java index 0d3857a0f85d..0bd46f68787a 100644 --- a/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureDialog.java +++ b/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureDialog.java @@ -229,11 +229,6 @@ public class PyChangeSignatureDialog extends ChangeSignatureDialogBase Date: Fri, 5 May 2017 18:02:28 +0300 Subject: [PATCH 083/136] PY-8174 Focus on the first new parameter in the dialog --- .../inspections/quickfix/PyChangeSignatureQuickFix.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java index 244226f43322..117de83ec401 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java @@ -34,6 +34,7 @@ import com.jetbrains.python.refactoring.PyRefactoringUtil; import com.jetbrains.python.refactoring.changeSignature.PyChangeSignatureDialog; import com.jetbrains.python.refactoring.changeSignature.PyMethodDescriptor; import com.jetbrains.python.refactoring.changeSignature.PyParameterInfo; +import one.util.streamex.StreamEx; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -126,7 +127,13 @@ public class PyChangeSignatureQuickFix extends LocalQuickFixOnPsiElement { @Override public void invoke(@NotNull Project project, @NotNull PsiFile file, @NotNull PsiElement startElement, @NotNull PsiElement endElement) { - final PyChangeSignatureDialog dialog = new PyChangeSignatureDialog(project, createMethodDescriptor(getFunction())); + final PyChangeSignatureDialog dialog = new PyChangeSignatureDialog(project, createMethodDescriptor(getFunction())) { + // Similar to JavaChangeSignatureDialog.createAndPreselectNew() + @Override + protected int getSelectedIdx() { + return (int)StreamEx.of(getParameters()).indexOf(info -> info.getOldIndex() < 0).orElse(super.getSelectedIdx()); + } + }; dialog.show(); } From 61797297a0c28f64bf51d010a8efef3793d5a98e Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Thu, 11 May 2017 16:52:07 +0300 Subject: [PATCH 084/136] PY-24150 Fix scaling by using EditorTextFieldJBTableRowRendered in the dialog Also provided straightforward implementation ParametersListTable and thus got rid of deprecated methods. --- .../PyChangeSignatureDialog.java | 274 +++++++++--------- 1 file changed, 145 insertions(+), 129 deletions(-) diff --git a/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureDialog.java b/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureDialog.java index 0bd46f68787a..99613726848f 100644 --- a/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureDialog.java +++ b/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureDialog.java @@ -42,9 +42,10 @@ import com.intellij.util.Consumer; import com.intellij.util.IJSwingUtilities; import com.intellij.util.containers.HashSet; import com.intellij.util.ui.UIUtil; -import com.intellij.util.ui.table.JBListTable; +import com.intellij.util.ui.table.EditorTextFieldJBTableRowRenderer; import com.intellij.util.ui.table.JBTableRow; import com.intellij.util.ui.table.JBTableRowEditor; +import com.intellij.util.ui.table.JBTableRowRenderer; import com.jetbrains.python.PyBundle; import com.jetbrains.python.PyNames; import com.jetbrains.python.PythonFileType; @@ -54,6 +55,7 @@ import com.jetbrains.python.psi.PyFunction; import com.jetbrains.python.psi.PyParameterList; import com.jetbrains.python.refactoring.introduce.IntroduceValidator; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; @@ -255,146 +257,160 @@ public class PyChangeSignatureDialog extends ChangeSignatureDialogBase item, boolean selected, final boolean focused) { - String text = item.parameter.getName(); - final String defaultCallValue = item.defaultValueCodeFragment.getText(); - PyParameterTableModelItem pyItem = (PyParameterTableModelItem)item; - final String defaultValue = pyItem.isDefaultInSignature()? pyItem.defaultValueCodeFragment.getText() : ""; - - if (StringUtil.isNotEmpty(defaultValue)) { - text += " = " + defaultValue; - } - - String tail = ""; - if (StringUtil.isNotEmpty(defaultCallValue)) { - tail += " default value = " + defaultCallValue; - } - if (!StringUtil.isEmpty(tail)) { - text += " //" + tail; - } - return JBListTable.createEditorTextFieldPresentation(getProject(), getFileType(), " " + text, selected, focused); - } - - @Override - protected boolean isListTableViewSupported() { - return true; - } - - @Override - protected JBTableRowEditor getTableEditor(final JTable t, final ParameterTableModelItemBase item) { - return new JBTableRowEditor() { - private EditorTextField myNameEditor; - private EditorTextField myDefaultValueEditor; - private JCheckBox myDefaultInSignature; - + protected ParametersListTable createParametersListTable() { + return new ParametersListTable() { @Override - public void prepareEditor(JTable table, int row) { - setLayout(new GridLayout(1, 3)); - final JPanel parameterPanel = createParameterPanel(); - add(parameterPanel); - final JPanel defaultValuePanel = createDefaultValuePanel(); - add(defaultValuePanel); - final JPanel defaultValueCheckBox = createDefaultValueCheckBox(); - add(defaultValueCheckBox); - - final String nameText = myNameEditor.getText(); - myDefaultValueEditor.setEnabled(!nameText.startsWith("*") - && !PyNames.CANONICAL_SELF.equals(nameText)); - myDefaultInSignature.setEnabled(!nameText.startsWith("*") - && !PyNames.CANONICAL_SELF.equals(nameText)); - } - - private JPanel createDefaultValueCheckBox() { - final JPanel defaultValuePanel = new JPanel(new VerticalFlowLayout(VerticalFlowLayout.TOP, 4, 2, true, false)); - - final JBLabel inSignatureLabel = new JBLabel(PyBundle.message("refactoring.change.signature.dialog.default.value.checkbox"), - UIUtil.ComponentStyle.SMALL); - IJSwingUtilities.adjustComponentsOnMac(inSignatureLabel, - myDefaultInSignature); - defaultValuePanel.add(inSignatureLabel, BorderLayout.WEST); - myDefaultInSignature = new JCheckBox(); - myDefaultInSignature.setSelected( - ((PyParameterTableModelItem)item).isDefaultInSignature()); - myDefaultInSignature.addItemListener(new ItemListener() { + protected JBTableRowRenderer getRowRenderer(int row) { + return new EditorTextFieldJBTableRowRenderer(getProject(), getFileType(), getDisposable()) { @Override - public void itemStateChanged(ItemEvent event) { - ((PyParameterTableModelItem)item) - .setDefaultInSignature(myDefaultInSignature.isSelected()); - } - }); - myDefaultInSignature.addChangeListener(mySignatureUpdater); - myDefaultInSignature.setEnabled(item.parameter.getOldIndex() == -1); - defaultValuePanel.add(myDefaultInSignature, BorderLayout.EAST); - return defaultValuePanel; - } + protected String getText(JTable table, int row) { + final PyParameterTableModelItem pyItem = getRowItem(row); + String text = pyItem.parameter.getName(); + final String defaultCallValue = pyItem.defaultValueCodeFragment.getText(); + final String defaultValue = pyItem.isDefaultInSignature() ? pyItem.defaultValueCodeFragment.getText() : ""; - private JPanel createDefaultValuePanel() { - final JPanel defaultValuePanel = new JPanel(new VerticalFlowLayout(VerticalFlowLayout.TOP, 4, 2, true, false)); - final Document doc = PsiDocumentManager.getInstance(getProject()).getDocument(item.defaultValueCodeFragment); - myDefaultValueEditor = new EditorTextField(doc, getProject(), getFileType()); - final JBLabel defaultValueLabel = new JBLabel(PyBundle.message("refactoring.change.signature.dialog.default.value.label"), - UIUtil.ComponentStyle.SMALL); - IJSwingUtilities.adjustComponentsOnMac(defaultValueLabel, myDefaultValueEditor); - defaultValuePanel.add(defaultValueLabel); - defaultValuePanel.add(myDefaultValueEditor); - myDefaultValueEditor.setPreferredWidth(t.getWidth() / 2); - myDefaultValueEditor.addDocumentListener(mySignatureUpdater); - return defaultValuePanel; - } - - private JPanel createParameterPanel() { - final JPanel namePanel = new JPanel(new VerticalFlowLayout(VerticalFlowLayout.TOP, 4, 2, true, false)); - myNameEditor = new EditorTextField(item.parameter.getName(), getProject(), getFileType()); - final JBLabel nameLabel = new JBLabel(PyBundle.message("refactoring.change.signature.dialog.name.label"), - UIUtil.ComponentStyle.SMALL); - IJSwingUtilities.adjustComponentsOnMac(nameLabel, myNameEditor); - namePanel.add(nameLabel); - namePanel.add(myNameEditor); - myNameEditor.setPreferredWidth(t.getWidth() / 2); - myNameEditor.addDocumentListener(new DocumentListener() { - @Override - public void documentChanged(DocumentEvent event) { - fireDocumentChanged(event, 0); - myDefaultValueEditor.setEnabled(!myNameEditor.getText().startsWith("*")); - myDefaultInSignature.setEnabled(!myNameEditor.getText().startsWith("*")); - } - }); - - myNameEditor.addDocumentListener(mySignatureUpdater); - return namePanel; - } - - @Override - public JBTableRow getValue() { - return new JBTableRow() { - @Override - public Object getValueAt(int column) { - switch (column) { - case 0: return myNameEditor.getText().trim(); - case 1: return new Pair<>(item.defaultValueCodeFragment, - ((PyParameterTableModelItem)item).isDefaultInSignature()); + if (StringUtil.isNotEmpty(defaultValue)) { + text += " = " + defaultValue; } - return null; + + String tail = ""; + if (StringUtil.isNotEmpty(defaultCallValue)) { + tail += " default value = " + defaultCallValue; + } + if (!StringUtil.isEmpty(tail)) { + text += " //" + tail; + } + return text; + } + }; + } + + @NotNull + @Override + protected JBTableRowEditor getRowEditor(ParameterTableModelItemBase item) { + return new JBTableRowEditor() { + private EditorTextField myNameEditor; + private EditorTextField myDefaultValueEditor; + private JCheckBox myDefaultInSignature; + + @Override + public void prepareEditor(JTable table, int row) { + setLayout(new GridLayout(1, 3)); + final JPanel parameterPanel = createParameterPanel(); + add(parameterPanel); + final JPanel defaultValuePanel = createDefaultValuePanel(); + add(defaultValuePanel); + final JPanel defaultValueCheckBox = createDefaultValueCheckBox(); + add(defaultValueCheckBox); + + final String nameText = myNameEditor.getText(); + myDefaultValueEditor.setEnabled(!nameText.startsWith("*") && !PyNames.CANONICAL_SELF.equals(nameText)); + myDefaultInSignature.setEnabled(!nameText.startsWith("*") && !PyNames.CANONICAL_SELF.equals(nameText)); + } + + private JPanel createDefaultValueCheckBox() { + final JPanel defaultValuePanel = new JPanel(new VerticalFlowLayout(VerticalFlowLayout.TOP, 4, 2, true, false)); + + final JBLabel inSignatureLabel = new JBLabel(PyBundle.message("refactoring.change.signature.dialog.default.value.checkbox"), + UIUtil.ComponentStyle.SMALL); + IJSwingUtilities.adjustComponentsOnMac(inSignatureLabel, + myDefaultInSignature); + defaultValuePanel.add(inSignatureLabel, BorderLayout.WEST); + myDefaultInSignature = new JCheckBox(); + myDefaultInSignature.setSelected(((PyParameterTableModelItem)item).isDefaultInSignature()); + myDefaultInSignature.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent event) { + ((PyParameterTableModelItem)item).setDefaultInSignature(myDefaultInSignature.isSelected()); + } + }); + myDefaultInSignature.addChangeListener(mySignatureUpdater); + myDefaultInSignature.setEnabled(item.parameter.getOldIndex() == -1); + defaultValuePanel.add(myDefaultInSignature, BorderLayout.EAST); + return defaultValuePanel; + } + + private JPanel createDefaultValuePanel() { + final JPanel defaultValuePanel = new JPanel(new VerticalFlowLayout(VerticalFlowLayout.TOP, 4, 2, true, false)); + final Document doc = PsiDocumentManager.getInstance(getProject()).getDocument(item.defaultValueCodeFragment); + myDefaultValueEditor = new EditorTextField(doc, getProject(), getFileType()); + final JBLabel defaultValueLabel = new JBLabel(PyBundle.message("refactoring.change.signature.dialog.default.value.label"), + UIUtil.ComponentStyle.SMALL); + IJSwingUtilities.adjustComponentsOnMac(defaultValueLabel, myDefaultValueEditor); + defaultValuePanel.add(defaultValueLabel); + defaultValuePanel.add(myDefaultValueEditor); + myDefaultValueEditor.setPreferredWidth(getTable().getWidth() / 2); + myDefaultValueEditor.addDocumentListener(mySignatureUpdater); + return defaultValuePanel; + } + + private JPanel createParameterPanel() { + final JPanel namePanel = new JPanel(new VerticalFlowLayout(VerticalFlowLayout.TOP, 4, 2, true, false)); + myNameEditor = new EditorTextField(item.parameter.getName(), getProject(), getFileType()); + final JBLabel nameLabel = new JBLabel(PyBundle.message("refactoring.change.signature.dialog.name.label"), + UIUtil.ComponentStyle.SMALL); + IJSwingUtilities.adjustComponentsOnMac(nameLabel, myNameEditor); + namePanel.add(nameLabel); + namePanel.add(myNameEditor); + myNameEditor.setPreferredWidth(getTable().getWidth() / 2); + myNameEditor.addDocumentListener(new DocumentListener() { + @Override + public void documentChanged(DocumentEvent event) { + fireDocumentChanged(event, 0); + myDefaultValueEditor.setEnabled(!myNameEditor.getText().startsWith("*")); + myDefaultInSignature.setEnabled(!myNameEditor.getText().startsWith("*")); + } + }); + + myNameEditor.addDocumentListener(mySignatureUpdater); + return namePanel; + } + + @Override + public JBTableRow getValue() { + return new JBTableRow() { + @Override + public Object getValueAt(int column) { + switch (column) { + case 0: + return myNameEditor.getText().trim(); + case 1: + return new Pair<>(item.defaultValueCodeFragment, + ((PyParameterTableModelItem)item).isDefaultInSignature()); + } + return null; + } + }; + } + + @Override + public JComponent getPreferredFocusedComponent() { + return myNameEditor.getFocusTarget(); + } + + @Override + public JComponent[] getFocusableComponents() { + final List focusable = new ArrayList<>(); + focusable.add(myNameEditor.getFocusTarget()); + if (myDefaultValueEditor != null) { + focusable.add(myDefaultValueEditor.getFocusTarget()); + } + return focusable.toArray(new JComponent[focusable.size()]); } }; } @Override - public JComponent getPreferredFocusedComponent() { - return myNameEditor.getFocusTarget(); - } - - @Override - public JComponent[] getFocusableComponents() { - final List focusable = new ArrayList<>(); - focusable.add(myNameEditor.getFocusTarget()); - if (myDefaultValueEditor != null) { - focusable.add(myDefaultValueEditor.getFocusTarget()); - } - return focusable.toArray(new JComponent[focusable.size()]); + protected boolean isRowEmpty(int row) { + return false; } }; } + + @Override + protected boolean isListTableViewSupported() { + return true; + } @Override protected boolean mayPropagateParameters() { From 9af15aabf161740114a7eab5ac588fb3592e2274 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Thu, 11 May 2017 17:19:42 +0300 Subject: [PATCH 085/136] PY-19010 Properly focus on the checkbox "Use default value in signature" on Tab It did work for Tab already, yet not for Shift+Tab when the focus was switching between "Name" and "Default value" fields. --- .../refactoring/changeSignature/PyChangeSignatureDialog.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureDialog.java b/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureDialog.java index 99613726848f..623d5e7f74d4 100644 --- a/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureDialog.java +++ b/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureDialog.java @@ -395,6 +395,9 @@ public class PyChangeSignatureDialog extends ChangeSignatureDialogBase Date: Thu, 11 May 2017 17:37:24 +0300 Subject: [PATCH 086/136] PY-8174 Fix several code style issues and warnings in the dialog Method doValidate() is already indented to be called of EDT, so additional invokeLater() is not necessary. --- .../PyChangeSignatureDialog.java | 59 ++++++++----------- 1 file changed, 26 insertions(+), 33 deletions(-) diff --git a/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureDialog.java b/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureDialog.java index 623d5e7f74d4..aa4984030914 100644 --- a/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureDialog.java +++ b/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureDialog.java @@ -38,6 +38,7 @@ import com.intellij.refactoring.ui.VisibilityPanelBase; import com.intellij.ui.EditorTextField; import com.intellij.ui.components.JBLabel; import com.intellij.ui.treeStructure.Tree; +import com.intellij.util.ArrayUtil; import com.intellij.util.Consumer; import com.intellij.util.IJSwingUtilities; import com.intellij.util.containers.HashSet; @@ -54,7 +55,6 @@ import com.jetbrains.python.psi.LanguageLevel; import com.jetbrains.python.psi.PyFunction; import com.jetbrains.python.psi.PyParameterList; import com.jetbrains.python.refactoring.introduce.IntroduceValidator; -import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -70,10 +70,10 @@ import java.util.Set; * User : ktisha */ -public class PyChangeSignatureDialog extends ChangeSignatureDialogBase { +public class PyChangeSignatureDialog extends + ChangeSignatureDialogBase { - public PyChangeSignatureDialog(Project project, - PyMethodDescriptor method) { + public PyChangeSignatureDialog(Project project, PyMethodDescriptor method) { super(project, method, false, method.getMethod().getContext()); } @@ -109,9 +109,7 @@ public class PyChangeSignatureDialog extends ChangeSignatureDialogBase parameters = myParametersTableModel.getItems(); - Set parameterNames = new HashSet<>(); + final Set parameterNames = new HashSet<>(); boolean hadPositionalContainer = false; boolean hadKeywordContainer = false; boolean hadDefaultValue = false; boolean hadSingleStar = false; boolean hadParamsAfterSingleStar = false; - LanguageLevel languageLevel = LanguageLevel.forElement(myMethod.getMethod()); + final LanguageLevel languageLevel = LanguageLevel.forElement(myMethod.getMethod()); - int parametersLength = parameters.size(); + final int parametersLength = parameters.size(); - for (int index = 0; index != parametersLength; ++index) { - PyParameterTableModelItem info = parameters.get(index); + for (int index = 0; index < parametersLength; index++) { + final PyParameterTableModelItem info = parameters.get(index); final PyParameterInfo parameter = info.parameter; final String name = parameter.getName(); final String nameWithoutStars = StringUtil.trimLeading(name, '*').trim(); @@ -150,7 +148,7 @@ public class PyChangeSignatureDialog extends ChangeSignatureDialogBase { - getRefactorAction().setEnabled(message == null); - getPreviewAction().setEnabled(message == null); - }); + getRefactorAction().setEnabled(message == null); + getPreviewAction().setEnabled(message == null); if (message != null) return new ValidationInfo(message); return super.doValidate(); } @Override protected String calculateSignature() { - @NonNls StringBuilder builder = new StringBuilder(); + final StringBuilder builder = new StringBuilder(); builder.append(getMethodName()); builder.append("("); final List parameters = myParametersTableModel.getItems(); - for (int i = 0; i != parameters.size(); ++i) { - PyParameterTableModelItem parameterInfo = parameters.get(i); + for (int i = 0; i < parameters.size(); i++) { + final PyParameterTableModelItem parameterInfo = parameters.get(i); builder.append(parameterInfo.parameter.getName()); final String defaultValue = parameterInfo.defaultValueCodeFragment.getText(); if (!defaultValue.isEmpty() && parameterInfo.isDefaultInSignature()) { - builder.append(" = " + defaultValue); + builder.append(" = ").append(defaultValue); } - if (i != parameters.size()-1) + if (i != parameters.size() - 1) { builder.append(", "); + } } builder.append(")"); return builder.toString(); @@ -253,7 +250,7 @@ public class PyChangeSignatureDialog extends ChangeSignatureDialogBase createVisibilityControl() { - return new ComboBoxVisibilityPanel<>(new String[0]); + return new ComboBoxVisibilityPanel<>(ArrayUtil.EMPTY_STRING_ARRAY); } @Override @@ -265,22 +262,18 @@ public class PyChangeSignatureDialog extends ChangeSignatureDialogBase Date: Tue, 16 May 2017 15:09:34 +0300 Subject: [PATCH 087/136] PY-8174 Use provided arguments as default values for new parameters --- .../com/jetbrains/python/PyTokenTypes.java | 4 ++ .../quickfix/PyChangeSignatureQuickFix.java | 59 ++++++++++++------- .../python/refactoring/PyRefactoringUtil.java | 14 +++++ .../PyChangeSignatureDialog.java | 2 +- .../PyChangeSignatureProcessor.java | 8 +-- 5 files changed, 62 insertions(+), 25 deletions(-) diff --git a/python/psi-api/src/com/jetbrains/python/PyTokenTypes.java b/python/psi-api/src/com/jetbrains/python/PyTokenTypes.java index 70f636351f3a..f35ce48bee2d 100644 --- a/python/psi-api/src/com/jetbrains/python/PyTokenTypes.java +++ b/python/psi-api/src/com/jetbrains/python/PyTokenTypes.java @@ -165,6 +165,10 @@ public class PyTokenTypes { public static final TokenSet WHITESPACE_OR_LINEBREAK = TokenSet.create(SPACE, TAB, FORMFEED, LINE_BREAK); public static final TokenSet OPEN_BRACES = TokenSet.create(LBRACKET, LBRACE, LPAR); public static final TokenSet CLOSE_BRACES = TokenSet.create(RBRACKET, RBRACE, RPAR); + + public static final TokenSet NUMERIC_LITERALS = TokenSet.create(FLOAT_LITERAL, INTEGER_LITERAL, IMAGINARY_LITERAL); + public static final TokenSet BOOL_LITERALS = TokenSet.create(TRUE_KEYWORD, FALSE_KEYWORD); + public static final TokenSet SCALAR_LITERALS = TokenSet.orSet(STRING_NODES, BOOL_LITERALS, NUMERIC_LITERALS, TokenSet.create(NONE_KEYWORD)); public static final TokenSet AUG_ASSIGN_OPERATIONS = TokenSet.create(PLUSEQ, MINUSEQ, MULTEQ, ATEQ, DIVEQ, PERCEQ, EXPEQ, GTGTEQ, LTLTEQ, ANDEQ, OREQ, XOREQ, FLOORDIVEQ); diff --git a/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java index 117de83ec401..c4ffdf79bd25 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java @@ -18,6 +18,7 @@ package com.jetbrains.python.inspections.quickfix; import com.google.common.collect.Iterators; import com.google.common.collect.PeekingIterator; import com.intellij.codeInspection.LocalQuickFixOnPsiElement; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; @@ -68,7 +69,16 @@ public class PyChangeSignatureQuickFix extends LocalQuickFixOnPsiElement { final List> newParameters = new ArrayList<>(); for (PyExpression arg : mapping.getUnmappedArguments()) { if (arg instanceof PyKeywordArgument) { - newParameters.add(Pair.create(parameters.length - 1, new PyParameterInfo(-1, ((PyKeywordArgument)arg).getKeyword(), "", false))); + final String defaultValueText; + final PyExpression value = ((PyKeywordArgument)arg).getValueExpression(); + if (value != null && PyRefactoringUtil.isSimpleExpression(value) && !value.textContains('\n')) { + defaultValueText = value.getText(); + } + else { + defaultValueText = ApplicationManager.getApplication().isUnitTestMode() ? "None" : ""; + } + newParameters.add(Pair.create(parameters.length - 1, + new PyParameterInfo(-1, ((PyKeywordArgument)arg).getKeyword(), defaultValueText, true))); } else { final TypeEvalContext context = TypeEvalContext.userInitiated(function.getProject(), callExpression.getContainingFile()); @@ -77,10 +87,10 @@ public class PyChangeSignatureQuickFix extends LocalQuickFixOnPsiElement { final String paramName = PyRefactoringUtil.selectUniqueNameFromType(typeName, function.getStatementList()); newParameters.add(Pair.create(positionalParamAnchor, new PyParameterInfo(-1, paramName, "", false))); } - } + } return new PyChangeSignatureQuickFix(function, newParameters); } - + @NotNull public static PyChangeSignatureQuickFix forMismatchingMethods(@NotNull PyFunction function, @NotNull PyFunction complementary) { @@ -117,7 +127,7 @@ public class PyChangeSignatureQuickFix extends LocalQuickFixOnPsiElement { }, ", "); return "" + PyBundle.message("QFIX.change.signature.of", StringUtil.notNullize(function.getName()) + "(" + params + ")") + - ""; + ""; } @Nullable @@ -127,36 +137,45 @@ public class PyChangeSignatureQuickFix extends LocalQuickFixOnPsiElement { @Override public void invoke(@NotNull Project project, @NotNull PsiFile file, @NotNull PsiElement startElement, @NotNull PsiElement endElement) { - final PyChangeSignatureDialog dialog = new PyChangeSignatureDialog(project, createMethodDescriptor(getFunction())) { + final PyFunction function = getFunction(); + final PyMethodDescriptor descriptor = createMethodDescriptor(function); + + final PyChangeSignatureDialog dialog = new PyChangeSignatureDialog(project, descriptor) { // Similar to JavaChangeSignatureDialog.createAndPreselectNew() @Override protected int getSelectedIdx() { return (int)StreamEx.of(getParameters()).indexOf(info -> info.getOldIndex() < 0).orElse(super.getSelectedIdx()); } }; - dialog.show(); + + if (ApplicationManager.getApplication().isUnitTestMode()) { + dialog.createRefactoringProcessor().run(); + } + else { + dialog.show(); + } } @NotNull private PyMethodDescriptor createMethodDescriptor(final PyFunction function) { return new PyMethodDescriptor(function) { - @Override - public List getParameters() { - final List result = new ArrayList<>(); - final List originalParams = super.getParameters(); - final PeekingIterator> extra = Iterators.peekingIterator(myExtraParameters.iterator()); - while (extra.hasNext() && extra.peek().getFirst() < 0) { + @Override + public List getParameters() { + final List result = new ArrayList<>(); + final List originalParams = super.getParameters(); + final PeekingIterator> extra = Iterators.peekingIterator(myExtraParameters.iterator()); + while (extra.hasNext() && extra.peek().getFirst() < 0) { + result.add(extra.next().getSecond()); + } + for (int i = 0; i < originalParams.size(); i++) { + result.add(originalParams.get(i)); + while (extra.hasNext() && extra.peek().getFirst() == i) { result.add(extra.next().getSecond()); } - for (int i = 0; i < originalParams.size(); i++) { - result.add(originalParams.get(i)); - while (extra.hasNext() && extra.peek().getFirst() == i) { - result.add(extra.next().getSecond()); - } - } - return result; } - }; + return result; + } + }; } @Nullable diff --git a/python/src/com/jetbrains/python/refactoring/PyRefactoringUtil.java b/python/src/com/jetbrains/python/refactoring/PyRefactoringUtil.java index 4448804a780c..14592a8abbe1 100644 --- a/python/src/com/jetbrains/python/refactoring/PyRefactoringUtil.java +++ b/python/src/com/jetbrains/python/refactoring/PyRefactoringUtil.java @@ -17,6 +17,7 @@ package com.jetbrains.python.refactoring; import com.intellij.codeInsight.PsiEquivalenceUtil; import com.intellij.find.findUsages.FindUsagesHandler; +import com.intellij.lang.ASTNode; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Pair; @@ -28,6 +29,7 @@ import com.intellij.usageView.UsageInfo; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashSet; import com.jetbrains.python.PyNames; +import com.jetbrains.python.PyTokenTypes; import com.jetbrains.python.findUsages.PyFindUsagesHandlerFactory; import com.jetbrains.python.psi.*; import com.jetbrains.python.refactoring.introduce.IntroduceValidator; @@ -397,4 +399,16 @@ public class PyRefactoringUtil { public static boolean isValidNewName(@NotNull String name, @NotNull PsiElement scopeAnchor) { return !(IntroduceValidator.isDefinedInScope(name, scopeAnchor) || PyNames.isReserved(name)); } + + public static boolean isSimpleExpression(@NotNull PyExpression value) { + if (value instanceof PyLiteralExpression) { + final ASTNode node = value.getNode(); + // Check that string literal doesn't contain multiple glued nodes + return node.getChildren(null).length == 1 && PyTokenTypes.SCALAR_LITERALS.contains(node.getFirstChildNode().getElementType()); + } + else if (value instanceof PyReferenceExpression) { + return PyUtil.isPy2ReservedWord((PyReferenceExpression)value); + } + return false; + } } diff --git a/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureDialog.java b/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureDialog.java index aa4984030914..09ba82f0c219 100644 --- a/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureDialog.java +++ b/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureDialog.java @@ -89,7 +89,7 @@ public class PyChangeSignatureDialog extends } @Override - protected BaseRefactoringProcessor createRefactoringProcessor() { + public BaseRefactoringProcessor createRefactoringProcessor() { final List parameters = getParameters(); return new PyChangeSignatureProcessor(myProject, myMethod.getMethod(), getMethodName(), parameters.toArray(new PyParameterInfo[parameters.size()])); diff --git a/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureProcessor.java b/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureProcessor.java index 7dc010b07233..9b19d26d9577 100644 --- a/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureProcessor.java +++ b/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureProcessor.java @@ -28,10 +28,10 @@ import org.jetbrains.annotations.NotNull; public class PyChangeSignatureProcessor extends ChangeSignatureProcessorBase { - protected PyChangeSignatureProcessor(Project project, - PyFunction method, - String newName, - PyParameterInfo[] parameterInfo) { + public PyChangeSignatureProcessor(Project project, + PyFunction method, + String newName, + PyParameterInfo[] parameterInfo) { super(project, generateChangeInfo(method, newName, parameterInfo)); } From 05e9418a47eab0c45415c32b82afe0aff2e69a75 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Tue, 30 May 2017 15:19:41 +0300 Subject: [PATCH 088/136] PY-8174 Include the new warning, the quickfix is bound to, in the test data --- .../PyArgumentListInspection/badarglist.py | 12 ++++++------ .../PyArgumentListInspection/decorators.py | 4 ++-- .../PyArgumentListInspection/kwargsMapToNothing.py | 4 ++-- .../inspections/PyArgumentListInspection/py1268.py | 14 +++++++------- .../inspections/PyArgumentListInspection/py3k.py | 2 +- .../PyArgumentListInspection/tupleVsLiteralList.py | 2 +- .../PyRemoveArgumentQuickFixTest/duplicate.py | 2 +- .../PyRemoveArgumentQuickFixTest/unexpected.py | 2 +- 8 files changed, 21 insertions(+), 21 deletions(-) diff --git a/python/testData/inspections/PyArgumentListInspection/badarglist.py b/python/testData/inspections/PyArgumentListInspection/badarglist.py index 8ac2755a27aa..08bf20f42b00 100644 --- a/python/testData/inspections/PyArgumentListInspection/badarglist.py +++ b/python/testData/inspections/PyArgumentListInspection/badarglist.py @@ -18,8 +18,8 @@ def f1(): pass f1() -f1(1) -f1(a = 1) +f1(1) +f1(a = 1) def f2(a): @@ -28,19 +28,19 @@ def f2(a): f2() # ok, fail f2(1) # ok, pass -f2(1, 2) # ok, fail +f2(1, 2) # ok, fail f2(a = 1) # ok, pass f2(b = 1) # ok, fail -f2(a = 1, b = 2) # ok, fail +f2(a = 1, b = 2) # ok, fail def f3(a, b): pass f3(1, 2) -f3(1, 2, 3) +f3(1, 2, 3) f3(b=2, a=1) -f3(b=1, b=2, a=1) +f3(b=1, b=2, a=1) f3(1, b=2) f3(a=1, 2) diff --git a/python/testData/inspections/PyArgumentListInspection/decorators.py b/python/testData/inspections/PyArgumentListInspection/decorators.py index 9377b5eb8148..6cb33c5d4842 100644 --- a/python/testData/inspections/PyArgumentListInspection/decorators.py +++ b/python/testData/inspections/PyArgumentListInspection/decorators.py @@ -13,7 +13,7 @@ def f7(): def f8(): pass -@deco(1, 2) # fail: extra param +@deco(1, 2) # fail: extra param def f9(): pass @@ -57,7 +57,7 @@ class Dec2: def __init__(self, p1, p2): pass -@Dec2() # fail: no p1, p2 +@Dec2() # fail: no p1, p2 def f17(): pass diff --git a/python/testData/inspections/PyArgumentListInspection/kwargsMapToNothing.py b/python/testData/inspections/PyArgumentListInspection/kwargsMapToNothing.py index 5029c3b56bd8..c285a60cf423 100644 --- a/python/testData/inspections/PyArgumentListInspection/kwargsMapToNothing.py +++ b/python/testData/inspections/PyArgumentListInspection/kwargsMapToNothing.py @@ -5,5 +5,5 @@ arg = [1, 2, 3] kwarg = {'c':3} f5(*arg, **kwarg) # ok f5(1,2, **kwarg) # ok -f5(1, 2, 3, **kwarg) # fail -f5(1, 2, 3, *arg) # fail +f5(1, 2, 3, **kwarg) # fail +f5(1, 2, 3, *arg) # fail diff --git a/python/testData/inspections/PyArgumentListInspection/py1268.py b/python/testData/inspections/PyArgumentListInspection/py1268.py index baaa9fd9e496..951df27428ee 100644 --- a/python/testData/inspections/PyArgumentListInspection/py1268.py +++ b/python/testData/inspections/PyArgumentListInspection/py1268.py @@ -3,8 +3,8 @@ def f(a, b, c): f(c=1, *(10, 20)) f(*(10, 20), c=1) -f(*(10, 20, 30), c=1) # fail: duplicate c -f(1, *(10, 20, 30)) # fail: tuple too long +f(*(10, 20, 30), c=1) # fail: duplicate c +f(1, *(10, 20, 30)) # fail: tuple too long f(1, *(10)) # fail: wrong type f(1, *(10,)) # fail: tuple too short, c not mapped @@ -24,14 +24,14 @@ f2(*(1,2), a=1, b=2, *(1,2)) # fail: a and b twice -f3(1, 2, *(3,), c=4) # fail: c twice +f3(c=3, a=1, b=2, *(1,2)) # fail: a and b twice +f3(1, 2, *(3,), c=4) # fail: c twice f3(1,2,3, *(1,2)) f3(c=3, *(1,2)) # -f3(1, c=3, *(1,2)) # fail: c twice -f3(c=3, a=1, b=2, d=(1,2)) # fail: unexpected d +f3(1, c=3, *(1,2)) # fail: c twice +f3(c=3, a=1, b=2, d=(1,2)) # fail: unexpected d f3(1, c=3, *(10,)) # ZZZ f3(1, *(10,)) f3(1, *(10,), c=20) f3(*(1,2), c=20) -f3(*(1,2), a=20) # fail: a twice +f3(*(1,2), a=20) # fail: a twice diff --git a/python/testData/inspections/PyArgumentListInspection/py3k.py b/python/testData/inspections/PyArgumentListInspection/py3k.py index 70e7820379f0..4ac87031c611 100644 --- a/python/testData/inspections/PyArgumentListInspection/py3k.py +++ b/python/testData/inspections/PyArgumentListInspection/py3k.py @@ -19,7 +19,7 @@ def a23(a, *b, c=1): pass a23(1,2,3, c=10) # pass -a23(1,2,3, c=10, a=1) # fail +a23(1,2,3, c=10, a=1) # fail a23(c=10, a=1) # pass a23(c=10, 1) # fail a23(*args, c=1) # pass diff --git a/python/testData/inspections/PyArgumentListInspection/tupleVsLiteralList.py b/python/testData/inspections/PyArgumentListInspection/tupleVsLiteralList.py index d3c41a99bd97..3936d0df1780 100644 --- a/python/testData/inspections/PyArgumentListInspection/tupleVsLiteralList.py +++ b/python/testData/inspections/PyArgumentListInspection/tupleVsLiteralList.py @@ -2,4 +2,4 @@ def f20(a, (b, c)): pass f20(1, [2, 3]) # ok -f20(1, (2, 3, 4)) # fail +f20(1, (2, 3, 4)) # fail diff --git a/python/testData/quickFixes/PyRemoveArgumentQuickFixTest/duplicate.py b/python/testData/quickFixes/PyRemoveArgumentQuickFixTest/duplicate.py index 4716f42c8564..47b5067cc548 100644 --- a/python/testData/quickFixes/PyRemoveArgumentQuickFixTest/duplicate.py +++ b/python/testData/quickFixes/PyRemoveArgumentQuickFixTest/duplicate.py @@ -2,4 +2,4 @@ def foo(a, p): pass -foo(1, p=2, p=33) +foo(1, p=2, p=33) diff --git a/python/testData/quickFixes/PyRemoveArgumentQuickFixTest/unexpected.py b/python/testData/quickFixes/PyRemoveArgumentQuickFixTest/unexpected.py index 82f42d31e15a..2ae09ef56bce 100644 --- a/python/testData/quickFixes/PyRemoveArgumentQuickFixTest/unexpected.py +++ b/python/testData/quickFixes/PyRemoveArgumentQuickFixTest/unexpected.py @@ -2,6 +2,6 @@ def foo(a): pass -foo(1, 23) +foo(1, 23) From 9eca5367bda1a89473b8beb3b121848822895f7b Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Mon, 5 Jun 2017 19:06:17 +0300 Subject: [PATCH 089/136] PY-8174 Don't change the original call in change signature It doesn't make much sense to modifying the call expression as the result of refactoring which sole purpose was to match the definition and the rest of the usages with this very expression. --- .../quickfix/PyChangeSignatureQuickFix.java | 51 ++++++++++++++----- .../PyChangeSignatureUsageProcessor.java | 6 +++ .../AddKeywordAndPositionalParameters.py | 5 ++ ...AddKeywordAndPositionalParameters_after.py | 5 ++ .../com/jetbrains/python/PyQuickFixTest.java | 5 ++ 5 files changed, 60 insertions(+), 12 deletions(-) create mode 100644 python/testData/inspections/AddKeywordAndPositionalParameters.py create mode 100644 python/testData/inspections/AddKeywordAndPositionalParameters_after.py diff --git a/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java index c4ffdf79bd25..1900f4ff5eed 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java @@ -20,10 +20,13 @@ import com.google.common.collect.PeekingIterator; import com.intellij.codeInspection.LocalQuickFixOnPsiElement; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; +import com.intellij.psi.SmartPointerManager; +import com.intellij.psi.SmartPsiElementPointer; import com.intellij.util.containers.ContainerUtil; import com.jetbrains.python.PyBundle; import com.jetbrains.python.PyNames; @@ -48,7 +51,7 @@ import static com.jetbrains.python.psi.PyUtil.as; public class PyChangeSignatureQuickFix extends LocalQuickFixOnPsiElement { - private final List> myExtraParameters; + public static final Key CHANGE_SIGNATURE_ORIGINAL_CALL = Key.create("CHANGE_SIGNATURE_ORIGINAL_CALL"); @NotNull public static PyChangeSignatureQuickFix forMismatchedCall(@NotNull PyArgumentsMapping mapping) { @@ -88,26 +91,39 @@ public class PyChangeSignatureQuickFix extends LocalQuickFixOnPsiElement { newParameters.add(Pair.create(positionalParamAnchor, new PyParameterInfo(-1, paramName, "", false))); } } - return new PyChangeSignatureQuickFix(function, newParameters); + return new PyChangeSignatureQuickFix(function, newParameters, mapping.getCallExpression()); } - @NotNull public static PyChangeSignatureQuickFix forMismatchingMethods(@NotNull PyFunction function, @NotNull PyFunction complementary) { final int paramLength = function.getParameterList().getParameters().length; final int complementaryParamLength = complementary.getParameterList().getParameters().length; + final List> extraParams; if (complementaryParamLength > paramLength) { - return new PyChangeSignatureQuickFix(function, - Collections.singletonList(Pair.create(paramLength - 1, - new PyParameterInfo(-1, "**kwargs", "", false)))); + extraParams = Collections.singletonList(Pair.create(paramLength - 1, new PyParameterInfo(-1, "**kwargs", "", false))); } - return new PyChangeSignatureQuickFix(function, Collections.emptyList()); + else { + extraParams = Collections.emptyList(); + } + return new PyChangeSignatureQuickFix(function, extraParams, null); } - public PyChangeSignatureQuickFix(@NotNull PyFunction function, @NotNull List> extraParameters) { + private final List> myExtraParameters; + private final SmartPsiElementPointer myOriginalCallExpression; + + + public PyChangeSignatureQuickFix(@NotNull PyFunction function, + @NotNull List> extraParameters, + @Nullable PyCallExpression expression) { super(function); myExtraParameters = ContainerUtil.sorted(extraParameters, Comparator.comparingInt(p -> p.getFirst())); + if (expression != null) { + myOriginalCallExpression = SmartPointerManager.getInstance(function.getProject()).createSmartPsiElementPointer(expression); + } + else { + myOriginalCallExpression = null; + } } @NotNull @@ -148,11 +164,22 @@ public class PyChangeSignatureQuickFix extends LocalQuickFixOnPsiElement { } }; - if (ApplicationManager.getApplication().isUnitTestMode()) { - dialog.createRefactoringProcessor().run(); + final PyCallExpression originalCall = myOriginalCallExpression.getElement(); + try { + if (originalCall != null) { + originalCall.putUserData(CHANGE_SIGNATURE_ORIGINAL_CALL, true); + } + if (ApplicationManager.getApplication().isUnitTestMode()) { + dialog.createRefactoringProcessor().run(); + } + else { + dialog.show(); + } } - else { - dialog.show(); + finally { + if (originalCall != null) { + originalCall.putUserData(CHANGE_SIGNATURE_ORIGINAL_CALL, null); + } } } diff --git a/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureUsageProcessor.java b/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureUsageProcessor.java index 2e7a96175f84..e84de500a694 100644 --- a/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureUsageProcessor.java +++ b/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureUsageProcessor.java @@ -34,6 +34,7 @@ import com.intellij.util.containers.MultiMap; import com.jetbrains.python.PyNames; import com.jetbrains.python.PythonLanguage; import com.jetbrains.python.documentation.docstrings.PyDocstringGenerator; +import com.jetbrains.python.inspections.quickfix.PyChangeSignatureQuickFix; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.search.PyOverridingMethodsSearch; import com.jetbrains.python.refactoring.PyRefactoringUtil; @@ -101,8 +102,13 @@ public class PyChangeSignatureUsageProcessor implements ChangeSignatureUsageProc RenameUtil.doRenameGenericNamedElement(method, changeInfo.getNewName(), usages, null); } if (element == null) return false; + if (element.getParent() instanceof PyCallExpression) { final PyCallExpression call = (PyCallExpression)element.getParent(); + // Don't modify the call that was the cause of Change Signature invocation + if (call.getUserData(PyChangeSignatureQuickFix.CHANGE_SIGNATURE_ORIGINAL_CALL) != null) { + return true; + } final PyArgumentList argumentList = call.getArgumentList(); if (argumentList != null) { final PyElementGenerator elementGenerator = PyElementGenerator.getInstance(element.getProject()); diff --git a/python/testData/inspections/AddKeywordAndPositionalParameters.py b/python/testData/inspections/AddKeywordAndPositionalParameters.py new file mode 100644 index 000000000000..d557fb3a02f5 --- /dev/null +++ b/python/testData/inspections/AddKeywordAndPositionalParameters.py @@ -0,0 +1,5 @@ +def f(x, foo=1): + pass + + +f(x, 42, bar='spam') \ No newline at end of file diff --git a/python/testData/inspections/AddKeywordAndPositionalParameters_after.py b/python/testData/inspections/AddKeywordAndPositionalParameters_after.py new file mode 100644 index 000000000000..1d9c05e5cf52 --- /dev/null +++ b/python/testData/inspections/AddKeywordAndPositionalParameters_after.py @@ -0,0 +1,5 @@ +def f(x, foo=1, bar='spam'): + pass + + +f(x, 42, bar='spam') \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java index 05f1b21b15f9..50dbf9511cd4 100644 --- a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java @@ -647,6 +647,11 @@ public class PyQuickFixTest extends PyTestCase { " \"((/(?P.+))?))\")"); } + // PY-8174 + public void testAddKeywordAndPositionalParameters() { + doInspectionTest(PyArgumentListInspection.class, "Change signature of", true, true); + } + @Override @NonNls protected String getTestDataPath() { From fc59d948bf180fa04f8554bfdd73ae4a556ecae4 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Mon, 5 Jun 2017 01:12:17 +0300 Subject: [PATCH 090/136] PY-8174 New parameters names are unique if arguments have the same type Incrementally increasing numbers are appended to the names derived from the types of the corresponding arguments to make them sufficiently different. --- .../quickfix/PyChangeSignatureQuickFix.java | 50 ++++++++++++++----- ...ositionalParametersWithSameArgumentType.py | 6 +++ ...nalParametersWithSameArgumentType_after.py | 6 +++ .../com/jetbrains/python/PyQuickFixTest.java | 5 ++ 4 files changed, 55 insertions(+), 12 deletions(-) create mode 100644 python/testData/inspections/AddSeveralPositionalParametersWithSameArgumentType.py create mode 100644 python/testData/inspections/AddSeveralPositionalParametersWithSameArgumentType_after.py diff --git a/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java index 1900f4ff5eed..f5ceff9d9893 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java @@ -20,6 +20,7 @@ import com.google.common.collect.PeekingIterator; import com.intellij.codeInspection.LocalQuickFixOnPsiElement; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Conditions; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; @@ -28,12 +29,16 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.SmartPointerManager; import com.intellij.psi.SmartPsiElementPointer; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.HashSet; +import com.intellij.xml.util.XmlStringUtil; import com.jetbrains.python.PyBundle; -import com.jetbrains.python.PyNames; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.PyCallExpression.PyArgumentsMapping; +import com.jetbrains.python.psi.types.PyClassType; import com.jetbrains.python.psi.types.PyType; +import com.jetbrains.python.psi.types.PyUnionType; import com.jetbrains.python.psi.types.TypeEvalContext; +import com.jetbrains.python.refactoring.NameSuggesterUtil; import com.jetbrains.python.refactoring.PyRefactoringUtil; import com.jetbrains.python.refactoring.changeSignature.PyChangeSignatureDialog; import com.jetbrains.python.refactoring.changeSignature.PyMethodDescriptor; @@ -42,10 +47,7 @@ import one.util.streamex.StreamEx; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; +import java.util.*; import static com.jetbrains.python.psi.PyUtil.as; @@ -70,6 +72,8 @@ public class PyChangeSignatureQuickFix extends LocalQuickFixOnPsiElement { positionalParamAnchor++; } final List> newParameters = new ArrayList<>(); + final TypeEvalContext context = TypeEvalContext.userInitiated(function.getProject(), callExpression.getContainingFile()); + final Set usedParamNames = new HashSet<>(); for (PyExpression arg : mapping.getUnmappedArguments()) { if (arg instanceof PyKeywordArgument) { final String defaultValueText; @@ -84,11 +88,9 @@ public class PyChangeSignatureQuickFix extends LocalQuickFixOnPsiElement { new PyParameterInfo(-1, ((PyKeywordArgument)arg).getKeyword(), defaultValueText, true))); } else { - final TypeEvalContext context = TypeEvalContext.userInitiated(function.getProject(), callExpression.getContainingFile()); - final PyType type = context.getType(arg); - final String typeName = type != null && type.getName() != null ? type.getName() : PyNames.OBJECT; - final String paramName = PyRefactoringUtil.selectUniqueNameFromType(typeName, function.getStatementList()); + final String paramName = generateParameterName(arg, function, usedParamNames, context); newParameters.add(Pair.create(positionalParamAnchor, new PyParameterInfo(-1, paramName, "", false))); + usedParamNames.add(paramName); } } return new PyChangeSignatureQuickFix(function, newParameters, mapping.getCallExpression()); @@ -141,9 +143,9 @@ public class PyChangeSignatureQuickFix extends LocalQuickFixOnPsiElement { final String params = StringUtil.join(createMethodDescriptor(function).getParameters(), info -> { return info.getOldIndex() == -1 ? "" + info.getName() + "" : info.getName(); }, ", "); - return "" + - PyBundle.message("QFIX.change.signature.of", StringUtil.notNullize(function.getName()) + "(" + params + ")") + - ""; + + final String message = PyBundle.message("QFIX.change.signature.of", StringUtil.notNullize(function.getName()) + "(" + params + ")"); + return XmlStringUtil.wrapInHtml(message); } @Nullable @@ -183,6 +185,30 @@ public class PyChangeSignatureQuickFix extends LocalQuickFixOnPsiElement { } } + @NotNull + private static String generateParameterName(@NotNull PyExpression argumentValue, + @NotNull PyFunction function, + @NotNull Set usedParameterNames, + @NotNull TypeEvalContext context) { + PyType type = context.getType(argumentValue); + if (type instanceof PyUnionType) { + type = ContainerUtil.find(((PyUnionType)type).getMembers(), Conditions.instanceOf(PyClassType.class)); + } + final String typeName = type != null && type.getName() != null ? type.getName() : "object"; + + final Collection suggestions = NameSuggesterUtil.generateNamesByType(typeName); + final String shortestName = ContainerUtil.getFirstItem(suggestions); + assert shortestName != null; + + String result = shortestName; + int counter = 1; + while (!PyRefactoringUtil.isValidNewName(result, function.getStatementList()) || usedParameterNames.contains(result)) { + result = shortestName + counter; + counter++; + } + return result; + } + @NotNull private PyMethodDescriptor createMethodDescriptor(final PyFunction function) { return new PyMethodDescriptor(function) { diff --git a/python/testData/inspections/AddSeveralPositionalParametersWithSameArgumentType.py b/python/testData/inspections/AddSeveralPositionalParametersWithSameArgumentType.py new file mode 100644 index 000000000000..fc6951765467 --- /dev/null +++ b/python/testData/inspections/AddSeveralPositionalParametersWithSameArgumentType.py @@ -0,0 +1,6 @@ +def func(i1): + i2 = 'Spam' + + + +func(1, 2, 3) \ No newline at end of file diff --git a/python/testData/inspections/AddSeveralPositionalParametersWithSameArgumentType_after.py b/python/testData/inspections/AddSeveralPositionalParametersWithSameArgumentType_after.py new file mode 100644 index 000000000000..6fc392ed7552 --- /dev/null +++ b/python/testData/inspections/AddSeveralPositionalParametersWithSameArgumentType_after.py @@ -0,0 +1,6 @@ +def func(i1, i, i3): + i2 = 'Spam' + + + +func(1, 2, 3) \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java index 50dbf9511cd4..2c18d8f19bce 100644 --- a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java @@ -652,6 +652,11 @@ public class PyQuickFixTest extends PyTestCase { doInspectionTest(PyArgumentListInspection.class, "Change signature of", true, true); } + // PY-8174 + public void testAddSeveralPositionalParametersWithSameArgumentType() { + doInspectionTest(PyArgumentListInspection.class, "Change signature of", true, true); + } + @Override @NonNls protected String getTestDataPath() { From ef60722aead81e020d4effddba8ea6db97b33502 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Mon, 5 Jun 2017 14:14:31 +0300 Subject: [PATCH 091/136] PY-8174 Always use actual arguments as default values for new parameters even if names referenced in them are not available at the places of some usages and the definition. It's user responsibility to review them and ensure that refactoring won't cause any errors. This approach follows the one used for Java. --- .../quickfix/PyChangeSignatureQuickFix.java | 19 ++++++++----------- .../python/refactoring/PyRefactoringUtil.java | 14 -------------- .../inspections/AddParameterDefaultValues.py | 6 ++++++ .../AddParameterDefaultValues_after.py | 6 ++++++ .../com/jetbrains/python/PyQuickFixTest.java | 5 +++++ 5 files changed, 25 insertions(+), 25 deletions(-) create mode 100644 python/testData/inspections/AddParameterDefaultValues.py create mode 100644 python/testData/inspections/AddParameterDefaultValues_after.py diff --git a/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java index f5ceff9d9893..ec2f531832ba 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java @@ -76,20 +76,14 @@ public class PyChangeSignatureQuickFix extends LocalQuickFixOnPsiElement { final Set usedParamNames = new HashSet<>(); for (PyExpression arg : mapping.getUnmappedArguments()) { if (arg instanceof PyKeywordArgument) { - final String defaultValueText; final PyExpression value = ((PyKeywordArgument)arg).getValueExpression(); - if (value != null && PyRefactoringUtil.isSimpleExpression(value) && !value.textContains('\n')) { - defaultValueText = value.getText(); - } - else { - defaultValueText = ApplicationManager.getApplication().isUnitTestMode() ? "None" : ""; - } + final String valueText = value != null ? value.getText() : ""; newParameters.add(Pair.create(parameters.length - 1, - new PyParameterInfo(-1, ((PyKeywordArgument)arg).getKeyword(), defaultValueText, true))); + new PyParameterInfo(-1, ((PyKeywordArgument)arg).getKeyword(), valueText, true))); } else { final String paramName = generateParameterName(arg, function, usedParamNames, context); - newParameters.add(Pair.create(positionalParamAnchor, new PyParameterInfo(-1, paramName, "", false))); + newParameters.add(Pair.create(positionalParamAnchor, new PyParameterInfo(-1, paramName, arg.getText(), false))); usedParamNames.add(paramName); } } @@ -109,12 +103,15 @@ public class PyChangeSignatureQuickFix extends LocalQuickFixOnPsiElement { } return new PyChangeSignatureQuickFix(function, extraParams, null); } - - + private final List> myExtraParameters; private final SmartPsiElementPointer myOriginalCallExpression; + /** + * @param extraParameters new parameters anchored by indexes of the existing parameters they should be inserted after + * (-1 in case they should precede the first parameter) + */ public PyChangeSignatureQuickFix(@NotNull PyFunction function, @NotNull List> extraParameters, @Nullable PyCallExpression expression) { diff --git a/python/src/com/jetbrains/python/refactoring/PyRefactoringUtil.java b/python/src/com/jetbrains/python/refactoring/PyRefactoringUtil.java index 14592a8abbe1..4448804a780c 100644 --- a/python/src/com/jetbrains/python/refactoring/PyRefactoringUtil.java +++ b/python/src/com/jetbrains/python/refactoring/PyRefactoringUtil.java @@ -17,7 +17,6 @@ package com.jetbrains.python.refactoring; import com.intellij.codeInsight.PsiEquivalenceUtil; import com.intellij.find.findUsages.FindUsagesHandler; -import com.intellij.lang.ASTNode; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Pair; @@ -29,7 +28,6 @@ import com.intellij.usageView.UsageInfo; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashSet; import com.jetbrains.python.PyNames; -import com.jetbrains.python.PyTokenTypes; import com.jetbrains.python.findUsages.PyFindUsagesHandlerFactory; import com.jetbrains.python.psi.*; import com.jetbrains.python.refactoring.introduce.IntroduceValidator; @@ -399,16 +397,4 @@ public class PyRefactoringUtil { public static boolean isValidNewName(@NotNull String name, @NotNull PsiElement scopeAnchor) { return !(IntroduceValidator.isDefinedInScope(name, scopeAnchor) || PyNames.isReserved(name)); } - - public static boolean isSimpleExpression(@NotNull PyExpression value) { - if (value instanceof PyLiteralExpression) { - final ASTNode node = value.getNode(); - // Check that string literal doesn't contain multiple glued nodes - return node.getChildren(null).length == 1 && PyTokenTypes.SCALAR_LITERALS.contains(node.getFirstChildNode().getElementType()); - } - else if (value instanceof PyReferenceExpression) { - return PyUtil.isPy2ReservedWord((PyReferenceExpression)value); - } - return false; - } } diff --git a/python/testData/inspections/AddParameterDefaultValues.py b/python/testData/inspections/AddParameterDefaultValues.py new file mode 100644 index 000000000000..eceee62d0ccc --- /dev/null +++ b/python/testData/inspections/AddParameterDefaultValues.py @@ -0,0 +1,6 @@ +def func(): + pass + + +func(42, foo='spam') +func() diff --git a/python/testData/inspections/AddParameterDefaultValues_after.py b/python/testData/inspections/AddParameterDefaultValues_after.py new file mode 100644 index 000000000000..046a292bc48c --- /dev/null +++ b/python/testData/inspections/AddParameterDefaultValues_after.py @@ -0,0 +1,6 @@ +def func(i, foo='spam'): + pass + + +func(42, foo='spam') +func(42) diff --git a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java index 2c18d8f19bce..2f07cca791a7 100644 --- a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java @@ -657,6 +657,11 @@ public class PyQuickFixTest extends PyTestCase { doInspectionTest(PyArgumentListInspection.class, "Change signature of", true, true); } + // PY-8174 + public void testAddParameterDefaultValues() { + doInspectionTest(PyArgumentListInspection.class, "Change signature of", true, true); + } + @Override @NonNls protected String getTestDataPath() { From 0a575d9531bfe6648a6af0c5eb54a1aaf0d33682 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Mon, 5 Jun 2017 14:37:21 +0300 Subject: [PATCH 092/136] PY-8174 Consistent naming of test cases and full quickfix names in them --- ...ChangeSignatureKeywordAndPositionalParameters.py} | 0 ...SignatureKeywordAndPositionalParameters_after.py} | 0 ...tType.py => ChangeSignatureNewParametersNames.py} | 0 ...py => ChangeSignatureNewParametersNames_after.py} | 0 ....py => ChangeSignatureParametersDefaultValues.py} | 0 ... ChangeSignatureParametersDefaultValues_after.py} | 0 .../testSrc/com/jetbrains/python/PyQuickFixTest.java | 12 ++++++------ 7 files changed, 6 insertions(+), 6 deletions(-) rename python/testData/inspections/{AddKeywordAndPositionalParameters.py => ChangeSignatureKeywordAndPositionalParameters.py} (100%) rename python/testData/inspections/{AddKeywordAndPositionalParameters_after.py => ChangeSignatureKeywordAndPositionalParameters_after.py} (100%) rename python/testData/inspections/{AddSeveralPositionalParametersWithSameArgumentType.py => ChangeSignatureNewParametersNames.py} (100%) rename python/testData/inspections/{AddSeveralPositionalParametersWithSameArgumentType_after.py => ChangeSignatureNewParametersNames_after.py} (100%) rename python/testData/inspections/{AddParameterDefaultValues.py => ChangeSignatureParametersDefaultValues.py} (100%) rename python/testData/inspections/{AddParameterDefaultValues_after.py => ChangeSignatureParametersDefaultValues_after.py} (100%) diff --git a/python/testData/inspections/AddKeywordAndPositionalParameters.py b/python/testData/inspections/ChangeSignatureKeywordAndPositionalParameters.py similarity index 100% rename from python/testData/inspections/AddKeywordAndPositionalParameters.py rename to python/testData/inspections/ChangeSignatureKeywordAndPositionalParameters.py diff --git a/python/testData/inspections/AddKeywordAndPositionalParameters_after.py b/python/testData/inspections/ChangeSignatureKeywordAndPositionalParameters_after.py similarity index 100% rename from python/testData/inspections/AddKeywordAndPositionalParameters_after.py rename to python/testData/inspections/ChangeSignatureKeywordAndPositionalParameters_after.py diff --git a/python/testData/inspections/AddSeveralPositionalParametersWithSameArgumentType.py b/python/testData/inspections/ChangeSignatureNewParametersNames.py similarity index 100% rename from python/testData/inspections/AddSeveralPositionalParametersWithSameArgumentType.py rename to python/testData/inspections/ChangeSignatureNewParametersNames.py diff --git a/python/testData/inspections/AddSeveralPositionalParametersWithSameArgumentType_after.py b/python/testData/inspections/ChangeSignatureNewParametersNames_after.py similarity index 100% rename from python/testData/inspections/AddSeveralPositionalParametersWithSameArgumentType_after.py rename to python/testData/inspections/ChangeSignatureNewParametersNames_after.py diff --git a/python/testData/inspections/AddParameterDefaultValues.py b/python/testData/inspections/ChangeSignatureParametersDefaultValues.py similarity index 100% rename from python/testData/inspections/AddParameterDefaultValues.py rename to python/testData/inspections/ChangeSignatureParametersDefaultValues.py diff --git a/python/testData/inspections/AddParameterDefaultValues_after.py b/python/testData/inspections/ChangeSignatureParametersDefaultValues_after.py similarity index 100% rename from python/testData/inspections/AddParameterDefaultValues_after.py rename to python/testData/inspections/ChangeSignatureParametersDefaultValues_after.py diff --git a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java index 2f07cca791a7..17244296a5b6 100644 --- a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java @@ -648,18 +648,18 @@ public class PyQuickFixTest extends PyTestCase { } // PY-8174 - public void testAddKeywordAndPositionalParameters() { - doInspectionTest(PyArgumentListInspection.class, "Change signature of", true, true); + public void testChangeSignatureKeywordAndPositionalParameters() { + doInspectionTest(PyArgumentListInspection.class, "Change signature of f(x, foo, bar)", true, true); } // PY-8174 - public void testAddSeveralPositionalParametersWithSameArgumentType() { - doInspectionTest(PyArgumentListInspection.class, "Change signature of", true, true); + public void testChangeSignatureNewParametersNames() { + doInspectionTest(PyArgumentListInspection.class, "Change signature of func(i1, i, i3)", true, true); } // PY-8174 - public void testAddParameterDefaultValues() { - doInspectionTest(PyArgumentListInspection.class, "Change signature of", true, true); + public void testChangeSignatureParametersDefaultValues() { + doInspectionTest(PyArgumentListInspection.class, "Change signature of func(i, foo)", true, true); } @Override From f303faa96733f0412da7145625ec001c5ea7a409 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Mon, 5 Jun 2017 14:55:56 +0300 Subject: [PATCH 093/136] PY-8174 Add a test on keyword only parameters in Python 3 The test on parameter names also checks the case when union type was inferred for an argument. --- .../inspections/ChangeSignatureAddKeywordOnlyParameter.py | 4 ++++ .../ChangeSignatureAddKeywordOnlyParameter_after.py | 4 ++++ .../inspections/ChangeSignatureNewParametersNames.py | 4 ++-- .../inspections/ChangeSignatureNewParametersNames_after.py | 4 ++-- python/testSrc/com/jetbrains/python/PyQuickFixTest.java | 7 +++++++ 5 files changed, 19 insertions(+), 4 deletions(-) create mode 100644 python/testData/inspections/ChangeSignatureAddKeywordOnlyParameter.py create mode 100644 python/testData/inspections/ChangeSignatureAddKeywordOnlyParameter_after.py diff --git a/python/testData/inspections/ChangeSignatureAddKeywordOnlyParameter.py b/python/testData/inspections/ChangeSignatureAddKeywordOnlyParameter.py new file mode 100644 index 000000000000..86ecc3f8fd07 --- /dev/null +++ b/python/testData/inspections/ChangeSignatureAddKeywordOnlyParameter.py @@ -0,0 +1,4 @@ +def func(x, *args, foo=None): + pass + +func(1, 2, 3, bar='spam') \ No newline at end of file diff --git a/python/testData/inspections/ChangeSignatureAddKeywordOnlyParameter_after.py b/python/testData/inspections/ChangeSignatureAddKeywordOnlyParameter_after.py new file mode 100644 index 000000000000..2196ab7ac340 --- /dev/null +++ b/python/testData/inspections/ChangeSignatureAddKeywordOnlyParameter_after.py @@ -0,0 +1,4 @@ +def func(x, *args, foo=None, bar='spam'): + pass + +func(1, 2, 3, bar='spam') \ No newline at end of file diff --git a/python/testData/inspections/ChangeSignatureNewParametersNames.py b/python/testData/inspections/ChangeSignatureNewParametersNames.py index fc6951765467..2d7040d78247 100644 --- a/python/testData/inspections/ChangeSignatureNewParametersNames.py +++ b/python/testData/inspections/ChangeSignatureNewParametersNames.py @@ -2,5 +2,5 @@ def func(i1): i2 = 'Spam' - -func(1, 2, 3) \ No newline at end of file +x = 42 or 'str' +func(1, 2, x) \ No newline at end of file diff --git a/python/testData/inspections/ChangeSignatureNewParametersNames_after.py b/python/testData/inspections/ChangeSignatureNewParametersNames_after.py index 6fc392ed7552..24912b03b2c8 100644 --- a/python/testData/inspections/ChangeSignatureNewParametersNames_after.py +++ b/python/testData/inspections/ChangeSignatureNewParametersNames_after.py @@ -2,5 +2,5 @@ def func(i1, i, i3): i2 = 'Spam' - -func(1, 2, 3) \ No newline at end of file +x = 42 or 'str' +func(1, 2, x) \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java index 17244296a5b6..44d34f955a4b 100644 --- a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java @@ -652,6 +652,13 @@ public class PyQuickFixTest extends PyTestCase { doInspectionTest(PyArgumentListInspection.class, "Change signature of f(x, foo, bar)", true, true); } + // PY-8174 + public void testChangeSignatureAddKeywordOnlyParameter() { + runWithLanguageLevel(LanguageLevel.PYTHON30, () -> { + doInspectionTest(PyArgumentListInspection.class, "Change signature of func(x, *args, foo, bar)", true, true); + }); + } + // PY-8174 public void testChangeSignatureNewParametersNames() { doInspectionTest(PyArgumentListInspection.class, "Change signature of func(i1, i, i3)", true, true); From cf0c247416168198d7e4b176a7ca8058f35a1411 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Mon, 5 Jun 2017 19:16:34 +0300 Subject: [PATCH 094/136] PY-8174 Make PyChangeSignatureQuickFix constructor private --- .../inspections/quickfix/PyChangeSignatureQuickFix.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java index ec2f531832ba..24886a73f171 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java @@ -112,9 +112,9 @@ public class PyChangeSignatureQuickFix extends LocalQuickFixOnPsiElement { * @param extraParameters new parameters anchored by indexes of the existing parameters they should be inserted after * (-1 in case they should precede the first parameter) */ - public PyChangeSignatureQuickFix(@NotNull PyFunction function, - @NotNull List> extraParameters, - @Nullable PyCallExpression expression) { + private PyChangeSignatureQuickFix(@NotNull PyFunction function, + @NotNull List> extraParameters, + @Nullable PyCallExpression expression) { super(function); myExtraParameters = ContainerUtil.sorted(extraParameters, Comparator.comparingInt(p -> p.getFirst())); if (expression != null) { From 33f424edc76b927a32731025e797e0fc1ef7299c Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Mon, 5 Jun 2017 20:55:25 +0300 Subject: [PATCH 095/136] PY-8174 Suggests better names for new parameters in the quickfix using NameSuggestionUtil#generateNames() and text of the main reference expression in argument as a template. Only if no meaningful names were found this way, it falls back to names generated from expression types. --- .../quickfix/PyChangeSignatureQuickFix.java | 22 ++++++++++++------- .../ChangeSignatureNewParametersNames.py | 3 +-- ...ChangeSignatureNewParametersNames_after.py | 5 ++--- .../com/jetbrains/python/PyQuickFixTest.java | 8 +++++-- 4 files changed, 23 insertions(+), 15 deletions(-) diff --git a/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java index 24886a73f171..0f61583d7b46 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java @@ -187,15 +187,21 @@ public class PyChangeSignatureQuickFix extends LocalQuickFixOnPsiElement { @NotNull PyFunction function, @NotNull Set usedParameterNames, @NotNull TypeEvalContext context) { - PyType type = context.getType(argumentValue); - if (type instanceof PyUnionType) { - type = ContainerUtil.find(((PyUnionType)type).getMembers(), Conditions.instanceOf(PyClassType.class)); + final Collection suggestions = new LinkedHashSet<>(); + final PyCallExpression callExpr = as(argumentValue, PyCallExpression.class); + final PyElement referenceElem = as(callExpr != null ? callExpr.getCallee() : argumentValue, PyReferenceExpression.class); + if (referenceElem != null) { + suggestions.addAll(NameSuggesterUtil.generateNames(referenceElem.getText())); } - final String typeName = type != null && type.getName() != null ? type.getName() : "object"; - - final Collection suggestions = NameSuggesterUtil.generateNamesByType(typeName); - final String shortestName = ContainerUtil.getFirstItem(suggestions); - assert shortestName != null; + if (suggestions.isEmpty()) { + PyType type = context.getType(argumentValue); + if (type instanceof PyUnionType) { + type = ContainerUtil.find(((PyUnionType)type).getMembers(), Conditions.instanceOf(PyClassType.class)); + } + final String typeName = type != null && type.getName() != null ? type.getName() : "object"; + suggestions.addAll(NameSuggesterUtil.generateNamesByType(typeName)); + } + final String shortestName = Collections.min(suggestions, Comparator.comparingInt(String::length)); String result = shortestName; int counter = 1; diff --git a/python/testData/inspections/ChangeSignatureNewParametersNames.py b/python/testData/inspections/ChangeSignatureNewParametersNames.py index 2d7040d78247..1477278aa0db 100644 --- a/python/testData/inspections/ChangeSignatureNewParametersNames.py +++ b/python/testData/inspections/ChangeSignatureNewParametersNames.py @@ -2,5 +2,4 @@ def func(i1): i2 = 'Spam' -x = 42 or 'str' -func(1, 2, x) \ No newline at end of file +func(1, 2, 42 or 'str', get_num()) \ No newline at end of file diff --git a/python/testData/inspections/ChangeSignatureNewParametersNames_after.py b/python/testData/inspections/ChangeSignatureNewParametersNames_after.py index 24912b03b2c8..8281e37a5ed5 100644 --- a/python/testData/inspections/ChangeSignatureNewParametersNames_after.py +++ b/python/testData/inspections/ChangeSignatureNewParametersNames_after.py @@ -1,6 +1,5 @@ -def func(i1, i, i3): +def func(i1, i, i3, num): i2 = 'Spam' -x = 42 or 'str' -func(1, 2, x) \ No newline at end of file +func(1, 2, 42 or 'str', get_num()) \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java index 44d34f955a4b..bf03e83e183d 100644 --- a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java @@ -17,8 +17,10 @@ package com.jetbrains.python; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInspection.ex.InspectionProfileImpl; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.testFramework.TestDataFile; import com.intellij.testFramework.TestDataPath; +import com.intellij.util.containers.ContainerUtil; import com.jetbrains.python.codeInsight.PyCodeInsightSettings; import com.jetbrains.python.documentation.docstrings.DocStringFormat; import com.jetbrains.python.fixtures.PyTestCase; @@ -661,7 +663,7 @@ public class PyQuickFixTest extends PyTestCase { // PY-8174 public void testChangeSignatureNewParametersNames() { - doInspectionTest(PyArgumentListInspection.class, "Change signature of func(i1, i, i3)", true, true); + doInspectionTest(PyArgumentListInspection.class, "Change signature of func(i1, i, i3, num)", true, true); } // PY-8174 @@ -712,7 +714,9 @@ public class PyQuickFixTest extends PyTestCase { final List intentionActions = myFixture.filterAvailableIntentions(quickFixName); if (available) { if (intentionActions.isEmpty()) { - throw new AssertionError("Quickfix \"" + quickFixName + "\" is not available"); + final List intentionNames = ContainerUtil.map(myFixture.getAvailableIntentions(), IntentionAction::getText); + throw new AssertionError("Quickfix starting with \"" + quickFixName + "\" is not available. " + + "Available intentions:\n" + StringUtil.join(intentionNames, "\n")); } if (intentionActions.size() > 1) { throw new AssertionError("There are more than one quickfix with the name \"" + quickFixName + "\""); From 57b939eef42bd9172d08e29e53114a28ef561ec3 Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Mon, 5 Jun 2017 22:29:21 +0300 Subject: [PATCH 096/136] convert to atomic: add rule for array.length --- .../typeMigration/rules/AtomicConversionRule.java | 14 ++++++++++++++ .../intentions/atomic/afterAtomicArrayLength.java | 10 ++++++++++ .../intentions/atomic/beforeAtomicArrayLength.java | 8 ++++++++ 3 files changed, 32 insertions(+) create mode 100644 java/typeMigration/testData/intentions/atomic/afterAtomicArrayLength.java create mode 100644 java/typeMigration/testData/intentions/atomic/beforeAtomicArrayLength.java diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/AtomicConversionRule.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/AtomicConversionRule.java index 49047421571e..86be70f05ee9 100644 --- a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/AtomicConversionRule.java +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/AtomicConversionRule.java @@ -12,6 +12,8 @@ import com.intellij.refactoring.typeMigration.TypeConversionDescriptor; import com.intellij.refactoring.typeMigration.TypeConversionDescriptorBase; import com.intellij.refactoring.typeMigration.TypeEvaluator; import com.intellij.refactoring.typeMigration.TypeMigrationLabeler; +import com.intellij.util.ObjectUtils; +import com.siyeh.HardcodedMethodConstants; import com.siyeh.ig.psiutils.ParenthesesUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -342,6 +344,9 @@ public class AtomicConversionRule extends TypeConversionRule { if (context instanceof PsiArrayAccessExpression) { return new TypeConversionDescriptor("$qualifier$[$idx$]", "$qualifier$.get($idx$)", (PsiExpression)context); } + if (parent instanceof PsiReferenceExpression && isReferenceToLengthField((PsiReferenceExpression)parent)) { + return new TypeConversionDescriptor("$qualifier$.length", "$qualifier$.length()", (PsiExpression)parent); + } return null; } @@ -438,4 +443,13 @@ public class AtomicConversionRule extends TypeConversionRule { return null; } + private static boolean isReferenceToLengthField(@NotNull PsiReferenceExpression refExpr) { + if (!"length".equals(refExpr.getReferenceName())) { + return false; + } + PsiClass aClass = JavaPsiFacade.getElementFactory(refExpr.getProject()).getArrayClass(PsiUtil.getLanguageLevel(refExpr)); + PsiField lengthField = ObjectUtils.notNull(aClass.findFieldByName(HardcodedMethodConstants.LENGTH, false)); + return refExpr.isReferenceTo(lengthField); + } + } diff --git a/java/typeMigration/testData/intentions/atomic/afterAtomicArrayLength.java b/java/typeMigration/testData/intentions/atomic/afterAtomicArrayLength.java new file mode 100644 index 000000000000..76bd5940cb3a --- /dev/null +++ b/java/typeMigration/testData/intentions/atomic/afterAtomicArrayLength.java @@ -0,0 +1,10 @@ +import java.util.concurrent.atomic.AtomicIntegerArray; + +// "Convert to atomic" "true" +class Test { + final AtomicIntegerArray ii = new AtomicIntegerArray(new int[12]); + + void m() { + int k = ii.length(); + } +} diff --git a/java/typeMigration/testData/intentions/atomic/beforeAtomicArrayLength.java b/java/typeMigration/testData/intentions/atomic/beforeAtomicArrayLength.java new file mode 100644 index 000000000000..01fa0f5b0866 --- /dev/null +++ b/java/typeMigration/testData/intentions/atomic/beforeAtomicArrayLength.java @@ -0,0 +1,8 @@ +// "Convert to atomic" "true" +class Test { + int[] ii = new int[12]; + + void m() { + int k = ii.length; + } +} From 706aad11ff195b37766097950b308571ed275188 Mon Sep 17 00:00:00 2001 From: "Vassiliy.Kudryashov" Date: Mon, 5 Jun 2017 23:22:39 +0300 Subject: [PATCH 097/136] IOExceptionDialog it too wide --- .../src/com/intellij/util/net/IOExceptionDialog.java | 1 - 1 file changed, 1 deletion(-) diff --git a/platform/platform-api/src/com/intellij/util/net/IOExceptionDialog.java b/platform/platform-api/src/com/intellij/util/net/IOExceptionDialog.java index 571ab8edc263..35dc36354d77 100644 --- a/platform/platform-api/src/com/intellij/util/net/IOExceptionDialog.java +++ b/platform/platform-api/src/com/intellij/util/net/IOExceptionDialog.java @@ -41,7 +41,6 @@ public class IOExceptionDialog extends DialogWrapper { myErrorLabel = new JTextArea(); myErrorLabel.setEditable(false); myErrorLabel.setText(errorText); - myErrorLabel.setColumns(120); myErrorLabel.setLineWrap(true); myErrorLabel.setWrapStyleWord(true); myErrorLabel.setFont(UIManager.getFont("Label.font")); From a893d47f6b3c4eb0c4f69c832eca624aa7f172d1 Mon Sep 17 00:00:00 2001 From: Rustam Vishnyakov Date: Mon, 5 Jun 2017 23:30:43 +0300 Subject: [PATCH 098/136] "" font item when no secondary font (for IDEA-173831) --- .../src/com/intellij/ui/FontComboBox.java | 66 ++++++++++++++----- 1 file changed, 49 insertions(+), 17 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/ui/FontComboBox.java b/platform/platform-impl/src/com/intellij/ui/FontComboBox.java index 93c4d48fd0ba..9d82923d1f55 100644 --- a/platform/platform-impl/src/com/intellij/ui/FontComboBox.java +++ b/platform/platform-impl/src/com/intellij/ui/FontComboBox.java @@ -16,9 +16,11 @@ package com.intellij.ui; import com.intellij.openapi.application.Application; +import com.intellij.openapi.application.ApplicationBundle; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.ui.ComboBox; import com.intellij.util.ui.FontInfo; +import org.jetbrains.annotations.Nullable; import java.awt.Dimension; import java.util.ArrayList; @@ -40,11 +42,11 @@ public final class FontComboBox extends ComboBox { } public FontComboBox(boolean withAllStyles) { - this(withAllStyles, true); + this(withAllStyles, true, false); } - public FontComboBox(boolean withAllStyles, boolean filterNonLatin) { - super(new Model(withAllStyles, filterNonLatin)); + public FontComboBox(boolean withAllStyles, boolean filterNonLatin, boolean noFontItem) { + super(new Model(withAllStyles, filterNonLatin, noFontItem)); Dimension size = getPreferredSize(); size.width = size.height * 8; setPreferredSize(size); @@ -68,10 +70,14 @@ public final class FontComboBox extends ComboBox { return item == null ? null : item.toString(); } - public void setFontName(String item) { + public void setFontName(@Nullable String item) { myModel.setSelectedItem(item); } + public boolean isNoFontSelected() { + return myModel.isNoFontSelected(); + } + @Override public void setModel(ComboBoxModel model) { if (model instanceof Model) { @@ -84,12 +90,14 @@ public final class FontComboBox extends ComboBox { } private static final class Model extends AbstractListModel implements ComboBoxModel { + private final NoFontItem NO_FONT_ITEM; private volatile List myAllFonts = Collections.emptyList(); private volatile List myMonoFonts = Collections.emptyList(); private boolean myMonospacedOnly; private Object mySelectedItem; - private Model(boolean withAllStyles, boolean filterNonLatin) { + private Model(boolean withAllStyles, boolean filterNonLatin, boolean noFontItem) { + NO_FONT_ITEM = noFontItem ? new NoFontItem() : null; Application application = ApplicationManager.getApplication(); if (application == null || application.isUnitTestMode()) { setFonts(FontInfo.getAll(withAllStyles), filterNonLatin); @@ -132,17 +140,22 @@ public final class FontComboBox extends ComboBox { } @Override - public void setSelectedItem(Object item) { - if (item instanceof FontInfo) { - FontInfo info = getInfo(item); - if (info == null) { - List list = myMonospacedOnly ? myMonoFonts : myAllFonts; - item = list.isEmpty() ? null : list.get(0); - } + public void setSelectedItem(@Nullable Object item) { + if (item == null && NO_FONT_ITEM != null) { + item = NO_FONT_ITEM; } - if (item instanceof String) { - FontInfo info = getInfo(item); - if (info != null) item = info; + else { + if (item instanceof FontInfo) { + FontInfo info = getInfo(item); + if (info == null) { + List list = myMonospacedOnly ? myMonoFonts : myAllFonts; + item = list.isEmpty() ? null : list.get(0); + } + } + if (item instanceof String) { + FontInfo info = getInfo(item); + if (info != null) item = info; + } } if (!(mySelectedItem == null ? item == null : mySelectedItem.equals(item))) { mySelectedItem = item; @@ -150,16 +163,28 @@ public final class FontComboBox extends ComboBox { } } + public boolean isNoFontSelected() { + return getSelectedItem() == NO_FONT_ITEM; + } + @Override public int getSize() { List list = myMonospacedOnly ? myMonoFonts : myAllFonts; - return mySelectedItem instanceof String ? 1 + list.size() : list.size(); + int size = list.size(); + if (mySelectedItem instanceof String) size ++; + if (NO_FONT_ITEM != null) size++; + return size; } @Override public Object getElementAt(int index) { + int i = index; + if (NO_FONT_ITEM != null) { + if (index == 0) return NO_FONT_ITEM; + i --; + } List list = myMonospacedOnly ? myMonoFonts : myAllFonts; - return 0 <= index && index < list.size() ? list.get(index) : mySelectedItem; + return 0 <= i && i < list.size() ? list.get(i) : mySelectedItem; } private FontInfo getInfo(Object item) { @@ -170,5 +195,12 @@ public final class FontComboBox extends ComboBox { } return null; } + + private final static class NoFontItem { + @Override + public String toString() { + return ApplicationBundle.message("settings.editor.font.none"); + } + } } } From c26bb5297cda8bf0d433c61ba50e9d00b13b7f07 Mon Sep 17 00:00:00 2001 From: Rustam Vishnyakov Date: Mon, 5 Jun 2017 23:34:37 +0300 Subject: [PATCH 099/136] IDEA-173831 Change editor/scheme Font settings UI --- .../colors/AbstractFontOptionsPanel.java | 91 +++++++++++++------ .../options/colors/ConsoleFontOptions.java | 14 ++- .../options/colors/FontOptions.java | 85 ++++++++++++++--- .../src/messages/ApplicationBundle.properties | 7 +- 4 files changed, 150 insertions(+), 47 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/application/options/colors/AbstractFontOptionsPanel.java b/platform/lang-impl/src/com/intellij/application/options/colors/AbstractFontOptionsPanel.java index 893da0cf4387..509478b12a95 100644 --- a/platform/lang-impl/src/com/intellij/application/options/colors/AbstractFontOptionsPanel.java +++ b/platform/lang-impl/src/com/intellij/application/options/colors/AbstractFontOptionsPanel.java @@ -23,7 +23,6 @@ import com.intellij.openapi.application.ApplicationNamesInfo; import com.intellij.openapi.editor.colors.EditorColorsManager; import com.intellij.openapi.editor.colors.FontPreferences; import com.intellij.openapi.editor.colors.ModifiableFontPreferences; -import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.util.SystemInfo; import com.intellij.ui.DocumentAdapter; import com.intellij.ui.FontComboBox; @@ -32,7 +31,6 @@ import com.intellij.ui.TooltipWithClickableLinks; import com.intellij.ui.components.JBCheckBox; import com.intellij.util.EventDispatcher; import com.intellij.util.ui.JBUI; -import net.miginfocom.swing.MigLayout; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -61,9 +59,8 @@ public abstract class AbstractFontOptionsPanel extends JPanel implements Options @NotNull private final JTextField myEditorFontSizeField = new JTextField(4); @NotNull private final JTextField myLineSpacingField = new JTextField(4); private final FontComboBox myPrimaryCombo = new FontComboBox(); - private final JCheckBox myUseSecondaryFontCheckbox = new JCheckBox(ApplicationBundle.message("secondary.font")); private final JCheckBox myEnableLigaturesCheckbox = new JCheckBox(ApplicationBundle.message("use.ligatures")); - private final FontComboBox mySecondaryCombo = new FontComboBox(false, false); + private final FontComboBox mySecondaryCombo = new FontComboBox(false, false, true); @NotNull private final JBCheckBox myOnlyMonospacedCheckBox = new JBCheckBox(ApplicationBundle.message("checkbox.show.only.monospaced.fonts")); @@ -72,30 +69,67 @@ public abstract class AbstractFontOptionsPanel extends JPanel implements Options private JLabel myPrimaryLabel; private JLabel mySizeLabel; + protected final static int ADDITIONAL_VERTICAL_GAP = 12; + protected final static int BASE_INSET = 5; + private JLabel mySecondaryFontLabel; + private JLabel myLineSpacingLabel; protected AbstractFontOptionsPanel() { - setLayout(new MigLayout("ins 0, gap 5, flowx")); - initControls(); + setLayout(new FlowLayout(FlowLayout.LEFT)); + add(createControls()); + } + + protected JComponent createControls() { + return createFontSettingsPanel(); } @SuppressWarnings("unchecked") - protected void initControls() { - add(myOnlyMonospacedCheckBox, "newline 10, sgx b, sx 2"); + protected final JPanel createFontSettingsPanel() { + JPanel fontPanel = new JPanel(new GridBagLayout()); + GridBagConstraints c = new GridBagConstraints(); + c.anchor = GridBagConstraints.WEST; + c.insets = JBUI.insets(BASE_INSET, BASE_INSET, 0, 0); + c.gridx = 0; + c.gridy = 0; myPrimaryLabel = new JLabel(ApplicationBundle.message("primary.font")); - add(myPrimaryLabel, "newline, ax right"); - add(myPrimaryCombo, "sgx b"); + fontPanel.add(myPrimaryLabel, c); + + c.gridx = 1; + fontPanel.add(myPrimaryCombo, c); + + c.gridx = 2; + fontPanel.add(myOnlyMonospacedCheckBox, c); + + c.gridx = 0; + c.gridy = 1; mySizeLabel = new JLabel(ApplicationBundle.message("editbox.font.size")); - add(mySizeLabel, "gapleft 20"); - add(myEditorFontSizeField); - add(new JLabel(ApplicationBundle.message("editbox.line.spacing")), "gapleft 20"); - add(myLineSpacingField); + fontPanel.add(mySizeLabel, c); + + c.gridx = 1; + fontPanel.add(myEditorFontSizeField, c); + + c.gridx = 0; + c.gridy = 2; + myLineSpacingLabel = new JLabel(ApplicationBundle.message("editbox.line.spacing")); + myLineSpacingLabel.setLabelFor(myLineSpacingField); + fontPanel.add(myLineSpacingLabel, c); + c.gridx = 1; + fontPanel.add(myLineSpacingField,c); + + c.gridy = 3; + c.gridx = 0; + c.insets = JBUI.insets(BASE_INSET + ADDITIONAL_VERTICAL_GAP, BASE_INSET, 0, 0); + mySecondaryFontLabel = new JLabel(ApplicationBundle.message("secondary.font")); + mySecondaryFontLabel.setLabelFor(mySecondaryCombo); + fontPanel.add(mySecondaryFontLabel, c); + c.gridx = 1; + fontPanel.add(mySecondaryCombo, c); + c.gridx = 2; + JLabel fallbackLabel = new JLabel(ApplicationBundle.message("label.fallback.fonts.list.description")); + fallbackLabel.setEnabled(false); + fontPanel.add(fallbackLabel, c); - add(new JLabel(ApplicationBundle.message("label.fallback.fonts.list.description"), - MessageType.INFO.getDefaultIcon(), - SwingConstants.LEFT), "newline, sx 5"); - add(myUseSecondaryFontCheckbox, "newline, ax right"); - add(mySecondaryCombo, "sgx b"); JPanel panel = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0)); myEnableLigaturesCheckbox.setBorder(null); panel.add(myEnableLigaturesCheckbox); @@ -108,10 +142,12 @@ public abstract class AbstractFontOptionsPanel extends JPanel implements Options warningIcon.setBorder(JBUI.Borders.emptyLeft(5)); warningIcon.setVisible(!SystemInfo.isJetBrainsJvm); panel.add(warningIcon); - add(panel, "newline, sx 2"); + c.gridx = 0; + c.gridy = 4; + c.gridwidth = 2; + fontPanel.add(panel, c); myOnlyMonospacedCheckBox.setBorder(null); - myUseSecondaryFontCheckbox.setBorder(null); mySecondaryCombo.setEnabled(false); myOnlyMonospacedCheckBox.setSelected(EditorColorsManager.getInstance().isUseOnlyMonospacedFonts()); @@ -126,10 +162,6 @@ public abstract class AbstractFontOptionsPanel extends JPanel implements Options mySecondaryCombo.setMonospacedOnly(myOnlyMonospacedCheckBox.isSelected()); mySecondaryCombo.setRenderer(RENDERER); - myUseSecondaryFontCheckbox.addActionListener(e -> { - mySecondaryCombo.setEnabled(myUseSecondaryFontCheckbox.isSelected()); - syncFontFamilies(); - }); ItemListener itemListener = this::syncFontFamilies; myPrimaryCombo.addItemListener(itemListener); mySecondaryCombo.addItemListener(itemListener); @@ -198,6 +230,7 @@ public abstract class AbstractFontOptionsPanel extends JPanel implements Options updateDescription(true); } }); + return fontPanel; } protected void setDelegatingPreferences(boolean isDelegating) { @@ -249,7 +282,7 @@ public abstract class AbstractFontOptionsPanel extends JPanel implements Options modifiableFontPreferences.clearFonts(); modifiableFontPreferences.setUseLigatures(myEnableLigaturesCheckbox.isSelected()); String primaryFontFamily = myPrimaryCombo.getFontName(); - String secondaryFontFamily = mySecondaryCombo.isEnabled() ? mySecondaryCombo.getFontName() : null; + String secondaryFontFamily = mySecondaryCombo.isNoFontSelected() ? null : mySecondaryCombo.getFontName(); int fontSize = getFontSizeFromField(); if (primaryFontFamily != null) { if (!FontPreferences.DEFAULT_FONT_NAME.equals(primaryFontFamily)) { @@ -276,7 +309,6 @@ public abstract class AbstractFontOptionsPanel extends JPanel implements Options List fontFamilies = fontPreferences.getEffectiveFontFamilies(); myPrimaryCombo.setFontName(fontPreferences.getFontFamily()); boolean isThereSecondaryFont = fontFamilies.size() > 1; - myUseSecondaryFontCheckbox.setSelected(isThereSecondaryFont); mySecondaryCombo.setFontName(isThereSecondaryFont ? fontFamilies.get(1) : null); myEditorFontSizeField.setText(String.valueOf(fontPreferences.getSize(fontPreferences.getFontFamily()))); @@ -285,12 +317,13 @@ public abstract class AbstractFontOptionsPanel extends JPanel implements Options boolean readOnly = isReadOnlyColorScheme || !(getFontPreferences() instanceof ModifiableFontPreferences); myPrimaryCombo.setEnabled(!readOnly); myPrimaryLabel.setEnabled(!readOnly); - mySecondaryCombo.setEnabled(isThereSecondaryFont && !readOnly); + mySecondaryCombo.setEnabled(!readOnly); + mySecondaryFontLabel.setEnabled(!readOnly); myOnlyMonospacedCheckBox.setEnabled(!readOnly); myLineSpacingField.setEnabled(!readOnly); + myLineSpacingLabel.setEnabled(!readOnly); myEditorFontSizeField.setEnabled(!readOnly); mySizeLabel.setEnabled(!readOnly); - myUseSecondaryFontCheckbox.setEnabled(!readOnly); myEnableLigaturesCheckbox.setEnabled(!readOnly && SystemInfo.isJetBrainsJvm); myEnableLigaturesCheckbox.setSelected(fontPreferences.useLigatures()); diff --git a/platform/lang-impl/src/com/intellij/application/options/colors/ConsoleFontOptions.java b/platform/lang-impl/src/com/intellij/application/options/colors/ConsoleFontOptions.java index 6a1f8fcd3c51..35c0886248c6 100644 --- a/platform/lang-impl/src/com/intellij/application/options/colors/ConsoleFontOptions.java +++ b/platform/lang-impl/src/com/intellij/application/options/colors/ConsoleFontOptions.java @@ -31,8 +31,13 @@ public class ConsoleFontOptions extends FontOptions { @Nullable @Override - protected String getInheritFontTitle() { - return "editor font"; + protected String getInheritedFontTitle() { + return "Scheme font"; + } + + @Override + protected String getOverwriteFontTitle() { + return "Set console font for color scheme"; } @Override @@ -55,6 +60,11 @@ public class ConsoleFontOptions extends FontOptions { return getCurrentScheme().getConsoleFontPreferences(); } + @Override + protected FontPreferences getBaseFontPreferences() { + return getCurrentScheme().getFontPreferences(); + } + @Override protected void setDelegatingPreferences(boolean isDelegating) { FontPreferences currPrefs = getCurrentScheme().getConsoleFontPreferences(); diff --git a/platform/lang-impl/src/com/intellij/application/options/colors/FontOptions.java b/platform/lang-impl/src/com/intellij/application/options/colors/FontOptions.java index 0f790fad6150..be16c024c59e 100644 --- a/platform/lang-impl/src/com/intellij/application/options/colors/FontOptions.java +++ b/platform/lang-impl/src/com/intellij/application/options/colors/FontOptions.java @@ -21,9 +21,13 @@ import com.intellij.ide.DataManager; import com.intellij.openapi.editor.colors.DelegatingFontPreferences; import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.colors.FontPreferences; +import com.intellij.openapi.editor.colors.impl.AppEditorFontOptions; import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.ex.Settings; import com.intellij.ui.HoverHyperlinkLabel; +import com.intellij.ui.JBColor; +import com.intellij.util.ui.JBDimension; +import com.intellij.util.ui.JBUI; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -39,24 +43,53 @@ public class FontOptions extends AbstractFontOptionsPanel { @NotNull private final ColorAndFontOptions myOptions; private @Nullable JCheckBox myInheritFontCheckbox; + private @Nullable JLabel myBaseFontInfoLabel; + + private final static int FONT_PANEL_LEFT_OFFSET = 15; public FontOptions(@NotNull ColorAndFontOptions options) { myOptions = options; } @Nullable - protected String getInheritFontTitle() { - return "default font"; + protected String getInheritedFontTitle() { + return "Default font"; + } + + protected String getOverwriteFontTitle() { + return "Set font for color scheme"; } @Override - protected void initControls() { - createInheritCheckBox(); - super.initControls(); + protected JComponent createControls() { + Component inheritBox = createInheritCheckBox(); + if (inheritBox != null) { + JPanel topPanel = new JPanel(new GridBagLayout()); + GridBagConstraints c = new GridBagConstraints(); + c.gridx = 0; + c.gridy = 0; + c.gridwidth = 2; + c.insets = JBUI.insets(BASE_INSET, BASE_INSET, ADDITIONAL_VERTICAL_GAP, 0); + c.anchor = GridBagConstraints.LINE_START; + topPanel.add(inheritBox, c); + c.gridy = 1; + c.gridx = 0; + c.gridwidth = 1; + c.insets = JBUI.emptyInsets(); + topPanel.add(Box.createRigidArea(JBDimension.create(new Dimension(FONT_PANEL_LEFT_OFFSET, 0))), c); + c.gridx = 1; + c.anchor = GridBagConstraints.NORTHWEST; + topPanel.add(createFontSettingsPanel(), c); + return topPanel; + } + else { + return super.createControls(); + } } - private void createInheritCheckBox() { - if (getInheritFontTitle() != null) { + @Nullable + private Component createInheritCheckBox() { + if (getInheritedFontTitle() != null) { JPanel inheritPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 0,0 )); inheritPanel.setBorder(BorderFactory.createEmptyBorder()); myInheritFontCheckbox = new JCheckBox(); @@ -68,17 +101,40 @@ public class FontOptions extends AbstractFontOptionsPanel { } }); inheritPanel.add(myInheritFontCheckbox); - inheritPanel.add(new JLabel("Use ")); - inheritPanel.add(createHyperlinkLabel()); - - add(inheritPanel, "newline, span"); - add(new JSeparator(), "newline, growx, span"); + inheritPanel.add(new JLabel(getOverwriteFontTitle())); + inheritPanel.add(Box.createRigidArea(JBDimension.create(new Dimension(5,0)))); + inheritPanel.add(grayed(new JLabel("("))); + inheritPanel.add(grayed(createHyperlinkLabel())); + inheritPanel.add(grayed(new JLabel(": "))); + myBaseFontInfoLabel = grayed(new JLabel("?")); + inheritPanel.add(myBaseFontInfoLabel); + inheritPanel.add(grayed(new JLabel(")"))); + return inheritPanel; } + return null; + } + + private static JLabel grayed(JLabel label) { + label.setForeground(JBColor.GRAY); + return label; + } + + private String getBaseFontInfo() { + StringBuilder sb = new StringBuilder(); + FontPreferences basePrefs = getBaseFontPreferences(); + sb.append(basePrefs.getFontFamily()); + sb.append(','); + sb.append(basePrefs.getSize(basePrefs.getFontFamily())); + return sb.toString(); + } + + protected FontPreferences getBaseFontPreferences() { + return AppEditorFontOptions.getInstance().getFontPreferences(); } @NotNull private JLabel createHyperlinkLabel() { - HoverHyperlinkLabel label = new HoverHyperlinkLabel(getInheritFontTitle()); + HoverHyperlinkLabel label = new HoverHyperlinkLabel(getInheritedFontTitle()); label.addHyperlinkListener(new HyperlinkListener() { @Override public void hyperlinkUpdate(HyperlinkEvent e) { @@ -154,6 +210,9 @@ public class FontOptions extends AbstractFontOptionsPanel { myInheritFontCheckbox.setEnabled(!isReadOnly()); myInheritFontCheckbox.setSelected(isDelegating()); } + if (myBaseFontInfoLabel != null) { + myBaseFontInfoLabel.setText(getBaseFontInfo()); + } } } diff --git a/platform/platform-resources-en/src/messages/ApplicationBundle.properties b/platform/platform-resources-en/src/messages/ApplicationBundle.properties index 9668a52b2261..3b9f5d210601 100644 --- a/platform/platform-resources-en/src/messages/ApplicationBundle.properties +++ b/platform/platform-resources-en/src/messages/ApplicationBundle.properties @@ -542,10 +542,11 @@ error.a.scheme.with.this.name.already.exists.or.was.deleted.without.applying.the title.select.font=Select Font action.apply.editor.font.settings=Apply editor font settings checkbox.show.only.monospaced.fonts=Show only monospaced fonts -primary.font=Primary font: -secondary.font=Secondary font: +primary.font=Font: +secondary.font=Fallback font: use.ligatures=Enable font ligatures ligatures.jre.warning=The JRE you are running {0} with
is known to have performance issues
related to ligatures support.
Use JetBrains Runtime instead to enable ligatures. +settings.editor.font.none= editbox.enter.tag.name=Enter tag name: title.tag.name=Tag Name title.xml=XML @@ -574,7 +575,7 @@ title.scope.based=By Scope title.colors.and.fonts=Colors \\& Fonts progress.analysing.font=Analysing font: {0} group.editor.font=Editor Font -label.fallback.fonts.list.description=If primary font fails, IDE tries to use the secondary one +label.fallback.fonts.list.description=For symbols not supported by the main font quickdoc.tooltip.font.size.by.wheel=Slider or Ctrl+Wheel change font size label.font.size=Font size: label.font.name=Name: From e43bd129443fe11f2c549536141fc269c06a8e94 Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Mon, 5 Jun 2017 23:48:10 +0200 Subject: [PATCH 100/136] Add shortcut collector --- .../statistic/ShortcutsCollector.java | 102 ++++++++++++++++++ .../keymap/impl/IdeKeyEventDispatcher.java | 2 + .../src/META-INF/PlatformExtensions.xml | 2 + 3 files changed, 106 insertions(+) create mode 100644 platform/platform-impl/src/com/intellij/internal/statistic/ShortcutsCollector.java diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/ShortcutsCollector.java b/platform/platform-impl/src/com/intellij/internal/statistic/ShortcutsCollector.java new file mode 100644 index 000000000000..cabfd17ed1c3 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/internal/statistic/ShortcutsCollector.java @@ -0,0 +1,102 @@ +/* + * Copyright 2000-2017 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.internal.statistic; + +import com.intellij.internal.statistic.beans.GroupDescriptor; +import com.intellij.internal.statistic.beans.UsageDescriptor; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.KeyboardShortcut; +import com.intellij.openapi.components.*; +import com.intellij.openapi.keymap.KeymapUtil; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.xmlb.annotations.MapAnnotation; +import com.intellij.util.xmlb.annotations.Tag; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * @author Konstantin Bulenkov + */ +@State( + name = "ToolbarClicksCollector", + storages = @Storage(value = "statistics.shortcuts.xml", roamingType = RoamingType.DISABLED) +) +public class ShortcutsCollector implements PersistentStateComponent { + final static class MyState { + @Tag("counts") + @MapAnnotation(surroundWithTag = false, keyAttributeName = "shortcut", valueAttributeName = "count") + public final Map myValues = new HashMap<>(); + } + private MyState myState = new MyState(); + + @NotNull + public MyState getState() { + return myState; + } + + public void loadState(final MyState state) { + myState = state; + } + + public static void record(AnActionEvent event) { + InputEvent e = event.getInputEvent(); + if (e instanceof KeyEvent) { + KeyboardShortcut shortcut = new KeyboardShortcut(KeyStroke.getKeyStrokeForEvent((KeyEvent)e), null); + String key = KeymapUtil.getShortcutText(shortcut); + ShortcutsCollector collector = getInstance(); + + if (collector == null) return; //no shortcuts stats for the IDE + + Map values = collector.getState().myValues; + values.put(key, ContainerUtil.getOrElse(values, key, 0) + 1); + } + } + + + private static ShortcutsCollector getInstance() { + return ServiceManager.getService(ShortcutsCollector.class); + } + + final static class ShortcutUsagesCollector extends UsagesCollector { + private static final GroupDescriptor GROUP = GroupDescriptor.create(getGroupName(), GroupDescriptor.HIGHER_PRIORITY); + + private static String getGroupName() { + if (SystemInfo.isMac) return "Shortcuts on Mac"; + if (SystemInfo.isWindows) return "Shortcuts on Windows"; + if (SystemInfo.isLinux) return "Shortcuts on Linux"; + return "Shortcuts on OtherOs"; + } + + @NotNull + public Set getUsages() { + MyState state = getInstance().getState(); + assert state != null; + return ContainerUtil.map2Set(state.myValues.entrySet(), e -> new UsageDescriptor(e.getKey(), e.getValue())); + } + + @NotNull + public GroupDescriptor getGroupId() { + return GROUP; + } + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeKeyEventDispatcher.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeKeyEventDispatcher.java index eb72386668d9..2585c2009b33 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeKeyEventDispatcher.java +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeKeyEventDispatcher.java @@ -19,6 +19,7 @@ import com.intellij.ide.DataManager; import com.intellij.ide.IdeEventQueue; import com.intellij.ide.ProhibitAWTEvents; import com.intellij.ide.impl.DataManagerImpl; +import com.intellij.internal.statistic.ShortcutsCollector; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.actionSystem.ex.ActionManagerEx; @@ -562,6 +563,7 @@ public final class IdeKeyEventDispatcher implements Disposable { @Override public void performAction(@NotNull InputEvent e, @NotNull AnAction action, @NotNull AnActionEvent actionEvent) { e.consume(); + ShortcutsCollector.record(actionEvent); DataContext ctx = actionEvent.getDataContext(); if (action instanceof ActionGroup && !((ActionGroup)action).canBePerformed(ctx)) { diff --git a/platform/platform-resources/src/META-INF/PlatformExtensions.xml b/platform/platform-resources/src/META-INF/PlatformExtensions.xml index 0a383fea75a1..cb4f0a6010ae 100644 --- a/platform/platform-resources/src/META-INF/PlatformExtensions.xml +++ b/platform/platform-resources/src/META-INF/PlatformExtensions.xml @@ -195,6 +195,7 @@ + @@ -375,6 +376,7 @@ + From fb700a78cb6a7fa42b87523a511c2f0a728dff7d Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Tue, 6 Jun 2017 00:12:30 +0200 Subject: [PATCH 101/136] support for double shortcuts in shortcuts collector --- .../statistic/ShortcutsCollector.java | 27 ++++++++++++++----- .../impl/ModifierKeyDoubleClickHandler.java | 2 ++ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/ShortcutsCollector.java b/platform/platform-impl/src/com/intellij/internal/statistic/ShortcutsCollector.java index cabfd17ed1c3..2505bbddf331 100644 --- a/platform/platform-impl/src/com/intellij/internal/statistic/ShortcutsCollector.java +++ b/platform/platform-impl/src/com/intellij/internal/statistic/ShortcutsCollector.java @@ -59,19 +59,34 @@ public class ShortcutsCollector implements PersistentStateComponent values = collector.getState().myValues; - values.put(key, ContainerUtil.getOrElse(values, key, 0) + 1); + if (isDoubleShortcut) { + key = SystemInfo.isMac ? key + key : key + "+" + key; + } + incValue(key); } } + private static void incValue(String key) { + ShortcutsCollector collector = getInstance(); + + if (collector == null) return; //no shortcuts stats for the IDE + + Map values = collector.getState().myValues; + values.put(key, ContainerUtil.getOrElse(values, key, 0) + 1); + } + private static ShortcutsCollector getInstance() { return ServiceManager.getService(ShortcutsCollector.class); diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ModifierKeyDoubleClickHandler.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ModifierKeyDoubleClickHandler.java index a70a78f118a8..756b060dca8c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ModifierKeyDoubleClickHandler.java +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ModifierKeyDoubleClickHandler.java @@ -17,6 +17,7 @@ package com.intellij.openapi.keymap.impl; import com.intellij.ide.DataManager; import com.intellij.ide.IdeEventQueue; +import com.intellij.internal.statistic.ShortcutsCollector; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.actionSystem.ex.ActionManagerEx; @@ -260,6 +261,7 @@ public class ModifierKeyDoubleClickHandler implements Disposable, ApplicationCom myActionManagerEx.fireBeforeActionPerformed(action, anActionEvent.getDataContext(), anActionEvent); action.actionPerformed(anActionEvent); myActionManagerEx.fireAfterActionPerformed(action, anActionEvent.getDataContext(), anActionEvent); + ShortcutsCollector.recordDoubleShortcut(anActionEvent); return true; } finally { From f3605a30af527bab9846e3291430ac32a8d2546f Mon Sep 17 00:00:00 2001 From: "Irina.Chernushina" Date: Mon, 5 Jun 2017 23:22:15 +0200 Subject: [PATCH 102/136] es6 convert to export: improve when module.exports in comma expression - gather inline comments also around expression - better cut of the expression from comma expression together with inline comments around (old method does this poorly) and comma --- .../com/intellij/psi/util/PsiTreeUtil.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/platform/core-api/src/com/intellij/psi/util/PsiTreeUtil.java b/platform/core-api/src/com/intellij/psi/util/PsiTreeUtil.java index ed907a2b696d..78ff118edeeb 100644 --- a/platform/core-api/src/com/intellij/psi/util/PsiTreeUtil.java +++ b/platform/core-api/src/com/intellij/psi/util/PsiTreeUtil.java @@ -30,7 +30,9 @@ import com.intellij.psi.search.PsiElementProcessor.FindElement; import com.intellij.psi.stubs.StubBase; import com.intellij.psi.stubs.StubElement; import com.intellij.psi.templateLanguages.OuterLanguageElement; +import com.intellij.psi.tree.IElementType; import com.intellij.util.ArrayUtil; +import com.intellij.util.Consumer; import com.intellij.util.PairProcessor; import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; @@ -661,6 +663,32 @@ public class PsiTreeUtil { return (T)element; } + @Nullable + public static PsiElement findSiblingForward(@NotNull final PsiElement element, + @NotNull final IElementType elementType, + @Nullable final Consumer consumer) { + for (PsiElement e = element.getNextSibling(); e != null; e = e.getNextSibling()) { + if (elementType.equals(e.getNode().getElementType())) { + return e; + } + if (consumer != null) consumer.consume(e); + } + return null; + } + + @Nullable + public static PsiElement findSiblingBackward(@NotNull final PsiElement element, + @NotNull final IElementType elementType, + @Nullable final Consumer consumer) { + for (PsiElement e = element.getPrevSibling(); e != null; e = e.getPrevSibling()) { + if (elementType.equals(e.getNode().getElementType())) { + return e; + } + if (consumer != null) consumer.consume(e); + } + return null; + } + @Nullable @Contract("null, _ -> null") public static PsiElement skipSiblingsForward(@Nullable PsiElement element, @NotNull Class... elementClasses) { From 3436d9ca19cc9f74f8ebe2ce05212e0c85088c34 Mon Sep 17 00:00:00 2001 From: "Ilya.Kazakevich" Date: Tue, 6 Jun 2017 03:22:29 +0300 Subject: [PATCH 103/136] PY-24490: Dot dup2 std descriptors for CRT dup2 for CRT is broken in Win10 (can't dup2 stdout and CreateProcess fails with STATUS_DLL_INIT_FAILED(0xc0000142) sometimes) That is not a big deal since new process inherits handlers its CRT is initialized correctly --- native/WinElevator/elevator/elevator.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/native/WinElevator/elevator/elevator.c b/native/WinElevator/elevator/elevator.c index bad1ed9b0f2c..0fd99fd6d8e7 100644 --- a/native/WinElevator/elevator/elevator.c +++ b/native/WinElevator/elevator/elevator.c @@ -53,13 +53,7 @@ static DWORD _ConnectIfNeededPipe(DWORD nParentPid, DWORD nDescriptor, FILE* str if (!SetHandleInformation(hPipe, HANDLE_FLAG_INHERIT, TRUE)) { return GetLastError(); - } - - // Fix CRT - if (_dup2(_open_osfhandle((intptr_t)hPipe, _O_WTEXT | _O_TEXT), _fileno(stream)) != 0) - { - return errno; - } + } // Fix Win32API DWORD hStdHandleToChange = ELEV_DESCR_GET_HANDLE(nDescriptor); From d7d02ad2ac929681a7823010897c6fe534c450ed Mon Sep 17 00:00:00 2001 From: "Ilya.Kazakevich" Date: Tue, 6 Jun 2017 03:38:20 +0300 Subject: [PATCH 104/136] PY-24490: Dot dup2 std descriptors for CRT (File added) --- bin/win/elevator.exe | Bin 168112 -> 166064 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/bin/win/elevator.exe b/bin/win/elevator.exe index 1f04f3f3d480f14ecc6db2ed6d08e10f5476793a..8e04f0477a0b6a7361b890131dfeaf0f0c7975de 100644 GIT binary patch delta 36707 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 n?(wK~ZxlTnnztl Date: Tue, 6 Jun 2017 08:48:29 +0200 Subject: [PATCH 105/136] fix assertion in Shortcut Collector --- .../src/com/intellij/internal/statistic/ShortcutsCollector.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/ShortcutsCollector.java b/platform/platform-impl/src/com/intellij/internal/statistic/ShortcutsCollector.java index 2505bbddf331..dafff7b6655f 100644 --- a/platform/platform-impl/src/com/intellij/internal/statistic/ShortcutsCollector.java +++ b/platform/platform-impl/src/com/intellij/internal/statistic/ShortcutsCollector.java @@ -38,7 +38,7 @@ import java.util.Set; * @author Konstantin Bulenkov */ @State( - name = "ToolbarClicksCollector", + name = "ShortcutsCollector", storages = @Storage(value = "statistics.shortcuts.xml", roamingType = RoamingType.DISABLED) ) public class ShortcutsCollector implements PersistentStateComponent { From 3e4055d6cec234ccc3ec6b99686902b58f98e586 Mon Sep 17 00:00:00 2001 From: Sergey Prigogin Date: Mon, 5 Jun 2017 11:17:23 +0200 Subject: [PATCH 106/136] IDEA-173861 Format string "%d%" is not flagged as invalid --- .../src/com/siyeh/ig/bugs/FormatDecode.java | 2 +- .../malformed_format_string/MalformedFormatString.java | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/FormatDecode.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/FormatDecode.java index 28cdf3b653c3..3de143f98144 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/FormatDecode.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/FormatDecode.java @@ -248,7 +248,7 @@ class FormatDecode { } storeValidator(allowed, pos, parameters, argumentCount); } - if (i < formatString.length() - 1) { + if (i < formatString.length()) { checkText(formatString.substring(i)); } diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/malformed_format_string/MalformedFormatString.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/malformed_format_string/MalformedFormatString.java index ee50bbfe8df2..951cd1c10cdb 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/malformed_format_string/MalformedFormatString.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/malformed_format_string/MalformedFormatString.java @@ -7,16 +7,15 @@ import java.util.Formattable; public class MalformedFormatString { - public void foo() - { - String.format("%", 3.0); - System.out.printf("%", 3.0); + public void foo() { + String.format("%%", 3.0); + System.out.printf("%s", 3.0, 2.0); System.out.printf("%q", 3.0); System.out.printf("%d", 3.0); System.out.printf(new Locale(""),"%d%s", 3.0, "foo"); } - public static void main(String[] args) { + public static void main(String[] args) { String local = "hmm"; String good = String.format("%s %s", 1, 2); // this is valid according to the inspector (correct) @@ -60,6 +59,7 @@ public class MalformedFormatString { void badStrings() { // bad format specifier String.format("%) %n"); + String.format("%d%", 1); // flags on newline not allowed String.format("%(n"); From bec31c50f6236a3e4712418ff57ffffdda304103 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Mon, 5 Jun 2017 12:11:43 +0200 Subject: [PATCH 107/136] RegExp: some lambdification --- .../psi/impl/RegExpNamedGroupRefImpl.java | 25 +++++-------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/RegExpSupport/src/org/intellij/lang/regexp/psi/impl/RegExpNamedGroupRefImpl.java b/RegExpSupport/src/org/intellij/lang/regexp/psi/impl/RegExpNamedGroupRefImpl.java index 634e069bbbf0..f793abc807ab 100644 --- a/RegExpSupport/src/org/intellij/lang/regexp/psi/impl/RegExpNamedGroupRefImpl.java +++ b/RegExpSupport/src/org/intellij/lang/regexp/psi/impl/RegExpNamedGroupRefImpl.java @@ -22,7 +22,6 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiReference; import com.intellij.psi.search.PsiElementProcessor; import com.intellij.psi.tree.TokenSet; -import com.intellij.psi.util.PsiElementFilter; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.IncorrectOperationException; import org.intellij.lang.regexp.RegExpTT; @@ -53,15 +52,12 @@ public class RegExpNamedGroupRefImpl extends RegExpElementImpl implements RegExp @Nullable public RegExpGroup resolve() { final PsiElementProcessor.FindFilteredElement processor = new PsiElementProcessor.FindFilteredElement<>( - new PsiElementFilter() { - @Override - public boolean isAccepted(PsiElement element) { - if (!(element instanceof RegExpGroup)) { - return false; - } - final RegExpGroup group = (RegExpGroup)element; - return group.isAnyNamedGroup() && Comparing.equal(getGroupName(), group.getGroupName()); + element -> { + if (!(element instanceof RegExpGroup)) { + return false; } + final RegExpGroup group = (RegExpGroup)element; + return group.isAnyNamedGroup() && Comparing.equal(getGroupName(), group.getGroupName()); } ); PsiTreeUtil.processElements(getContainingFile(), processor); @@ -142,16 +138,7 @@ public class RegExpNamedGroupRefImpl extends RegExpElementImpl implements RegExp @NotNull public Object[] getVariants() { final PsiElementProcessor.CollectFilteredElements processor = new PsiElementProcessor.CollectFilteredElements<>( - new PsiElementFilter() { - @Override - public boolean isAccepted(PsiElement element) { - if (!(element instanceof RegExpGroup)) { - return false; - } - final RegExpGroup regExpGroup = (RegExpGroup)element; - return regExpGroup.isAnyNamedGroup(); - } - } + e -> e instanceof RegExpGroup && ((RegExpGroup)e).isAnyNamedGroup() ); PsiTreeUtil.processElements(getContainingFile(), processor); return processor.toArray(); From 751d7b6aafd93df67071568ae2c9f6930ed1875d Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Mon, 5 Jun 2017 17:29:42 +0200 Subject: [PATCH 108/136] RegExp: report already defined group name --- .../lang/regexp/validation/RegExpAnnotator.java | 12 ++++++++++++ .../java/codeInsight/RegExpHighlightingTest.java | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/RegExpSupport/src/org/intellij/lang/regexp/validation/RegExpAnnotator.java b/RegExpSupport/src/org/intellij/lang/regexp/validation/RegExpAnnotator.java index c4f04f7213b5..aff8abcc2673 100644 --- a/RegExpSupport/src/org/intellij/lang/regexp/validation/RegExpAnnotator.java +++ b/RegExpSupport/src/org/intellij/lang/regexp/validation/RegExpAnnotator.java @@ -20,7 +20,9 @@ import com.intellij.codeInspection.ProblemHighlightType; import com.intellij.lang.ASTNode; import com.intellij.lang.annotation.Annotation; import com.intellij.lang.annotation.AnnotationHolder; +import com.intellij.lang.annotation.AnnotationSession; import com.intellij.lang.annotation.Annotator; +import com.intellij.openapi.util.Key; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiComment; import com.intellij.psi.PsiElement; @@ -35,7 +37,9 @@ import org.intellij.lang.regexp.psi.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Set; public final class RegExpAnnotator extends RegExpElementVisitor implements Annotator { @@ -43,6 +47,7 @@ public final class RegExpAnnotator extends RegExpElementVisitor implements Annot "alnum", "alpha", "ascii", "blank", "cntrl", "digit", "graph", "lower", "print", "punct", "space", "upper", "word", "xdigit"); private AnnotationHolder myHolder; private final RegExpLanguageHosts myLanguageHosts; + private final Key> NAMED_GROUP_MAP = new Key<>("REG_EXP_NAMED_GROUP_MAP"); public RegExpAnnotator() { myLanguageHosts = RegExpLanguageHosts.getInstance(); @@ -288,6 +293,13 @@ public final class RegExpAnnotator extends RegExpElementVisitor implements Annot final ASTNode node = group.getNode().findChildByType(RegExpTT.NAME); if (node != null) myHolder.createErrorAnnotation(node, "Invalid group name"); } + final AnnotationSession session = myHolder.getCurrentAnnotationSession(); + final Map namedGroups = NAMED_GROUP_MAP.get(session, new HashMap<>()); + if (namedGroups.isEmpty()) session.putUserData(NAMED_GROUP_MAP, namedGroups); + if (namedGroups.put(name, group) != null) { + final ASTNode node = group.getNode().findChildByType(RegExpTT.NAME); + if (node != null) myHolder.createErrorAnnotation(node, "Group with name '" + name + "' already defined"); + } final RegExpGroup.Type groupType = group.getType(); if (groupType == RegExpGroup.Type.POSITIVE_LOOKBEHIND || groupType == RegExpGroup.Type.NEGATIVE_LOOKBEHIND) { final RegExpLanguageHost.Lookbehind support = myLanguageHosts.supportsLookbehind(group); diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/RegExpHighlightingTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/RegExpHighlightingTest.java index 137b1c9c6b2c..a6165474f638 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/RegExpHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/RegExpHighlightingTest.java @@ -31,6 +31,10 @@ import org.jetbrains.annotations.NotNull; @SuppressWarnings("Annotator") public class RegExpHighlightingTest extends LightCodeInsightFixtureTestCase { + public void testDuplicateNamedGroup() { + doTest("(?abc)(?<name>xyz)"); + } + public void testAnonymousCapturingGroupInspection() { myFixture.enableInspections(new AnonymousGroupInspection()); doTest("(moo)\\1"); From f2dff6085b1ae027b2e4021956a05057b2d4a13b Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Tue, 6 Jun 2017 08:26:51 +0200 Subject: [PATCH 109/136] IG: "Assignment to 'null'" -> "'null' assignment" --- .../src/com/siyeh/InspectionGadgetsBundle.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties index d067787d0e95..00491cad2657 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties @@ -45,8 +45,8 @@ equals.called.on.array.display.name='equals()' called on array equals.called.on.array.problem.descriptor=#ref() between arrays should probably be 'Arrays.equals()' #loc replace.with.arrays.equals=Replace with 'Arrays.equals()' replace.with.arrays.deep.equals=Replace with 'Arrays.deepEquals()' -assignment.to.null.display.name=Assignment to 'null' -assignment.to.null.problem.descriptor=Assignment of variable #ref to 'null' #loc +assignment.to.null.display.name='null' assignment +assignment.to.null.problem.descriptor='null' assigned to variable #ref #loc assignment.to.null.option=Ignore assignments to fields assignment.to.static.field.from.instance.method.display.name=Assignment to static field from instance context assignment.to.static.field.from.instance.method.problem.descriptor=Assignment to static field #ref from instance context #loc From ebe81ca2c650344414912c73a2c1bda714bf4deb Mon Sep 17 00:00:00 2001 From: Alexander Lobas Date: Tue, 6 Jun 2017 11:24:31 +0300 Subject: [PATCH 110/136] Fix corner cases with "not merge" bug --- .../impl/NotificationsManagerImpl.java | 6 ++---- .../src/com/intellij/ui/BalloonLayoutImpl.java | 18 ++---------------- 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/notification/impl/NotificationsManagerImpl.java b/platform/platform-impl/src/com/intellij/notification/impl/NotificationsManagerImpl.java index 1e688f143a56..a98262b8a1a8 100644 --- a/platform/platform-impl/src/com/intellij/notification/impl/NotificationsManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/notification/impl/NotificationsManagerImpl.java @@ -325,10 +325,8 @@ public class NotificationsManagerImpl extends NotificationsManager { @NotNull Disposable parentDisposable) { final BalloonLayoutData layoutData = layoutDataRef.isNull() ? new BalloonLayoutData() : layoutDataRef.get(); if (layoutData.groupId == null) { - if (NotificationsConfigurationImpl.getSettings(notification.getGroupId()).isShouldLog()) { - layoutData.groupId = notification.getGroupId(); - layoutData.id = notification.id; - } + layoutData.groupId = notification.getGroupId(); + layoutData.id = notification.id; } else { layoutData.groupId = null; diff --git a/platform/platform-impl/src/com/intellij/ui/BalloonLayoutImpl.java b/platform/platform-impl/src/com/intellij/ui/BalloonLayoutImpl.java index 694b7af17f31..55186d044540 100644 --- a/platform/platform-impl/src/com/intellij/ui/BalloonLayoutImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/BalloonLayoutImpl.java @@ -134,22 +134,8 @@ public class BalloonLayoutImpl implements BalloonLayout { ApplicationManager.getApplication().assertIsDispatchThread(); Balloon merge = merge(layoutData); if (merge == null) { - if (getVisibleCount() > 0 && layoutData instanceof BalloonLayoutData && ((BalloonLayoutData)layoutData).groupId != null) { - int index = -1; - int count = 0; - for (int i = 0, size = myBalloons.size(); i < size; i++) { - BalloonLayoutData ld = myLayoutData.get(myBalloons.get(i)); - if (ld != null && ld.groupId != null) { - if (index == -1) { - index = i; - } - count++; - } - } - - if (count > 0 && count == getVisibleCount()) { - remove(myBalloons.get(index)); - } + if (!myBalloons.isEmpty() && myBalloons.size() == getVisibleCount()) { + remove(myBalloons.get(0)); } myBalloons.add(balloon); } From 8d97e86da3e5ac3d688d2139dce459e70b9bb54a Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Tue, 6 Jun 2017 11:28:53 +0300 Subject: [PATCH 111/136] measure breakpoints overhead --- .../intellij/debugger/ui/OverheadTimings.java | 48 +++++++++++++++++ .../debugger/ui/breakpoints/Breakpoint.java | 54 +++++++++++-------- 2 files changed, 79 insertions(+), 23 deletions(-) create mode 100644 java/debugger/impl/src/com/intellij/debugger/ui/OverheadTimings.java diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/OverheadTimings.java b/java/debugger/impl/src/com/intellij/debugger/ui/OverheadTimings.java new file mode 100644 index 000000000000..501962e2cf1d --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/ui/OverheadTimings.java @@ -0,0 +1,48 @@ +/* + * Copyright 2000-2017 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.debugger.ui; + +import com.intellij.debugger.engine.DebugProcessImpl; +import com.intellij.openapi.util.Key; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author egor + */ +public class OverheadTimings { + public static final Key KEY = Key.create("OVERHEAD_TIMINGS"); + + private final Map myMap = new ConcurrentHashMap<>(); + + public static float get(DebugProcessImpl process, Object producer) { + return getTimings(process).myMap.get(producer); + } + + public static void add(DebugProcessImpl process, Object producer, long overhead) { + getTimings(process).myMap.merge(producer, overhead, (old, value) -> old + value); + } + + private static OverheadTimings getTimings(DebugProcessImpl process) { + OverheadTimings data = process.getUserData(KEY); + if (data == null) { + data = new OverheadTimings(); + process.putUserData(KEY, data); + } + return data; + } +} diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/Breakpoint.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/Breakpoint.java index eae7bf755316..9d001083a62c 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/Breakpoint.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/Breakpoint.java @@ -34,6 +34,7 @@ import com.intellij.debugger.requests.ClassPrepareRequestor; import com.intellij.debugger.requests.Requestor; import com.intellij.debugger.settings.DebuggerSettings; import com.intellij.debugger.ui.impl.watch.CompilingEvaluatorImpl; +import com.intellij.debugger.ui.OverheadTimings; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.project.Project; @@ -222,40 +223,47 @@ public abstract class Breakpoint

implements @Override public boolean processLocatableEvent(SuspendContextCommandImpl action, LocatableEvent event) throws EventProcessingException { + long start = System.currentTimeMillis(); + SuspendContextImpl context = action.getSuspendContext(); if (!isValid()) { context.getDebugProcess().getRequestsManager().deleteRequest(this); return false; } - String title = DebuggerBundle.message("title.error.evaluating.breakpoint.condition"); - try { - StackFrameProxyImpl frameProxy = context.getThread().frame(0); - if (frameProxy == null) { - // might be if the thread has been collected - return false; + String title = DebuggerBundle.message("title.error.evaluating.breakpoint.condition"); + + try { + StackFrameProxyImpl frameProxy = context.getThread().frame(0); + if (frameProxy == null) { + // might be if the thread has been collected + return false; + } + + EvaluationContextImpl evaluationContext = new EvaluationContextImpl(context, frameProxy, getThisObject(context, event)); + + if (!evaluateCondition(evaluationContext, event)) { + return false; + } + + title = DebuggerBundle.message("title.error.evaluating.breakpoint.action"); + runAction(evaluationContext, event); + } + catch (final EvaluateException ex) { + if (ApplicationManager.getApplication().isUnitTestMode()) { + System.out.println(ex.getMessage()); + return false; + } + + throw new EventProcessingException(title, ex.getMessage(), ex); } - EvaluationContextImpl evaluationContext = new EvaluationContextImpl(context, frameProxy, getThisObject(context, event)); - - if (!evaluateCondition(evaluationContext, event)) { - return false; - } - - title = DebuggerBundle.message("title.error.evaluating.breakpoint.action"); - runAction(evaluationContext, event); + return true; } - catch (final EvaluateException ex) { - if(ApplicationManager.getApplication().isUnitTestMode()) { - System.out.println(ex.getMessage()); - return false; - } - - throw new EventProcessingException(title, ex.getMessage(), ex); + finally { + OverheadTimings.add(context.getDebugProcess(), this, System.currentTimeMillis() - start); } - - return true; } private void runAction(EvaluationContextImpl context, LocatableEvent event) { From bcdccd315a7a619edc042f17be250b0036919211 Mon Sep 17 00:00:00 2001 From: Alexandr Evstigneev Date: Tue, 6 Jun 2017 11:29:00 +0300 Subject: [PATCH 112/136] isAbsolute is now public. May be necessary for proxying indentions by wrappers --- platform/lang-impl/src/com/intellij/formatting/IndentImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/formatting/IndentImpl.java b/platform/lang-impl/src/com/intellij/formatting/IndentImpl.java index 19287539275c..8db1e77d0ed4 100644 --- a/platform/lang-impl/src/com/intellij/formatting/IndentImpl.java +++ b/platform/lang-impl/src/com/intellij/formatting/IndentImpl.java @@ -50,7 +50,7 @@ public class IndentImpl extends Indent { /** * @return {@code 'isAbsolute'} property value as defined during {@link IndentImpl} object construction */ - boolean isAbsolute(){ + public boolean isAbsolute() { return myIsAbsolute; } From 022f8f79b60f6fc134a54cf9d4538b481433894d Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Tue, 6 Jun 2017 10:34:54 +0200 Subject: [PATCH 113/136] IJ trunk now targets 2017.3 --- community-resources/src/idea/IdeaApplicationInfo.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/community-resources/src/idea/IdeaApplicationInfo.xml b/community-resources/src/idea/IdeaApplicationInfo.xml index 37c211591ec1..c032b6e579f4 100644 --- a/community-resources/src/idea/IdeaApplicationInfo.xml +++ b/community-resources/src/idea/IdeaApplicationInfo.xml @@ -1,7 +1,7 @@ - + From 6dc7596e56d5296a1d93cddb41dcc0c48dac1cdb Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Tue, 6 Jun 2017 10:31:14 +0200 Subject: [PATCH 114/136] IG: fix test --- .../assignment/assignment_to_null/AssignmentToNull.java | 6 +++--- .../siyeh/ig/assignment/AssignmentToNullInspectionTest.java | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/assignment/assignment_to_null/AssignmentToNull.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/assignment/assignment_to_null/AssignmentToNull.java index 2a0d001d5bea..eb579752830e 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/assignment/assignment_to_null/AssignmentToNull.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/assignment/assignment_to_null/AssignmentToNull.java @@ -7,7 +7,7 @@ public class AssignmentToNull public static void main(String[] args) { new AssignmentToNull(new Object()).bar(); - args[0] = null; + args[0] = null; } public AssignmentToNull(Object foo) @@ -19,8 +19,8 @@ public class AssignmentToNull { Object foo = new Object(); System.out.println("foo = " + foo); - foo = null; - m_foo = null; + foo = null; + m_foo = null; System.out.println("foo = " + foo); System.out.println("m_foo = " + m_foo); } diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/assignment/AssignmentToNullInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/assignment/AssignmentToNullInspectionTest.java index c43e4501f47e..13e64e321b9e 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/assignment/AssignmentToNullInspectionTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/assignment/AssignmentToNullInspectionTest.java @@ -17,7 +17,6 @@ package com.siyeh.ig.assignment; import com.intellij.codeInspection.InspectionProfileEntry; import com.siyeh.ig.LightInspectionTestCase; -import junit.framework.TestCase; import org.jetbrains.annotations.Nullable; /** From 547abc95e530acc6bab115ab11f3dfb5ed20cd54 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Tue, 6 Jun 2017 10:36:21 +0200 Subject: [PATCH 115/136] IG: leave cast alone when it is necessary (IDEA-173430) --- .../psi/util/PsiConcatenationUtil.java | 39 ++++++++++-- ...oncatenationWithFormatStringIntention.java | 18 +++--- ...tenationWithFormatStringIntentionTest.java | 61 +++++++++++++++++++ 3 files changed, 103 insertions(+), 15 deletions(-) create mode 100644 plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/concatenation/ReplaceConcatenationWithFormatStringIntentionTest.java diff --git a/java/java-psi-api/src/com/intellij/psi/util/PsiConcatenationUtil.java b/java/java-psi-api/src/com/intellij/psi/util/PsiConcatenationUtil.java index c7fcf5c8e122..ad5b90e58150 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/PsiConcatenationUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/PsiConcatenationUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -18,7 +18,7 @@ package com.intellij.psi.util; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.Nullable; import java.util.List; @@ -102,8 +102,8 @@ public class PsiConcatenationUtil { formatParameters.add(getBoxedArgument(expression)); } - private static PsiExpression getBoxedArgument(PsiExpression arg) throws IncorrectOperationException { - arg = PsiUtil.deparenthesizeExpression(arg); + private static PsiExpression getBoxedArgument(PsiExpression arg) { + arg = unwrapExpression(arg); assert arg != null; if (PsiUtil.isLanguageLevel5OrHigher(arg)) { return arg; @@ -130,4 +130,35 @@ public class PsiConcatenationUtil { return newExpr; } + @Nullable + private static PsiExpression unwrapExpression(PsiExpression expression) { + while (true) { + if (expression instanceof PsiParenthesizedExpression) { + expression = ((PsiParenthesizedExpression)expression).getExpression(); + continue; + } + if (expression instanceof PsiTypeCastExpression) { + final PsiTypeCastExpression typeCastExpression = (PsiTypeCastExpression)expression; + final PsiType castType = typeCastExpression.getType(); + if (TypeConversionUtil.isNumericType(castType)) { + final PsiExpression operand = typeCastExpression.getOperand(); + if (operand == null) { + return expression; + } + final PsiType operandType = operand.getType(); + if (operandType == null) { + return expression; + } + final int castRank = TypeConversionUtil.getTypeRank(castType); + final int operandRank = TypeConversionUtil.getTypeRank(operandType); + if (castRank < operandRank || castRank == TypeConversionUtil.CHAR_RANK && operandRank != castRank) { + return expression; + } + } + expression = typeCastExpression.getOperand(); + continue; + } + return expression; + } + } } diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/ReplaceConcatenationWithFormatStringIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/ReplaceConcatenationWithFormatStringIntention.java index dc2a3880f9db..af23015cfa15 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/ReplaceConcatenationWithFormatStringIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/ReplaceConcatenationWithFormatStringIntention.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2015 Bas Leijdekkers + * Copyright 2008-2017 Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,6 @@ package com.siyeh.ipp.concatenation; import com.intellij.psi.*; import com.intellij.psi.util.PsiConcatenationUtil; -import com.intellij.util.IncorrectOperationException; import com.siyeh.ig.PsiReplacementUtil; import com.siyeh.ig.psiutils.ExpressionUtils; import com.siyeh.ipp.base.Intention; @@ -36,7 +35,7 @@ public class ReplaceConcatenationWithFormatStringIntention extends Intention { } @Override - protected void processIntention(@NotNull PsiElement element) throws IncorrectOperationException { + protected void processIntention(@NotNull PsiElement element) { PsiPolyadicExpression expression = (PsiPolyadicExpression)element; PsiElement parent = expression.getParent(); while (ExpressionUtils.isConcatenation(parent)) { @@ -44,7 +43,7 @@ public class ReplaceConcatenationWithFormatStringIntention extends Intention { parent = expression.getParent(); } final StringBuilder formatString = new StringBuilder(); - final List formatParameters = new ArrayList(); + final List formatParameters = new ArrayList<>(); PsiConcatenationUtil.buildFormatString(expression, formatString, formatParameters, true); if (replaceWithPrintfExpression(expression, formatString, formatParameters)) { return; @@ -62,7 +61,7 @@ public class ReplaceConcatenationWithFormatStringIntention extends Intention { } private static boolean replaceWithPrintfExpression(PsiExpression expression, CharSequence formatString, - List formatParameters) throws IncorrectOperationException { + List formatParameters) { final PsiElement expressionParent = expression.getParent(); if (!(expressionParent instanceof PsiExpressionList)) { return false; @@ -100,18 +99,15 @@ public class ReplaceConcatenationWithFormatStringIntention extends Intention { final StringBuilder newExpression = new StringBuilder(); final PsiExpression qualifier = methodExpression.getQualifierExpression(); if (qualifier != null) { - newExpression.append(qualifier.getText()); - newExpression.append('.'); + newExpression.append(qualifier.getText()).append('.'); } - newExpression.append("printf(\""); - newExpression.append(formatString); + newExpression.append("printf(\"").append(formatString); if (insertNewline) { newExpression.append("%n"); } newExpression.append('\"'); for (PsiExpression formatParameter : formatParameters) { - newExpression.append(", "); - newExpression.append(formatParameter.getText()); + newExpression.append(", ").append(formatParameter.getText()); } newExpression.append(')'); PsiReplacementUtil.replaceExpression(methodCallExpression, newExpression.toString()); diff --git a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/concatenation/ReplaceConcatenationWithFormatStringIntentionTest.java b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/concatenation/ReplaceConcatenationWithFormatStringIntentionTest.java new file mode 100644 index 000000000000..20d2035b6ae8 --- /dev/null +++ b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/concatenation/ReplaceConcatenationWithFormatStringIntentionTest.java @@ -0,0 +1,61 @@ +/* + * Copyright 2000-2017 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.siyeh.ipp.concatenation; + +import com.siyeh.ipp.IPPTestCase; +import junit.framework.TestCase; + +/** + * @author Bas Leijdekkers + */ +public class ReplaceConcatenationWithFormatStringIntentionTest extends IPPTestCase { + + public void testNarrowingCast() { + doTest("class X {" + + " String s = (byte)321 +/*_Replace '+' with 'String.format()'*/ \" parsecs\";" + + "}", + + "class X {" + + " String s = String.format(\"%s parsecs\", (byte) 321);" + + "}" + ); + } + + public void testWideningCast() { + doTest("class X {" + + " String s = (long)42 /*_Replace '+' with 'String.format()'*/+ \" the answer to life, the universe and everything\";" + + "}", + + "class X {" + + " String s = String.format(\"%d the answer to life, the universe and everything\", 42);" + + "}"); + } + + public void testCastToChar() { + doTest("class X {" + + " String deepThought(byte b) {" + + " return (char)b/*_Replace '+' with 'String.format()'*/ + \" the answer to life, the universe and everything\";" + + " }" + + "}", + + "class X {" + + " String deepThought(byte b) {" + + " return String.format(\"%s the answer to life, the universe and everything\", (char) b);" + + " }" + + "}"); + } + +} \ No newline at end of file From aea101abf11709fd082c58c0b42b474e94b632df Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Tue, 6 Jun 2017 10:55:56 +0200 Subject: [PATCH 116/136] hide user paths in stats for VM options --- .../internal/statistic/JdkSettingsUsageCollector.kt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/JdkSettingsUsageCollector.kt b/platform/platform-impl/src/com/intellij/internal/statistic/JdkSettingsUsageCollector.kt index 81e268480da9..aab4a81d85b7 100644 --- a/platform/platform-impl/src/com/intellij/internal/statistic/JdkSettingsUsageCollector.kt +++ b/platform/platform-impl/src/com/intellij/internal/statistic/JdkSettingsUsageCollector.kt @@ -25,10 +25,23 @@ import java.lang.management.ManagementFactory class JdkSettingsUsageCollector: UsagesCollector() { override fun getUsages(): Set { return ManagementFactory.getRuntimeMXBean().inputArguments + .map { s -> hideUserPath(s) } .map { s -> UsageDescriptor(s) } .toSet() } + val keysWithPath = arrayOf("-Didea.home.path", "-Didea.launcher.bin.path", "-Didea.plugins.path", "-Xbootclasspath", + "-Djb.vmOptionsFile", "-XX ErrorFile", "-XX HeapDumpPath", " -Didea.launcher.bin.path", "-agentlib:jdwp") + + private fun hideUserPath(key: String): String { + @Suppress("LoopToCallChain") + for (s in keysWithPath) { + if (key.startsWith(s)) return "$s ..." + } + + return key + } + override fun getGroupId(): GroupDescriptor { return GroupDescriptor.create("user.jdk.settings") } From 8d59ee08704211f4c7c98cf9b237acdb517378da Mon Sep 17 00:00:00 2001 From: "Vladislav.Soroka" Date: Tue, 6 Jun 2017 12:07:37 +0300 Subject: [PATCH 117/136] IDEA-173957 IDEA 2017.2 EAP detects 'compile' dependency in a Gradle project as 'provided' --- .../GradleDependenciesImportingTest.java | 33 +++++++++++++++++++ .../util/DependencyResolverImpl.groovy | 19 ++++++----- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleDependenciesImportingTest.java b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleDependenciesImportingTest.java index 25c1382e4555..599cb8c84d35 100644 --- a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleDependenciesImportingTest.java +++ b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleDependenciesImportingTest.java @@ -978,6 +978,39 @@ public class GradleDependenciesImportingTest extends GradleImportingTestCase { } } + @Test + @TargetVersions("2.12+") + public void testCompileOnlyAndCompileScope() throws Exception { + createSettingsFile("include 'app'\n"); + importProject( + "apply plugin: 'java'\n" + + "dependencies {\n" + + " compileOnly project(':app')\n" + + " compile 'junit:junit:4.11'\n" + + "}\n" + + "project(':app') {\n" + + " apply plugin: 'java'\n" + + " repositories {\n" + + " mavenCentral()\n" + + " }\n" + + " dependencies {\n" + + " compile 'junit:junit:4.11'\n" + + " }\n" + + "}" + ); + + assertModules("project", "project_main", "project_test", "app", "app_main", "app_test"); + + assertModuleModuleDepScope("project_main", "app_main", DependencyScope.PROVIDED); + assertModuleLibDepScope("project_main", "Gradle: org.hamcrest:hamcrest-core:1.3", DependencyScope.COMPILE); + assertModuleLibDepScope("project_main", "Gradle: junit:junit:4.11", DependencyScope.COMPILE); + + assertModuleModuleDeps("project_test", "project_main"); + assertModuleModuleDepScope("project_test", "project_main", DependencyScope.COMPILE); + assertModuleLibDepScope("project_test", "Gradle: junit:junit:4.11", DependencyScope.COMPILE); + assertModuleLibDepScope("project_test", "Gradle: org.hamcrest:hamcrest-core:1.3", DependencyScope.COMPILE); + } + @Test @TargetVersions("3.4+") public void testJavaLibraryPluginConfigurations() throws Exception { diff --git a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/util/DependencyResolverImpl.groovy b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/util/DependencyResolverImpl.groovy index f8fb31faf26d..2af733b27af4 100644 --- a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/util/DependencyResolverImpl.groovy +++ b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/util/DependencyResolverImpl.groovy @@ -32,7 +32,6 @@ import org.gradle.api.artifacts.component.ModuleComponentIdentifier import org.gradle.api.artifacts.component.ModuleComponentSelector import org.gradle.api.artifacts.component.ProjectComponentIdentifier import org.gradle.api.artifacts.component.ProjectComponentSelector -import org.gradle.api.artifacts.dsl.DependencyHandler import org.gradle.api.artifacts.result.* import org.gradle.api.plugins.WarPlugin import org.gradle.api.specs.Specs @@ -44,6 +43,7 @@ import org.gradle.api.tasks.compile.AbstractCompile import org.gradle.language.base.artifact.SourcesArtifact import org.gradle.language.java.artifact.JavadocArtifact import org.gradle.plugins.ide.idea.IdeaPlugin +import org.gradle.util.GUtil import org.gradle.util.GradleVersion import org.jetbrains.annotations.NotNull import org.jetbrains.annotations.Nullable @@ -210,6 +210,9 @@ class DependencyResolverImpl implements DependencyResolver { Collection result = new ArrayList<>() // resolve compile dependencies + def isMainSourceSet = sourceSet.name == SourceSet.MAIN_SOURCE_SET_NAME + String deprecatedCompileConfigurationName = isMainSourceSet ? "compile" : GUtil.toCamelCase(sourceSet.name) + "Compile" + def deprecatedCompileConfiguration = myProject.configurations.findByName(deprecatedCompileConfigurationName) def compileConfigurationName = sourceSet.compileConfigurationName def compileClasspathConfiguration = myProject.configurations.findByName(compileConfigurationName + 'Classpath') def originCompileConfiguration = myProject.configurations.findByName(compileConfigurationName) @@ -238,17 +241,17 @@ class DependencyResolverImpl implements DependencyResolver { // since version 3.4 compileOnly no longer extends compile // so, we can use compileOnly configuration for the check + Object[] resolvedObjArray = resolvedObj instanceof Collection ? ((Collection)resolvedObj).toArray() : [resolvedObj] if (isJavaLibraryPluginSupported) { - if (compileOnlyConfiguration != null && - (resolvedObj instanceof Collection ? compileOnlyConfiguration.containsAll(((Collection)resolvedObj).toArray()) : - compileOnlyConfiguration.contains(resolvedObj))) { - ((AbstractExternalDependency)it).scope = providedScope + if (compileOnlyConfiguration != null && compileOnlyConfiguration.containsAll(resolvedObjArray)) { + // deprecated 'compile' configuration still can be used + if (deprecatedCompileConfiguration == null || !deprecatedCompileConfiguration.containsAll(resolvedObjArray)) { + ((AbstractExternalDependency)it).scope = providedScope + } } } else { - if (checkCompileOnlyDeps && - (resolvedObj instanceof Collection ? !originCompileConfiguration.containsAll(((Collection)resolvedObj).toArray()) : - !originCompileConfiguration.contains(resolvedObj))) { + if (checkCompileOnlyDeps && !originCompileConfiguration.containsAll(resolvedObjArray)) { ((AbstractExternalDependency)it).scope = providedScope } } From f5d4c81dfcdd7f760194c22f6d2a347828cee4ba Mon Sep 17 00:00:00 2001 From: "Liana.Bakradze" Date: Tue, 6 Jun 2017 12:12:00 +0300 Subject: [PATCH 118/136] EDU-970 Unreadable text in Add/Edit Answer placeholder dialog window --- .../edu/learning/ui/CCCreateAnswerPlaceholderPanel.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/educational-core/src/com/jetbrains/edu/learning/ui/CCCreateAnswerPlaceholderPanel.java b/python/educational-core/src/com/jetbrains/edu/learning/ui/CCCreateAnswerPlaceholderPanel.java index b93c6774ced0..5dd63e2c5943 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/ui/CCCreateAnswerPlaceholderPanel.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/ui/CCCreateAnswerPlaceholderPanel.java @@ -58,7 +58,8 @@ public class CCCreateAnswerPlaceholderPanel { myHintsPanel.setBorder(BorderFactory.createLineBorder(JBColor.border())); ((GridLayoutManager)myHintsPanel.getLayout()).setHGap(1); - myHintTextArea.setFont(myPlaceholderTextArea.getFont()); + myHintTextArea.setFont(UIUtil.getLabelFont()); + myPlaceholderTextArea.setFont(UIUtil.getLabelFont()); myHintTextArea.addFocusListener(createFocusListenerToSetDefaultHintText()); actionsPanel.add(createHintToolbarComponent(), BorderLayout.WEST); From 66ffd15c1c13011ee8aae567fd0502e8e9500526 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Mon, 5 Jun 2017 15:39:05 +0300 Subject: [PATCH 119/136] javafx switcher available on welcome screen. --- .../jetbrains/edu/learning/StudySettings.java | 9 +++ .../edu/learning/StudyTaskManager.java | 9 --- .../actions/StudySwitchTaskPanelAction.kt | 69 ++++++++----------- .../jetbrains/edu/learning/ui/StudyHint.kt | 6 +- .../learning/ui/StudyToolWindowFactory.java | 6 +- 5 files changed, 43 insertions(+), 56 deletions(-) diff --git a/python/educational-core/src/com/jetbrains/edu/learning/StudySettings.java b/python/educational-core/src/com/jetbrains/edu/learning/StudySettings.java index 03351ab16fe1..637ed5d4088d 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/StudySettings.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/StudySettings.java @@ -17,6 +17,7 @@ public class StudySettings implements PersistentStateComponent { private StepicUser myUser; public long LAST_TIME_CHECKED = 0; private boolean myEnableTestingFromSamples = false; + public boolean myShouldUseJavaFx = StudyUtils.hasJavaFx(); public StudySettings() { } @@ -55,6 +56,14 @@ public class StudySettings implements PersistentStateComponent { updateStepicUserWidget(); } + public boolean shouldUseJavaFx() { + return myShouldUseJavaFx; + } + + public void setShouldUseJavaFx(boolean shouldUseJavaFx) { + this.myShouldUseJavaFx = shouldUseJavaFx; + } + private static void updateStepicUserWidget() { StudyStepicUserWidget widget = StudyUtils.getStepicWidget(); if (widget != null) { diff --git a/python/educational-core/src/com/jetbrains/edu/learning/StudyTaskManager.java b/python/educational-core/src/com/jetbrains/edu/learning/StudyTaskManager.java index 9fe00e9759d3..33692d12095f 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/StudyTaskManager.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/StudyTaskManager.java @@ -50,7 +50,6 @@ public class StudyTaskManager implements PersistentStateComponent, Dumb public final Map> myUserTests = new HashMap<>(); - public boolean myShouldUseJavaFx = StudyUtils.hasJavaFx(); private StudyToolWindow.StudyToolWindowMode myToolWindowMode = StudyToolWindow.StudyToolWindowMode.TEXT; private boolean myTurnEditingMode = false; @@ -232,14 +231,6 @@ public class StudyTaskManager implements PersistentStateComponent, Dumb return ServiceManager.getService(project, StudyTaskManager.class); } - public boolean shouldUseJavaFx() { - return myShouldUseJavaFx; - } - - public void setShouldUseJavaFx(boolean shouldUseJavaFx) { - this.myShouldUseJavaFx = shouldUseJavaFx; - } - public StudyToolWindow.StudyToolWindowMode getToolWindowMode() { return myToolWindowMode; } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/actions/StudySwitchTaskPanelAction.kt b/python/educational-core/src/com/jetbrains/edu/learning/actions/StudySwitchTaskPanelAction.kt index 449503989fd5..2b15f0bbd192 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/actions/StudySwitchTaskPanelAction.kt +++ b/python/educational-core/src/com/jetbrains/edu/learning/actions/StudySwitchTaskPanelAction.kt @@ -1,11 +1,11 @@ package com.jetbrains.edu.learning.actions +import com.intellij.openapi.actionSystem.ActionPlaces.ACTION_SEARCH import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent -import com.intellij.openapi.project.Project import com.intellij.openapi.ui.ComboBox import com.intellij.openapi.ui.DialogWrapper -import com.jetbrains.edu.learning.StudyTaskManager +import com.jetbrains.edu.learning.StudySettings import com.jetbrains.edu.learning.StudyUtils import javax.swing.DefaultComboBoxModel import javax.swing.JComponent @@ -16,45 +16,22 @@ class StudySwitchTaskPanelAction: AnAction() { override fun actionPerformed(e: AnActionEvent?) { val project = e?.project - if (project != null) { - if (createDialog(project).showAndGet()) { - StudyUtils.initToolWindows(project) - } + val result = createDialog().showAndGet() + if (result && project != null) { + StudyUtils.initToolWindows(project) } } - fun createDialog(project: Project): DialogWrapper { - return MyDialog(project, false) + fun createDialog(): DialogWrapper { + return MyDialog(false) } - - - class MyDialog: DialogWrapper { + + class MyDialog(canBeParent: Boolean) : DialogWrapper(null, canBeParent) { val JAVAFX_ITEM = "JavaFX" val SWING_ITEM = "Swing" - private val myProject: Project - private val myComboBox: ComboBox + private val myComboBox: ComboBox = ComboBox() - - constructor(project: Project, canBeParent: Boolean) : super(project, canBeParent) { - myProject = project - myComboBox = ComboBox() - val comboBoxModel = DefaultComboBoxModel() - - if (StudyUtils.hasJavaFx()) { - comboBoxModel.addElement(JAVAFX_ITEM) - } - comboBoxModel.addElement(SWING_ITEM) - - comboBoxModel.selectedItem = - if (StudyUtils.hasJavaFx() && StudyTaskManager.getInstance(project).shouldUseJavaFx()) JAVAFX_ITEM else SWING_ITEM - myComboBox.model = comboBoxModel - title = "Switch Task Description Panel" - myComboBox.setMinimumAndPreferredWidth(250) - init() - } - - - override fun createCenterPanel(): JComponent? { + override fun createCenterPanel(): JComponent? { return myComboBox } @@ -68,17 +45,27 @@ class StudySwitchTaskPanelAction: AnAction() { override fun doOKAction() { super.doOKAction() - StudyTaskManager.getInstance(myProject).setShouldUseJavaFx(myComboBox.selectedItem == JAVAFX_ITEM) + StudySettings.getInstance().setShouldUseJavaFx(myComboBox.selectedItem == JAVAFX_ITEM) + } + + init { + val comboBoxModel = DefaultComboBoxModel() + if (StudyUtils.hasJavaFx()) { + comboBoxModel.addElement(JAVAFX_ITEM) + } + comboBoxModel.addElement(SWING_ITEM) + comboBoxModel.selectedItem = + if (StudyUtils.hasJavaFx() && StudySettings.getInstance().shouldUseJavaFx()) JAVAFX_ITEM else SWING_ITEM + myComboBox.model = comboBoxModel + title = "Switch Task Description Panel" + myComboBox.setMinimumAndPreferredWidth(250) + init() } } override fun update(e: AnActionEvent?) { + val place = e?.place val project = e?.project - if (project != null && StudyUtils.isStudyProject(project)) { - e?.presentation?.isEnabled = true - } - else { - e?.presentation?.isEnabled = false - } + e?.presentation?.isEnabled = project != null && StudyUtils.isStudyProject(project) || ACTION_SEARCH == place } } \ No newline at end of file diff --git a/python/educational-core/src/com/jetbrains/edu/learning/ui/StudyHint.kt b/python/educational-core/src/com/jetbrains/edu/learning/ui/StudyHint.kt index aa213f8f16a3..01e2be6a8065 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/ui/StudyHint.kt +++ b/python/educational-core/src/com/jetbrains/edu/learning/ui/StudyHint.kt @@ -7,6 +7,7 @@ import com.intellij.openapi.actionSystem.DefaultActionGroup import com.intellij.openapi.actionSystem.Presentation import com.intellij.openapi.project.Project import com.jetbrains.edu.coursecreator.actions.CCEditHintAction +import com.jetbrains.edu.learning.StudySettings import com.jetbrains.edu.learning.StudyTaskManager import com.jetbrains.edu.learning.StudyUtils import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder @@ -25,8 +26,7 @@ open class StudyHint(private val myPlaceholder: AnswerPlaceholder?, protected var isEditingMode = false init { - val taskManager = StudyTaskManager.getInstance(myProject) - if (StudyUtils.hasJavaFx() && taskManager.shouldUseJavaFx()) { + if (StudyUtils.hasJavaFx() && StudySettings.getInstance().shouldUseJavaFx()) { studyToolWindow = StudyJavaFxToolWindow() } else { @@ -39,7 +39,7 @@ open class StudyHint(private val myPlaceholder: AnswerPlaceholder?, studyToolWindow.setActionToolbar(DefaultActionGroup()) } - val course = taskManager.course + val course = StudyTaskManager.getInstance(myProject).course if (course != null) { val group = DefaultActionGroup() val hints = myPlaceholder?.hints diff --git a/python/educational-core/src/com/jetbrains/edu/learning/ui/StudyToolWindowFactory.java b/python/educational-core/src/com/jetbrains/edu/learning/ui/StudyToolWindowFactory.java index 00fb4d35891f..a10d70e423a2 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/ui/StudyToolWindowFactory.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/ui/StudyToolWindowFactory.java @@ -7,6 +7,7 @@ import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowFactory; import com.intellij.ui.content.Content; import com.intellij.ui.content.ContentManager; +import com.jetbrains.edu.learning.StudySettings; import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.courseFormat.Course; @@ -20,11 +21,10 @@ public class StudyToolWindowFactory implements ToolWindowFactory, DumbAware { @Override public void createToolWindowContent(@NotNull final Project project, @NotNull final ToolWindow toolWindow) { toolWindow.setIcon(EducationalCoreIcons.TaskDescription); - StudyTaskManager taskManager = StudyTaskManager.getInstance(project); - final Course course = taskManager.getCourse(); + final Course course = StudyTaskManager.getInstance(project).getCourse(); if (course != null) { final StudyToolWindow studyToolWindow; - if (StudyUtils.hasJavaFx() && taskManager.shouldUseJavaFx()) { + if (StudyUtils.hasJavaFx() && StudySettings.getInstance().shouldUseJavaFx()) { studyToolWindow = new StudyJavaFxToolWindow(); } else { From a6297d3b2881f33fccbfd86407c30775a9d72f23 Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Tue, 6 Jun 2017 11:21:09 +0200 Subject: [PATCH 120/136] major version is 173 --- build.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.txt b/build.txt index 38383cb00036..7bd408d4f89c 100644 --- a/build.txt +++ b/build.txt @@ -1 +1 @@ -172.SNAPSHOT +173.SNAPSHOT From 4b16918ede52362a9e142fbe10b147b754240774 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Fri, 2 Jun 2017 14:24:31 +0300 Subject: [PATCH 121/136] [groovy] extract EmptyGroovyResolveResult object --- .../assignment/GrListOrMapInfo.java | 5 +- .../GrUnresolvedAccessChecker.java | 3 +- .../GroovyNamedArgumentProvider.java | 3 +- .../LiteralConstructorReference.java | 5 +- .../lang/psi/api/EmptyGroovyResolveResult.kt | 40 ++++++++++++++ .../lang/psi/api/GroovyResolveResult.java | 55 ++----------------- .../psi/impl/GroovyResolveResultImpl.java | 5 +- .../groovy/lang/psi/impl/PsiImplUtil.java | 3 +- .../arguments/GrArgumentLabelImpl.java | 5 +- .../plugins/groovy/lang/psi/util/PsiUtil.java | 5 +- .../resolve/delegatesTo/grDelegatesToUtil.kt | 6 +- .../convertToJava/GenerationUtil.java | 3 +- 12 files changed, 71 insertions(+), 67 deletions(-) create mode 100644 plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/EmptyGroovyResolveResult.kt diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/assignment/GrListOrMapInfo.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/assignment/GrListOrMapInfo.java index 24eb5814566b..93951d98277a 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/assignment/GrListOrMapInfo.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/assignment/GrListOrMapInfo.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 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 @@ import com.intellij.psi.PsiType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.findUsages.LiteralConstructorReference; +import org.jetbrains.plugins.groovy.lang.psi.api.EmptyGroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap; import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; @@ -116,7 +117,7 @@ public class GrListOrMapInfo implements ConstructorCallInfo { if (type == null) return GroovyResolveResult.EMPTY_ARRAY; final GroovyResolveResult result = GroovyResolveResultImpl.from(type.resolveGenerics()); - if (result == GroovyResolveResult.EMPTY_RESULT) return GroovyResolveResult.EMPTY_ARRAY; + if (result == EmptyGroovyResolveResult.INSTANCE) return GroovyResolveResult.EMPTY_ARRAY; return new GroovyResolveResult[]{result}; } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/untypedUnresolvedAccess/GrUnresolvedAccessChecker.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/untypedUnresolvedAccess/GrUnresolvedAccessChecker.java index 4d059172b88c..a50ae423a2e6 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/untypedUnresolvedAccess/GrUnresolvedAccessChecker.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/untypedUnresolvedAccess/GrUnresolvedAccessChecker.java @@ -50,6 +50,7 @@ import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement; import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; +import org.jetbrains.plugins.groovy.lang.psi.api.EmptyGroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotation; import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; @@ -389,7 +390,7 @@ public class GrUnresolvedAccessChecker { @NotNull private static GroovyResolveResult getBestResolveResult(GrReferenceExpression ref) { GroovyResolveResult[] results = ref.multiResolve(false); - if (results.length == 0) return GroovyResolveResult.EMPTY_RESULT; + if (results.length == 0) return EmptyGroovyResolveResult.INSTANCE; if (results.length == 1) return results[0]; for (GroovyResolveResult result : results) { diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/extensions/GroovyNamedArgumentProvider.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/extensions/GroovyNamedArgumentProvider.java index 77c1f975c74f..6e591c3e2771 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/extensions/GroovyNamedArgumentProvider.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/extensions/GroovyNamedArgumentProvider.java @@ -20,6 +20,7 @@ import com.intellij.psi.*; import com.intellij.psi.util.InheritanceUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.groovy.lang.psi.api.EmptyGroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable; @@ -98,7 +99,7 @@ public abstract class GroovyNamedArgumentProvider { if (callVariants.length == 0 || PsiUtil.isSingleBindingVariant(callVariants)) { for (GroovyNamedArgumentProvider namedArgumentProvider : EP_NAME.getExtensions()) { - namedArgumentProvider.getNamedArguments(call, GroovyResolveResult.EMPTY_RESULT, argumentName, forCompletion, namedArguments); + namedArgumentProvider.getNamedArguments(call, EmptyGroovyResolveResult.INSTANCE, argumentName, forCompletion, namedArguments); } } else { diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/findUsages/LiteralConstructorReference.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/findUsages/LiteralConstructorReference.java index 014ebd44aad6..2d55cc739841 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/findUsages/LiteralConstructorReference.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/findUsages/LiteralConstructorReference.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils; import org.jetbrains.plugins.groovy.lang.psi.GrControlFlowOwner; +import org.jetbrains.plugins.groovy.lang.psi.api.EmptyGroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable; @@ -190,7 +191,7 @@ public class LiteralConstructorReference extends PsiReferenceBase.Poly Date: Tue, 6 Jun 2017 13:13:28 +0300 Subject: [PATCH 122/136] IDEA-173836 After upgrade to Ubuntu 17.04 integrated menus do not work Forced jayatana for Unity DE via env variable --- bin/scripts/unix/idea.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/bin/scripts/unix/idea.sh b/bin/scripts/unix/idea.sh index 7df94672fb65..08811358451a 100755 --- a/bin/scripts/unix/idea.sh +++ b/bin/scripts/unix/idea.sh @@ -147,6 +147,17 @@ BITS=$? "$RM" -f "$VERSION_LOG" test ${BITS} -eq 0 && BITS="64" || BITS="" +#---------------------------------------------------------------------- +# Set platform enviroment variables for IDE +#---------------------------------------------------------------------- +if [ "$OS_TYPE" = "Linux" ] ; then + case "$XDG_CURRENT_DESKTOP" in + *Unity*) + export JAYATANA_FORCE=true + ;; + esac +fi + # --------------------------------------------------------------------- # Collect JVM options and IDE properties. # --------------------------------------------------------------------- From dbcddf5a4a837a7c1b05136bfa42e3c300c2e9f7 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Tue, 6 Jun 2017 17:15:35 +0700 Subject: [PATCH 123/136] BoxForComparisonInspection enhanced -> UseCompareMethodInspection Suggests to use Integer.compare(), etc. instead if ternary operator or if chain. Fixes IDEA-173766. --- .../BoxForComparisonInspection.java | 157 -------- .../UseCompareMethodInspection.java | 380 ++++++++++++++++++ .../afterBox.java | 2 +- .../afterBoxComplex.java | 2 +- .../useCompareMethod/afterIfAssign.java | 16 + .../useCompareMethod/afterIfReturn.java | 17 + .../useCompareMethod/afterTernary.java | 18 + .../beforeBox.java | 2 +- .../beforeBoxComplex.java | 2 +- .../useCompareMethod/beforeIfAssign.java | 18 + .../useCompareMethod/beforeIfReturn.java | 22 + .../useCompareMethod/beforeTernary.java | 15 + ...va => UseCompareMethodInspectionTest.java} | 8 +- .../BoxForComparison.html | 10 - .../UseCompareMethod.html | 11 + resources/src/META-INF/IdeaPlugin.xml | 4 +- 16 files changed, 507 insertions(+), 177 deletions(-) delete mode 100644 java/java-analysis-impl/src/com/intellij/codeInspection/BoxForComparisonInspection.java create mode 100644 java/java-analysis-impl/src/com/intellij/codeInspection/UseCompareMethodInspection.java rename java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/{boxForComparison => useCompareMethod}/afterBox.java (74%) rename java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/{boxForComparison => useCompareMethod}/afterBoxComplex.java (75%) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/afterIfAssign.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/afterIfReturn.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/afterTernary.java rename java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/{boxForComparison => useCompareMethod}/beforeBox.java (77%) rename java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/{boxForComparison => useCompareMethod}/beforeBoxComplex.java (75%) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/beforeIfAssign.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/beforeIfReturn.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/beforeTernary.java rename java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/{BoxForComparisonInspectionTest.java => UseCompareMethodInspectionTest.java} (82%) delete mode 100644 resources-en/src/inspectionDescriptions/BoxForComparison.html create mode 100644 resources-en/src/inspectionDescriptions/UseCompareMethod.html diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/BoxForComparisonInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/BoxForComparisonInspection.java deleted file mode 100644 index b7f5db473fe7..000000000000 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/BoxForComparisonInspection.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 2000-2017 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.codeInspection; - -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.pom.java.LanguageLevel; -import com.intellij.psi.*; -import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.psi.util.PsiUtil; -import com.siyeh.ig.psiutils.CommentTracker; -import org.jetbrains.annotations.Nls; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * @author Tagir Valeev - */ -public class BoxForComparisonInspection extends BaseJavaBatchLocalInspectionTool { - @NotNull - @Override - public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) { - if(!PsiUtil.getLanguageLevel(holder.getFile()).isAtLeast(LanguageLevel.JDK_1_4)) { - return PsiElementVisitor.EMPTY_VISITOR; - } - return new JavaElementVisitor() { - @Override - public void visitMethodCallExpression(PsiMethodCallExpression call) { - PsiElement nameElement = call.getMethodExpression().getReferenceNameElement(); - if (nameElement == null) return; - String name = nameElement.getText(); - if (!"compareTo".equals(name)) return; - PsiExpression[] args = call.getArgumentList().getExpressions(); - if (args.length != 1) return; - PsiExpression arg = args[0]; - PsiExpression qualifier = call.getMethodExpression().getQualifierExpression(); - if (qualifier == null) return; - PsiClassType boxedType = getBoxedType(call); - if (boxedType == null) return; - PsiPrimitiveType primitiveType = PsiPrimitiveType.getUnboxedType(boxedType); - if (primitiveType == null || !PsiType.DOUBLE.equals(primitiveType) && !PsiType.FLOAT.equals(primitiveType) && - !PsiUtil.isLanguageLevel7OrHigher(call)) { - return; - } - PsiExpression left = extractPrimitive(boxedType, primitiveType, qualifier); - if (left == null) return; - PsiExpression right = extractPrimitive(boxedType, primitiveType, arg); - if (right == null) return; - holder.registerProblem(nameElement, "Can be replaced with '" + boxedType.getClassName() + ".compare'", - new ReplaceWithPrimitiveCompareFix(boxedType.getCanonicalText())); - } - }; - } - - @Nullable - static PsiClassType getBoxedType(PsiMethodCallExpression call) { - PsiMethod method = call.resolveMethod(); - if (method == null) return null; - PsiClass aClass = method.getContainingClass(); - if (aClass == null) return null; - return JavaPsiFacade.getElementFactory(call.getProject()).createType(aClass); - } - - @Nullable - static PsiExpression extractPrimitive(PsiClassType type, PsiPrimitiveType primitiveType, PsiExpression expression) { - expression = PsiUtil.skipParenthesizedExprDown(expression); - if (expression == null) return null; - if (primitiveType.equals(expression.getType())) { - return expression; - } - if (expression instanceof PsiMethodCallExpression) { - PsiMethodCallExpression call = (PsiMethodCallExpression)expression; - if (!"valueOf".equals(call.getMethodExpression().getReferenceName())) return null; - PsiExpression[] args = call.getArgumentList().getExpressions(); - if (args.length != 1) return null; - PsiMethod method = call.resolveMethod(); - if (method == null || type.resolve() != method.getContainingClass()) return null; - return checkPrimitive(args[0]); - } - if (expression instanceof PsiTypeCastExpression) { - PsiTypeCastExpression cast = (PsiTypeCastExpression)expression; - if (!type.equals(cast.getType())) return null; - return checkPrimitive(cast.getOperand()); - } - if (expression instanceof PsiNewExpression) { - PsiNewExpression newExpression = (PsiNewExpression)expression; - if (!type.equals(newExpression.getType())) return null; - PsiExpressionList argumentList = newExpression.getArgumentList(); - if (argumentList == null) return null; - PsiExpression[] args = argumentList.getExpressions(); - if (args.length != 1) return null; - if (!(args[0].getType() instanceof PsiPrimitiveType)) return null; - return checkPrimitive(args[0]); - } - return null; - } - - private static PsiExpression checkPrimitive(PsiExpression expression) { - return expression != null && expression.getType() instanceof PsiPrimitiveType ? expression : null; - } - - private static class ReplaceWithPrimitiveCompareFix implements LocalQuickFix { - private String myClassName; - - public ReplaceWithPrimitiveCompareFix(String className) { - myClassName = className; - } - - @Nls - @NotNull - @Override - public String getName() { - return "Replace with '" + StringUtil.getShortName(myClassName) + ".compare'"; - } - - @Nls - @NotNull - @Override - public String getFamilyName() { - return "Replace with static 'compare' method"; - } - - @Override - public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { - PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(descriptor.getStartElement(), PsiMethodCallExpression.class); - if (call == null) return; - PsiClassType boxedType = getBoxedType(call); - if (boxedType == null) return; - PsiPrimitiveType primitiveType = PsiPrimitiveType.getUnboxedType(boxedType); - if (primitiveType == null) return; - PsiExpression qualifier = call.getMethodExpression().getQualifierExpression(); - if (qualifier == null) return; - PsiExpression[] args = call.getArgumentList().getExpressions(); - if (args.length != 1) return; - PsiExpression left = extractPrimitive(boxedType, primitiveType, qualifier); - if (left == null) return; - PsiExpression right = extractPrimitive(boxedType, primitiveType, args[0]); - if (right == null) return; - - CommentTracker ct = new CommentTracker(); - ct.replaceAndRestoreComments(call, boxedType.getCanonicalText() + ".compare(" + ct.text(left) + "," + ct.text(right) + ")"); - } - } -} diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/UseCompareMethodInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/UseCompareMethodInspection.java new file mode 100644 index 000000000000..e25ba55db6e0 --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/UseCompareMethodInspection.java @@ -0,0 +1,380 @@ +/* + * Copyright 2000-2017 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.codeInspection; + +import com.intellij.codeInsight.PsiEquivalenceUtil; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.pom.java.LanguageLevel; +import com.intellij.psi.*; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; +import com.siyeh.ig.psiutils.CommentTracker; +import com.siyeh.ig.psiutils.ControlFlowUtils; +import com.siyeh.ig.psiutils.ExpressionUtils; +import one.util.streamex.StreamEx; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.intellij.util.ObjectUtils.tryCast; + +/** + * @author Tagir Valeev + */ +public class UseCompareMethodInspection extends BaseJavaBatchLocalInspectionTool { + @NotNull + @Override + public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) { + if (!PsiUtil.getLanguageLevel(holder.getFile()).isAtLeast(LanguageLevel.JDK_1_4)) { + return PsiElementVisitor.EMPTY_VISITOR; + } + return new JavaElementVisitor() { + @Override + public void visitMethodCallExpression(PsiMethodCallExpression call) { + CompareInfo info = fromCall(call); + PsiElement nameElement = call.getMethodExpression().getReferenceNameElement(); + if (info != null && nameElement != null) { + register(info, nameElement); + } + } + + @Override + public void visitIfStatement(PsiIfStatement statement) { + CompareInfo info = fromIf(statement); + PsiElement keyword = statement.getFirstChild(); + if (info != null && keyword != null) { + register(info, keyword); + } + } + + @Override + public void visitConditionalExpression(PsiConditionalExpression expression) { + CompareInfo info = fromTernary(expression); + if (info != null) { + register(info, expression); + } + } + + private void register(CompareInfo info, PsiElement nameElement) { + holder.registerProblem(nameElement, "Can be replaced with '" + info.myClass.getClassName() + ".compare'", + new ReplaceWithPrimitiveCompareFix(info.myClass.getCanonicalText())); + } + }; + } + + private static CompareInfo fromIf(PsiIfStatement ifStatement) { + PsiExpression firstCondition = ifStatement.getCondition(); + if (firstCondition == null) return null; + PsiIfStatement elseIfStatement = tryCast(getElse(ifStatement), PsiIfStatement.class); + if (elseIfStatement == null) return null; + PsiExpression secondCondition = elseIfStatement.getCondition(); + if (secondCondition == null) return null; + PsiStatement firstStatement = ControlFlowUtils.stripBraces(ifStatement.getThenBranch()); + if (firstStatement == null) return null; + PsiStatement secondStatement = ControlFlowUtils.stripBraces(elseIfStatement.getThenBranch()); + if (secondStatement == null) return null; + PsiStatement thirdStatement = getElse(elseIfStatement); + if (thirdStatement == null) return null; + + Map result = new HashMap<>(3); + // like if(...) return 1; else if(...) return -1; return 0; + if (firstStatement instanceof PsiReturnStatement) { + if (!(secondStatement instanceof PsiReturnStatement) || !(thirdStatement instanceof PsiReturnStatement)) return null; + PsiExpression firstValue = ((PsiReturnStatement)firstStatement).getReturnValue(); + if (!storeCondition(result, firstCondition, firstValue)) return null; + if (!storeCondition(result, secondCondition, ((PsiReturnStatement)secondStatement).getReturnValue())) return null; + if (!storeCondition(result, null, ((PsiReturnStatement)thirdStatement).getReturnValue())) return null; + return fromMap(result, firstValue, firstStatement); + } + // like if(...) x = 1; else if(...) x = -1; else x = 0; + PsiAssignmentExpression assignment = ExpressionUtils.getAssignment(firstStatement); + if (assignment == null) return null; + PsiReferenceExpression ref = tryCast(assignment.getLExpression(), PsiReferenceExpression.class); + if (ref == null) return null; + PsiVariable variable = tryCast(ref.resolve(), PsiVariable.class); + if (variable == null) return null; + PsiExpression firstExpression = assignment.getRExpression(); + if (!storeCondition(result, firstCondition, firstExpression)) return null; + if (!storeCondition(result, secondCondition, ExpressionUtils.getAssignmentTo(secondStatement, variable))) return null; + if (!storeCondition(result, null, ExpressionUtils.getAssignmentTo(thirdStatement, variable))) return null; + return fromMap(result, firstExpression, assignment); + } + + private static PsiStatement getElse(PsiIfStatement ifStatement) { + PsiStatement branch = ControlFlowUtils.stripBraces(ifStatement.getElseBranch()); + if (branch != null) return branch; + PsiStatement thenBranch = ControlFlowUtils.stripBraces(ifStatement.getThenBranch()); + if (!(thenBranch instanceof PsiReturnStatement)) return null; + PsiElement next = PsiTreeUtil.skipSiblingsForward(ifStatement, PsiComment.class, PsiWhiteSpace.class); + return tryCast(next, PsiStatement.class); + } + + @Nullable + private static Map extractConditions(PsiConditionalExpression ternary) { + Map result = new HashMap<>(3); + if (!storeCondition(result, ternary.getCondition(), ternary.getThenExpression())) return null; + PsiExpression elseExpression = PsiUtil.skipParenthesizedExprDown(ternary.getElseExpression()); + if (elseExpression instanceof PsiConditionalExpression) { + Map m = extractConditions((PsiConditionalExpression)elseExpression); + if (m == null) return null; + result.putAll(m); + return result; + } + return storeCondition(result, null, elseExpression) ? result : null; + } + + @Contract("_, _, null -> false") + private static boolean storeCondition(@NotNull Map result, + @Nullable PsiExpression condition, + @Nullable PsiExpression expression) { + if (expression == null) return false; + Object thenValue = ExpressionUtils.computeConstantExpression(expression); + if (!(thenValue instanceof Integer) || Math.abs((Integer)thenValue) > 1) return false; + result.put((Integer)thenValue, condition); + return true; + } + + private static CompareInfo fromTernary(PsiConditionalExpression ternary) { + if (!PsiType.INT.equals(ternary.getType())) return null; + Map map = extractConditions(ternary); + return fromMap(map, ternary, ternary); + } + + private static CompareInfo fromMap(@Nullable Map map, + @NotNull PsiExpression expression, + @NotNull PsiElement template) { + if (map == null || map.size() != 3) { + return null; + } + PsiExpression lt = map.get(-1); + Pair ltPair = getOperands(lt, JavaTokenType.LT); + if (lt != null && ltPair == null) return null; + + PsiExpression gt = map.get(1); + Pair gtPair = getOperands(gt, JavaTokenType.GT); + if ((gt != null || ltPair == null) && gtPair == null) return null; + + if (ltPair != null && gtPair != null) { + if (!PsiEquivalenceUtil.areElementsEquivalent(ltPair.getFirst(), gtPair.getFirst())) return null; + if (!PsiEquivalenceUtil.areElementsEquivalent(ltPair.getSecond(), gtPair.getSecond())) return null; + } + Pair canonicalPair = ltPair == null ? gtPair : ltPair; + PsiType leftType = canonicalPair.getFirst().getType(); + PsiType rightType = canonicalPair.getSecond().getType(); + if (!isTypeConvertible(leftType, expression) || !leftType.equals(rightType)) return null; + + PsiExpression eq = map.get(0); + Pair eqPair = getOperands(eq, JavaTokenType.EQEQ); + if (eq != null && eqPair == null) return null; + if (eqPair != null) { + if ((!PsiEquivalenceUtil.areElementsEquivalent(canonicalPair.getFirst(), eqPair.getFirst()) || + !PsiEquivalenceUtil.areElementsEquivalent(canonicalPair.getSecond(), eqPair.getSecond())) && + (!PsiEquivalenceUtil.areElementsEquivalent(canonicalPair.getFirst(), eqPair.getSecond()) || + !PsiEquivalenceUtil.areElementsEquivalent(canonicalPair.getSecond(), eqPair.getFirst()))) { + return null; + } + } + PsiClassType boxedType = ((PsiPrimitiveType)leftType).getBoxedType(expression); + return new CompareInfo(template, expression, canonicalPair.getFirst(), canonicalPair.getSecond(), boxedType); + } + + private static Pair getOperands(PsiExpression expression, IElementType expectedToken) { + expression = PsiUtil.skipParenthesizedExprDown(expression); + if (!(expression instanceof PsiBinaryExpression)) return null; + PsiBinaryExpression binOp = (PsiBinaryExpression)expression; + PsiExpression left = PsiUtil.skipParenthesizedExprDown(binOp.getLOperand()); + PsiExpression right = PsiUtil.skipParenthesizedExprDown(binOp.getROperand()); + if (left == null || right == null) return null; + if (binOp.getOperationTokenType().equals(expectedToken)) { + return Pair.create(left, right); + } + if (expectedToken.equals(JavaTokenType.GT) && binOp.getOperationTokenType().equals(JavaTokenType.LT) || + expectedToken.equals(JavaTokenType.LT) && binOp.getOperationTokenType().equals(JavaTokenType.GT)) { + return Pair.create(right, left); + } + return null; + } + + @Nullable + static PsiClassType getBoxedType(PsiMethodCallExpression call) { + PsiMethod method = call.resolveMethod(); + if (method == null) return null; + PsiClass aClass = method.getContainingClass(); + if (aClass == null) return null; + return JavaPsiFacade.getElementFactory(call.getProject()).createType(aClass); + } + + @Nullable + static PsiExpression extractPrimitive(PsiClassType type, PsiPrimitiveType primitiveType, PsiExpression expression) { + expression = PsiUtil.skipParenthesizedExprDown(expression); + if (expression == null) return null; + if (primitiveType.equals(expression.getType())) { + return expression; + } + if (expression instanceof PsiMethodCallExpression) { + PsiMethodCallExpression call = (PsiMethodCallExpression)expression; + if (!"valueOf".equals(call.getMethodExpression().getReferenceName())) return null; + PsiExpression[] args = call.getArgumentList().getExpressions(); + if (args.length != 1) return null; + PsiMethod method = call.resolveMethod(); + if (method == null || type.resolve() != method.getContainingClass()) return null; + return checkPrimitive(args[0]); + } + if (expression instanceof PsiTypeCastExpression) { + PsiTypeCastExpression cast = (PsiTypeCastExpression)expression; + if (!type.equals(cast.getType())) return null; + return checkPrimitive(cast.getOperand()); + } + if (expression instanceof PsiNewExpression) { + PsiNewExpression newExpression = (PsiNewExpression)expression; + if (!type.equals(newExpression.getType())) return null; + PsiExpressionList argumentList = newExpression.getArgumentList(); + if (argumentList == null) return null; + PsiExpression[] args = argumentList.getExpressions(); + if (args.length != 1) return null; + if (!(args[0].getType() instanceof PsiPrimitiveType)) return null; + return checkPrimitive(args[0]); + } + return null; + } + + private static PsiExpression checkPrimitive(PsiExpression expression) { + return expression != null && expression.getType() instanceof PsiPrimitiveType ? expression : null; + } + + static class CompareInfo { + final PsiElement myTemplate; + final PsiExpression myToReplace; + final PsiExpression myLeft; + final PsiExpression myRight; + final PsiClassType myClass; + + CompareInfo(PsiElement template, + PsiExpression toReplace, + PsiExpression left, + PsiExpression right, + PsiClassType aClass) { + myTemplate = template; + myToReplace = toReplace; + myLeft = left; + myRight = right; + myClass = aClass; + } + + private void replace(PsiElement toReplace, CommentTracker ct) { + String replacement = this.myClass.getCanonicalText() + ".compare(" + ct.text(this.myLeft) + "," + ct.text(this.myRight) + ")"; + if(toReplace == myTemplate) { + ct.replaceAndRestoreComments(myToReplace, replacement); + } else { + ct.replace(myToReplace, replacement); + ct.replaceAndRestoreComments(toReplace, myTemplate); + } + } + } + + @Contract("null -> null") + private static CompareInfo fromCall(PsiMethodCallExpression call) { + if (call == null) return null; + PsiElement nameElement = call.getMethodExpression().getReferenceNameElement(); + if (nameElement == null) return null; + String name = nameElement.getText(); + if (!"compareTo".equals(name)) return null; + PsiExpression[] args = call.getArgumentList().getExpressions(); + if (args.length != 1) return null; + PsiExpression arg = args[0]; + PsiExpression qualifier = call.getMethodExpression().getQualifierExpression(); + if (qualifier == null) return null; + PsiClassType boxedType = getBoxedType(call); + if (boxedType == null) return null; + PsiPrimitiveType primitiveType = PsiPrimitiveType.getUnboxedType(boxedType); + if (!isTypeConvertible(primitiveType, call)) return null; + PsiExpression left = extractPrimitive(boxedType, primitiveType, qualifier); + if (left == null) return null; + PsiExpression right = extractPrimitive(boxedType, primitiveType, arg); + if (right == null) return null; + return new CompareInfo(call, call, left, right, boxedType); + } + + @Contract("null, _ -> false") + private static boolean isTypeConvertible(PsiType type, PsiElement context) { + return type instanceof PsiPrimitiveType && (PsiType.DOUBLE.equals(type) || + PsiType.FLOAT.equals(type) || + PsiUtil.isLanguageLevel7OrHigher(context)); + } + + private static class ReplaceWithPrimitiveCompareFix implements LocalQuickFix { + private String myClassName; + + public ReplaceWithPrimitiveCompareFix(String className) { + myClassName = className; + } + + @Nls + @NotNull + @Override + public String getName() { + return "Replace with '" + StringUtil.getShortName(myClassName) + ".compare'"; + } + + @Nls + @NotNull + @Override + public String getFamilyName() { + return "Replace with static 'compare' method"; + } + + @Override + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + PsiElement element = descriptor.getStartElement(); + PsiElement toReplace; + List toDelete = new ArrayList<>(); + CompareInfo info; + if (element instanceof PsiConditionalExpression) { + toReplace = element; + info = fromTernary((PsiConditionalExpression)element); + } + else { + PsiElement parent = element.getParent(); + if (parent instanceof PsiIfStatement) { + toReplace = parent; + info = fromIf((PsiIfStatement)parent); + PsiStatement elseIf = getElse((PsiIfStatement)parent); + toDelete.add(elseIf); + if(elseIf instanceof PsiIfStatement) { + toDelete.add(getElse((PsiIfStatement)elseIf)); + } + } else { + PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression.class); + info = fromCall(call); + toReplace = call; + } + } + if (info == null) return; + CommentTracker ct = new CommentTracker(); + info.replace(toReplace, ct); + StreamEx.of(toDelete).nonNull().filter(PsiElement::isValid).forEach(e -> new CommentTracker().deleteAndRestoreComments(e)); + } + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/boxForComparison/afterBox.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/afterBox.java similarity index 74% rename from java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/boxForComparison/afterBox.java rename to java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/afterBox.java index 3009f33c0c90..bfdff778c0b1 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/boxForComparison/afterBox.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/afterBox.java @@ -1,4 +1,4 @@ -// "Fix all 'Unnecessary boxing to compare primitives' problems in file" "true" +// "Fix all ''compare()' method can be used to compare primitives' problems in file" "true" public class Test { public void test(int a, int b) { if(Integer.compare(a, b) > 0) { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/boxForComparison/afterBoxComplex.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/afterBoxComplex.java similarity index 75% rename from java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/boxForComparison/afterBoxComplex.java rename to java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/afterBoxComplex.java index d981e9ef5252..f6d7b98cdf37 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/boxForComparison/afterBoxComplex.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/afterBoxComplex.java @@ -1,4 +1,4 @@ -// "Fix all 'Unnecessary boxing to compare primitives' problems in file" "true" +// "Fix all ''compare()' method can be used to compare primitives' problems in file" "true" public class Test { public int test(String s1, String s2) { int res = Integer.compare(s1.length(), s2.length()); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/afterIfAssign.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/afterIfAssign.java new file mode 100644 index 000000000000..019c86d4c1cc --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/afterIfAssign.java @@ -0,0 +1,16 @@ +// "Fix all ''compare()' method can be used to compare primitives' problems in file" "true" +class Test { + public void test(String s1, String s2) { + int res; + res = Integer.compare(s2.length(), s1.length()) + System.out.println(res); + } + + public void testMissingElse(String s1, String s2) { + int res; + if(s1.length() < s2.length()) res = 1; + else if(s1.length() > s2.length()) res = -1; + res = 0; + System.out.println(res); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/afterIfReturn.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/afterIfReturn.java new file mode 100644 index 000000000000..aebbc5943de9 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/afterIfReturn.java @@ -0,0 +1,17 @@ +// "Fix all ''compare()' method can be used to compare primitives' problems in file" "true" +class Test { + public int test(String s1, String s2) { + return Integer.compare(s1.length(), s2.length()); + /*otherwise bigger*/ + } + + public int test2(String s1, String s2) { + return Integer.compare(s2.length(), s1.length()); + } + + public int test3(String s1, String s2) { + if(s1.length() > s2.length()) return -1; + else if(s2.length() > s1.length()) return -1; + else return 0; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/afterTernary.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/afterTernary.java new file mode 100644 index 000000000000..b61ee932ddd3 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/afterTernary.java @@ -0,0 +1,18 @@ +// "Fix all ''compare()' method can be used to compare primitives' problems in file" "true" +public class Test { + public void test(String s1, String s2) { + System.out.println(Integer.compare(s1.length(), s2.length())); + System.out.println(Integer.compare(s2.length(), s1.length())); + /*greater!*/ + /*less!*/ + /*equal!*/ + System.out.println(Integer.compare(s1.length(), s2.length())); + System.out.println(Integer.compare(s2.length(), s1.length())); + System.out.println(Integer.compare(s2.length(), s1.length())); + + System.out.println(s1.length() < s2.length() ? -1 : s1.length() == s2.length() ? 0 : 2); + System.out.println(s1.length() < s2.length() ? 1 : s2.length() < s1.length() ? 0 : 1); + System.out.println(s1.length() == s2.length() ? 1 : s2.length() < s1.length() ? 0 : 1); + System.out.println(s1.length() == s2.length() ? 0 : s2.length() < s2.length() ? -1 : 1); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/boxForComparison/beforeBox.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/beforeBox.java similarity index 77% rename from java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/boxForComparison/beforeBox.java rename to java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/beforeBox.java index 98598424573b..d36be483d032 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/boxForComparison/beforeBox.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/beforeBox.java @@ -1,4 +1,4 @@ -// "Fix all 'Unnecessary boxing to compare primitives' problems in file" "true" +// "Fix all ''compare()' method can be used to compare primitives' problems in file" "true" public class Test { public void test(int a, int b) { if(((Integer)a).compareTo(b) > 0) { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/boxForComparison/beforeBoxComplex.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/beforeBoxComplex.java similarity index 75% rename from java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/boxForComparison/beforeBoxComplex.java rename to java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/beforeBoxComplex.java index d64b01c4bc03..213640090c52 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/boxForComparison/beforeBoxComplex.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/beforeBoxComplex.java @@ -1,4 +1,4 @@ -// "Fix all 'Unnecessary boxing to compare primitives' problems in file" "true" +// "Fix all ''compare()' method can be used to compare primitives' problems in file" "true" public class Test { public int test(String s1, String s2) { int res = new Integer(s1.length()).compareTo(s2.length()); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/beforeIfAssign.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/beforeIfAssign.java new file mode 100644 index 000000000000..c13a2b6af582 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/beforeIfAssign.java @@ -0,0 +1,18 @@ +// "Fix all ''compare()' method can be used to compare primitives' problems in file" "true" +class Test { + public void test(String s1, String s2) { + int res; + if(s1.length() < s2.length()) res = 1; + else if(s1.length() > s2.length()) res = -1; + else res = 0; + System.out.println(res); + } + + public void testMissingElse(String s1, String s2) { + int res; + if(s1.length() < s2.length()) res = 1; + else if(s1.length() > s2.length()) res = -1; + res = 0; + System.out.println(res); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/beforeIfReturn.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/beforeIfReturn.java new file mode 100644 index 000000000000..fdd39ab32544 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/beforeIfReturn.java @@ -0,0 +1,22 @@ +// "Fix all ''compare()' method can be used to compare primitives' problems in file" "true" +class Test { + public int test(String s1, String s2) { + if(s1.length() < s2.length()) { + return -1; + } + if((s1.length()) == s2.length()) return 0; + else /*otherwise bigger*/ return +1; + } + + public int test2(String s1, String s2) { + if(s1.length() > s2.length()) return -1; + else if(s2.length() > s1.length()) return 1; + else return 0; + } + + public int test3(String s1, String s2) { + if(s1.length() > s2.length()) return -1; + else if(s2.length() > s1.length()) return -1; + else return 0; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/beforeTernary.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/beforeTernary.java new file mode 100644 index 000000000000..e320398da6fb --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/beforeTernary.java @@ -0,0 +1,15 @@ +// "Fix all ''compare()' method can be used to compare primitives' problems in file" "true" +public class Test { + public void test(String s1, String s2) { + System.out.println(s1.length() < s2.length() ? -1 : s1.length() == s2.length() ? 0 : 1); + System.out.println((s1.length() > s2.length()) ? -1 : s1.length() == s2.length() ? 0 : 1); + System.out.println((s1.length() > s2.length()) ? /*greater!*/+1 : s1.length() < s2.length() ? /*less!*/-1 : /*equal!*/0); + System.out.println(s1.length() == s2.length() ? 0 : s2.length() < s1.length() ? -1 : 1); + System.out.println(s1.length() < s2.length() ? 1 : s2.length() < s1.length() ? -1 : 0); + + System.out.println(s1.length() < s2.length() ? -1 : s1.length() == s2.length() ? 0 : 2); + System.out.println(s1.length() < s2.length() ? 1 : s2.length() < s1.length() ? 0 : 1); + System.out.println(s1.length() == s2.length() ? 1 : s2.length() < s1.length() ? 0 : 1); + System.out.println(s1.length() == s2.length() ? 0 : s2.length() < s2.length() ? -1 : 1); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/BoxForComparisonInspectionTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/UseCompareMethodInspectionTest.java similarity index 82% rename from java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/BoxForComparisonInspectionTest.java rename to java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/UseCompareMethodInspectionTest.java index 6689194b0e3c..204121ac1613 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/BoxForComparisonInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/UseCompareMethodInspectionTest.java @@ -16,17 +16,17 @@ package com.intellij.java.codeInsight.daemon.quickFix; import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase; -import com.intellij.codeInspection.BoxForComparisonInspection; import com.intellij.codeInspection.LocalInspectionTool; +import com.intellij.codeInspection.UseCompareMethodInspection; import org.jetbrains.annotations.NotNull; -public class BoxForComparisonInspectionTest extends LightQuickFixParameterizedTestCase { +public class UseCompareMethodInspectionTest extends LightQuickFixParameterizedTestCase { @NotNull @Override protected LocalInspectionTool[] configureLocalInspectionTools() { return new LocalInspectionTool[]{ - new BoxForComparisonInspection(), + new UseCompareMethodInspection(), }; } @@ -34,6 +34,6 @@ public class BoxForComparisonInspectionTest extends LightQuickFixParameterizedTe @Override protected String getBasePath() { - return "/codeInsight/daemonCodeAnalyzer/quickFix/boxForComparison"; + return "/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod"; } } \ No newline at end of file diff --git a/resources-en/src/inspectionDescriptions/BoxForComparison.html b/resources-en/src/inspectionDescriptions/BoxForComparison.html deleted file mode 100644 index ec2772159f8a..000000000000 --- a/resources-en/src/inspectionDescriptions/BoxForComparison.html +++ /dev/null @@ -1,10 +0,0 @@ - - -

This inspection suggests to use Integer.compare(), etc. static methods where - constructs involving boxing Integer.valueOf(x).compareTo(y) are used.

-

Double.compare and Float.compare methods appeared in Java 1.4, methods for other boxed types - are available since Java 1.7

- -

New in 2017.2

- - \ No newline at end of file diff --git a/resources-en/src/inspectionDescriptions/UseCompareMethod.html b/resources-en/src/inspectionDescriptions/UseCompareMethod.html new file mode 100644 index 000000000000..4321e3df8303 --- /dev/null +++ b/resources-en/src/inspectionDescriptions/UseCompareMethod.html @@ -0,0 +1,11 @@ + + +

This inspection suggests to use Integer.compare(), etc. static methods where more verbose or less efficient constructs are + used. For example, x > y ? 1 : x < y ? -1 : 0 or Integer.valueOf(x).compareTo(y) could be + replaced with Integer.compare(x, y).

+

Double.compare and Float.compare methods appeared in Java 1.4, methods for other primitive types + are available since Java 1.7

+ +

New in 2017.2

+ + \ No newline at end of file diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index e4d9b21a6d54..dd92cd281200 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -769,9 +769,9 @@ - + implementationClass="com.intellij.codeInspection.UseCompareMethodInspection" /> Date: Sat, 3 Jun 2017 16:32:33 +0300 Subject: [PATCH 124/136] [groovy] implement GroovyPolyVariantReference in PSI --- .../groovy/lang/psi/GrReferenceElement.java | 18 ++++-------------- .../api}/GroovyPolyVariantReference.java | 9 +++++++-- .../statements/arguments/GrArgumentLabel.java | 18 +++++------------- .../expressions/GrOperatorExpression.java | 10 +++------- .../expressions/GrSafeCastExpression.java | 12 ++++-------- .../expressions/GrUnaryExpression.java | 12 ++++-------- .../expressions/path/GrIndexProperty.java | 2 +- .../arguments/GrArgumentLabelImpl.java | 6 ------ .../expressions/path/GrIndexPropertyImpl.java | 2 +- .../path/GrIndexPropertyReference.kt | 2 +- .../types/GrSafeCastExpressionImpl.java | 4 ++-- 11 files changed, 32 insertions(+), 63 deletions(-) rename plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/{resolve => psi/api}/GroovyPolyVariantReference.java (80%) diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/GrReferenceElement.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/GrReferenceElement.java index d2e19cc8d50e..28a74e11984c 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/GrReferenceElement.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/GrReferenceElement.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -16,18 +16,17 @@ package org.jetbrains.plugins.groovy.lang.psi; import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiPolyVariantReference; import com.intellij.psi.PsiType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; +import org.jetbrains.plugins.groovy.lang.psi.api.GroovyPolyVariantReference; import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeArgumentList; -import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil; /** * @author ven */ -public interface GrReferenceElement extends GroovyPsiElement, PsiPolyVariantReference, GrQualifiedReference { +public interface GrReferenceElement extends GroovyPsiElement, GroovyPolyVariantReference, GrQualifiedReference { + @Override @Nullable String getReferenceName(); @@ -38,15 +37,6 @@ public interface GrReferenceElement extends GroovyPsiEleme return advancedResolve().getElement(); } - @NotNull - default GroovyResolveResult advancedResolve() { - return PsiImplUtil.extractUniqueResult(multiResolve(false)); - } - - @Override - @NotNull - GroovyResolveResult[] multiResolve(boolean incompleteCode); - @NotNull PsiType[] getTypeArguments(); diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/GroovyPolyVariantReference.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/GroovyPolyVariantReference.java similarity index 80% rename from plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/GroovyPolyVariantReference.java rename to plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/GroovyPolyVariantReference.java index 8fe3740006eb..deb7cb2fa289 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/GroovyPolyVariantReference.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/GroovyPolyVariantReference.java @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.plugins.groovy.lang.resolve; +package org.jetbrains.plugins.groovy.lang.psi.api; import com.intellij.psi.PsiPolyVariantReference; import org.jetbrains.annotations.NotNull; -import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; +import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil; /** * Same as {@link PsiPolyVariantReference} but returns {@link GroovyResolveResult}. @@ -29,4 +29,9 @@ public interface GroovyPolyVariantReference extends PsiPolyVariantReference { @NotNull @Override GroovyResolveResult[] multiResolve(boolean incompleteCode); + + @NotNull + default GroovyResolveResult advancedResolve() { + return PsiImplUtil.extractUniqueResult(multiResolve(false)); + } } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/arguments/GrArgumentLabel.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/arguments/GrArgumentLabel.java index 885042c40ebb..9d06b4a170ff 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/arguments/GrArgumentLabel.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/arguments/GrArgumentLabel.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 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,30 +17,29 @@ package org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments; import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiPolyVariantReference; import com.intellij.psi.PsiType; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; -import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; +import org.jetbrains.plugins.groovy.lang.psi.api.GroovyPolyVariantReference; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; /** * @author ilyas */ -public interface GrArgumentLabel extends GroovyPsiElement, PsiPolyVariantReference { +public interface GrArgumentLabel extends GroovyPsiElement, GroovyPolyVariantReference { GrArgumentLabel[] EMPTY_ARRAY = new GrArgumentLabel[0]; @NotNull PsiElement getNameElement(); - @Nullable /** - * returns expression which is put into parentheses. + * @return expression which is put into parentheses. */ + @Nullable GrExpression getExpression(); @Nullable @@ -55,11 +54,4 @@ public interface GrArgumentLabel extends GroovyPsiElement, PsiPolyVariantReferen PsiType getLabelType(); GrNamedArgument getNamedArgument(); - - @Override - @NotNull - GroovyResolveResult[] multiResolve(boolean incomplete); - - @NotNull - GroovyResolveResult advancedResolve(); } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrOperatorExpression.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrOperatorExpression.java index 0854410cd381..7787f995bfa8 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrOperatorExpression.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrOperatorExpression.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -16,14 +16,13 @@ package org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions; import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiPolyVariantReference; import com.intellij.psi.PsiType; import com.intellij.psi.tree.IElementType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; +import org.jetbrains.plugins.groovy.lang.psi.api.GroovyPolyVariantReference; -public interface GrOperatorExpression extends GrExpression, PsiPolyVariantReference { +public interface GrOperatorExpression extends GrExpression, GroovyPolyVariantReference { @Nullable PsiType getLeftType(); @@ -36,7 +35,4 @@ public interface GrOperatorExpression extends GrExpression, PsiPolyVariantRefere @NotNull IElementType getOperationTokenType(); - - @NotNull - GroovyResolveResult[] multiResolve(final boolean incompleteCode); } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrSafeCastExpression.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrSafeCastExpression.java index 36ea0ff91741..59c1e7a89a43 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrSafeCastExpression.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrSafeCastExpression.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 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,16 +17,16 @@ package org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions; import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiPolyVariantReference; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; +import org.jetbrains.plugins.groovy.lang.psi.api.GroovyPolyVariantReference; import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement; /** * @author ven */ -public interface GrSafeCastExpression extends GrExpression, PsiPolyVariantReference { +public interface GrSafeCastExpression extends GrExpression, GroovyPolyVariantReference { + @Nullable GrTypeElement getCastTypeElement(); @@ -35,8 +35,4 @@ public interface GrSafeCastExpression extends GrExpression, PsiPolyVariantRefere @NotNull PsiElement getOperationToken(); - - @NotNull - @Override - GroovyResolveResult[] multiResolve(final boolean incompleteCode); } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrUnaryExpression.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrUnaryExpression.java index ee4bc08ed674..68f92f37510b 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrUnaryExpression.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrUnaryExpression.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 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,16 +17,16 @@ package org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions; import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiPolyVariantReference; import com.intellij.psi.tree.IElementType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; +import org.jetbrains.plugins.groovy.lang.psi.api.GroovyPolyVariantReference; /** * @author ilyas */ -public interface GrUnaryExpression extends GrExpression, PsiPolyVariantReference { +public interface GrUnaryExpression extends GrExpression, GroovyPolyVariantReference { + @NotNull IElementType getOperationTokenType(); @@ -36,9 +36,5 @@ public interface GrUnaryExpression extends GrExpression, PsiPolyVariantReference @Nullable GrExpression getOperand(); - @NotNull - @Override - GroovyResolveResult[] multiResolve(final boolean incompleteCode); - boolean isPostfix(); } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/path/GrIndexProperty.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/path/GrIndexProperty.java index de2db7261b58..70a9b916871e 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/path/GrIndexProperty.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/path/GrIndexProperty.java @@ -18,9 +18,9 @@ package org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.groovy.lang.psi.api.GroovyPolyVariantReference; import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; -import org.jetbrains.plugins.groovy.lang.resolve.GroovyPolyVariantReference; public interface GrIndexProperty extends GrExpression { diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/arguments/GrArgumentLabelImpl.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/arguments/GrArgumentLabelImpl.java index 1e4a2722edcc..52a56dcac9e7 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/arguments/GrArgumentLabelImpl.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/arguments/GrArgumentLabelImpl.java @@ -217,12 +217,6 @@ public class GrArgumentLabelImpl extends GroovyPsiElementImpl implements GrArgum } } - @NotNull - @Override - public GroovyResolveResult advancedResolve() { - return PsiImplUtil.extractUniqueResult(multiResolve(false)); - } - @Override @NotNull public String getCanonicalText() { diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrIndexPropertyImpl.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrIndexPropertyImpl.java index d2a2c988eaf9..36caedf0fa86 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrIndexPropertyImpl.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrIndexPropertyImpl.java @@ -25,6 +25,7 @@ import com.intellij.psi.PsiType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor; +import org.jetbrains.plugins.groovy.lang.psi.api.GroovyPolyVariantReference; import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrIndexProperty; @@ -32,7 +33,6 @@ import org.jetbrains.plugins.groovy.lang.psi.dataFlow.types.TypeInferenceHelper; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.GrExpressionImpl; import org.jetbrains.plugins.groovy.lang.psi.util.GroovyIndexPropertyUtil; import org.jetbrains.plugins.groovy.lang.psi.util.GroovyLValueUtil; -import org.jetbrains.plugins.groovy.lang.resolve.GroovyPolyVariantReference; import org.jetbrains.plugins.groovy.lang.typing.GrTypeCalculator; /** diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrIndexPropertyReference.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrIndexPropertyReference.kt index 5675a3644dfd..7e15a11b7b0a 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrIndexPropertyReference.kt +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrIndexPropertyReference.kt @@ -18,6 +18,7 @@ package org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.path import com.intellij.openapi.util.TextRange import com.intellij.psi.PsiPolyVariantReferenceBase import com.intellij.psi.PsiType +import org.jetbrains.plugins.groovy.lang.psi.api.GroovyPolyVariantReference import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrAssignmentExpression import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrIndexProperty @@ -26,7 +27,6 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.GrTupleType import org.jetbrains.plugins.groovy.lang.psi.util.getArgumentListType import org.jetbrains.plugins.groovy.lang.psi.util.isClassLiteral import org.jetbrains.plugins.groovy.lang.psi.util.isSimpleArrayAccess -import org.jetbrains.plugins.groovy.lang.resolve.GroovyPolyVariantReference import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil class GrIndexPropertyReference(element: GrIndexPropertyImpl, val rhs: Boolean) diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/types/GrSafeCastExpressionImpl.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/types/GrSafeCastExpressionImpl.java index ba158bc4c38b..a1d48adbafc0 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/types/GrSafeCastExpressionImpl.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/types/GrSafeCastExpressionImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -45,7 +45,7 @@ import java.util.HashMap; /** * @author ven */ -public class GrSafeCastExpressionImpl extends GrExpressionImpl implements GrSafeCastExpression, PsiPolyVariantReference { +public class GrSafeCastExpressionImpl extends GrExpressionImpl implements GrSafeCastExpression { private static final Function TYPE_CALCULATOR = (NullableFunction)cast -> { From d2aaf814aa3f9b37ddd54780c600a8e155e8ff0f Mon Sep 17 00:00:00 2001 From: yarik Date: Tue, 6 Jun 2017 13:20:53 +0300 Subject: [PATCH 125/136] [java formatter] ditched tab post format processor, since it is doing something inappropriate, has performance issues and we can happily live without it. --- .../codeStyle/TabPostFormatProcessor.java | 534 ------------------ .../formatter/java/JavadocFormatterTest.java | 16 + .../codeStyle/TabPostFormatProcessorTest.java | 403 ------------- resources/src/META-INF/IdeaPlugin.xml | 1 - 4 files changed, 16 insertions(+), 938 deletions(-) delete mode 100644 java/java-impl/src/com/intellij/psi/impl/source/codeStyle/TabPostFormatProcessor.java delete mode 100644 java/java-tests/testSrc/com/intellij/java/psi/impl/source/codeStyle/TabPostFormatProcessorTest.java diff --git a/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/TabPostFormatProcessor.java b/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/TabPostFormatProcessor.java deleted file mode 100644 index 49f9b8dd178e..000000000000 --- a/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/TabPostFormatProcessor.java +++ /dev/null @@ -1,534 +0,0 @@ -/* - * Copyright 2000-2012 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.lang.ASTNode; -import com.intellij.lang.Language; -import com.intellij.lang.java.JavaLanguage; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.util.TextRange; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.codeStyle.CodeStyleSettings; -import com.intellij.psi.codeStyle.CommonCodeStyleSettings; -import com.intellij.psi.formatter.FormatterUtil; -import com.intellij.psi.impl.source.tree.TreeUtil; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * This class handles a use-case when reformatted text conflicts with 'use tab' code style setting. E.g. target text uses - * tabs for indentation but our code style is configured to use spaces. - *

- * We already have corresponding support at the block level but it's possible that multiline text is treated as a single block, - * i.e. all its internal indents are not visible to the formatter. That's why current class is introduced. - *

- * Thread-safe. - * - * @author Denis Zhdanov - * @since 8/1/12 2:38 PM - */ -public class TabPostFormatProcessor implements PostFormatProcessor { - - @Override - public PsiElement processElement(@NotNull PsiElement source, @NotNull CodeStyleSettings settings) { - doProcess(source, TextRange.from(source.getTextRange().getStartOffset(), source.getTextLength()), settings); - return source; - } - - @NotNull - @Override - public TextRange processText(@NotNull PsiFile source, @NotNull TextRange rangeToReformat, @NotNull CodeStyleSettings settings) { - return doProcess(source, rangeToReformat, settings); - } - - @NotNull - private static TextRange doProcess(@NotNull PsiElement source, @NotNull TextRange range, @NotNull CodeStyleSettings settings) { - ASTNode node = source.getNode(); - if (node == null) { - return range; - } - - Language language = source.getLanguage(); - if (language != JavaLanguage.INSTANCE) { - // We had the only complaint for tabs not being converted to spaces for now. It was for the java code which has - // a single block for the multi-line comment. This check should be removed if it is decided to generalize - // this logic to other languages as well. - return range; - } - - if (!source.isValid()) return range; - PsiFile file = source.getContainingFile(); - CommonCodeStyleSettings.IndentOptions indentOptions = settings.getIndentOptionsByFile(file, range); - - boolean useTabs = indentOptions.USE_TAB_CHARACTER; - boolean smartTabs = indentOptions.SMART_TABS; - int tabWidth = indentOptions.TAB_SIZE; - return processViaPsi(node, range, new TreeHelperImpl(), useTabs, smartTabs, tabWidth); - } - - @NotNull - public static TextRange processViaPsi(@NotNull ASTNode node, - @NotNull TextRange range, - @NotNull TreeHelper treeHelper, - boolean useTabs, - boolean smartTabs, - int tabWidth) - { - AstHelper helper = new AstHelper(node, treeHelper); - do { - if (useTabs) { - if (smartTabs) { - range = processSmartTabs(helper, range, tabWidth); - } - else { - range = processTabs(helper, range, tabWidth); - } - } - else { - range = processSpaces(helper, range, tabWidth); - } - } - while (helper.nextLine()); - return range; - } - - @NotNull - public static TextRange processViaDocument(@NotNull Document document, - @NotNull TextRange range, - boolean useTabs, - boolean useSmartTabs, - int tabWidth) - { - TextRange result = range; - int startLine = document.getLineNumber(Math.min(document.getTextLength(), range.getStartOffset())); - int endLine = document.getLineNumber(Math.max(0, Math.min(document.getTextLength(), range.getEndOffset()) - 1)); - DocumentHelper helper = new DocumentHelper(document, startLine); - for (int line = startLine; line <= endLine; line++) { - helper.setLine(line); - if (useTabs) { - if (useSmartTabs) { - result = processSmartTabs(helper, result, tabWidth); - } - else { - result = processTabs(helper, result, tabWidth); - } - } - else { - result = processSpaces(helper, result, tabWidth); - } - } - return result; - } - - /** - * Converts tabulations to white spaces at the target line's indent space. - * - * @param helper data facade - * @param range target range allowed for modification - * @param tabWidth tab width in columns to use during conversion (each tab symbol is replaced by white spaces which number is - * equal to tab width) - * @return given text range if no modification to the target line's indent space has been performed: - * adjusted range that points to semantically the same region otherwise - */ - @NotNull - private static TextRange processSpaces(@NotNull Helper helper, @NotNull TextRange range, int tabWidth) { - CharSequence indent = helper.getCurrentLineIndent(); - int start = Math.max(0, range.getStartOffset() - helper.getCurrentLineStartOffset()); - int end = Math.min(indent.length(), range.getEndOffset() - helper.getCurrentLineStartOffset()); - int tabsNumber = 0; - int indentOffset = end; - for (int i = start; i < end; i++) { - char c = indent.charAt(i); - if (c == '\t') { - tabsNumber++; - } - else if (c != ' ') { - indentOffset = i; - break; - } - } - if (tabsNumber > 0) { - helper.replace(start, indentOffset, StringUtil.repeat(" ", indentOffset - start - tabsNumber + tabsNumber * tabWidth)); - return TextRange.create(range.getStartOffset(), range.getEndOffset() - tabsNumber + tabsNumber * tabWidth); - } - else { - return range; - } - } - - /** - * Converts white spaces to tabulations at the target line's indent space. - * - * @param helper data facade - * @param range target range allowed for modification - * @param tabWidth tab width in columns to use during conversion (each tab symbol is replaced by white spaces which number is - * equal to tab width) - * @return given text range if no modification to the target line's indent space has been performed: - * adjusted range that points to semantically the same region otherwise - */ - @NotNull - private static TextRange processTabs(@NotNull Helper helper, @NotNull TextRange range, int tabWidth) { - CharSequence indent = helper.getCurrentLineIndent(); - int start = Math.max(0, range.getStartOffset() - helper.getCurrentLineStartOffset()); - int end = Math.min(indent.length(), range.getEndOffset() - helper.getCurrentLineStartOffset()); - int replacementsNumber = 0; - int consecutiveSpaces = 0; - for (int i = start; i < end; i++) { - char c = indent.charAt(i); - if (c == ' ') { - ++consecutiveSpaces; - } - else { - int tabsNumber = consecutiveSpaces / tabWidth; - if (tabsNumber > 0) { - helper.replace(i - consecutiveSpaces, i - consecutiveSpaces + tabsNumber * tabWidth, StringUtil.repeat("\t", tabsNumber)); - replacementsNumber++; - consecutiveSpaces = 0; - } - if (c != '\t') { - break; - } - } - } - - int tabsNumber = consecutiveSpaces / tabWidth; - if (tabsNumber > 0) { - helper.replace(end - consecutiveSpaces, end - consecutiveSpaces + tabsNumber * tabWidth, StringUtil.repeat("\t", tabsNumber)); - } - - if (replacementsNumber > 0) { - return TextRange.create(range.getStartOffset(), range.getEndOffset() - replacementsNumber * (tabWidth - 1)); - } - else { - return range; - } - } - - /** - * Converts tabulations to white spaces at the target line's indent space. - * - * @param helper data facade - * @param range target range allowed for modification - * @param tabWidth tab width in columns to use during conversion (every group of 'tab width' white spaces from the indent space might - * be replaced by a tab symbol) - * @return given text range if no modification to the target line's indent space has been performed: - * adjusted range that points to semantically the same region otherwise - */ - @SuppressWarnings("AssignmentToForLoopParameter") - @NotNull - private static TextRange processSmartTabs(@NotNull Helper helper, @NotNull TextRange range, int tabWidth) { - // Adjust current line indent. The general idea is to replace white spaces by tab symbols if that maps to the previous line indent. - CharSequence prevLineIndent = helper.getPrevLineIndent(); - if (prevLineIndent == null) { - return processTabs(helper, range, tabWidth); - } - - CharSequence currentLineIndent = helper.getCurrentLineIndent(); - int lineStart = 0; - int start = Math.max(0, range.getStartOffset() - helper.getCurrentLineStartOffset()); - int end = Math.min(currentLineIndent.length(), range.getEndOffset() - helper.getCurrentLineStartOffset()); - int indentOffset = 0; - int tabsReplaced = 0; - for (int i = lineStart; i < end && indentOffset < prevLineIndent.length(); i++, indentOffset++) { - char c = currentLineIndent.charAt(i); - if (prevLineIndent.charAt(indentOffset) == ' ') { - if (c == ' ') { - continue; - } - else { - break; - } - } - - // Assuming that target prevLineIndent symbol is tab then. - if (c == '\t') { - continue; - } - - if (end - i < tabWidth) { - break; - } - - boolean canReplace = true; - for (int j = i + 1, max = Math.min(end, i + tabWidth); j < max; j++) { - if (currentLineIndent.charAt(j) != ' ') { - canReplace = false; - break; - } - } - - if (!canReplace) { - break; - } - - if (i < start) { - // Continue processing if target range doesn't cover the whole white spaces which are intended to replace tab symbol. - i += tabWidth - 1; // -1 because of 'for' loop increment - continue; - } - - helper.replace(i, i + tabWidth, "\t"); - tabsReplaced++; - end -= tabWidth - 1; - } - - return tabsReplaced > 0 ? TextRange.create(range.getStartOffset(), range.getEndOffset() - tabsReplaced * (tabWidth - 1)) : range; - } - - /** - * There are two possible processing use-cases: - *

-   * 
    - *
  • document-based processing;
  • - *
  • PSI-based processing;
  • - *
- *
- * That's why we hide implementation-specific processing behind the current interface and use it at the generic 'engine'. - *

- * The general idea is to process indent spaces line-by-line from top to bottom. - */ - interface Helper { - - /** - * @return previous line indent space if current line is not the first one; {@code null} otherwise - */ - @Nullable CharSequence getPrevLineIndent(); - - int getCurrentLineStartOffset(); - - /** @return current line's indent space */ - @NotNull CharSequence getCurrentLineIndent(); - - /** - * Asks current helper to modify target line's indent space. - * - * @param start start offset of the indent range to modify (counts from the line start, i.e. doesn't take into - * consideration line start offset at the document) - * @param end end offset of the indent range to modify (counts from the line start, i.e. doesn't take into - * consideration line start offset at the document) - * @param newText replacement text - */ - void replace(int start, int end, @NotNull String newText); - } - - private static class DocumentHelper implements Helper { - - @NotNull private final Document myDocument; - private int myLine; - private int myLineStartOffset; - - DocumentHelper(@NotNull Document document, int line) { - myDocument = document; - setLine(line); - } - - @Nullable - @Override - public CharSequence getPrevLineIndent() { - if (myLine <= 0) { - return null; - } - int prevLineStart = myDocument.getLineStartOffset(myLine - 1); - int prevLineIndentEnd = prevLineStart; - int prevLineEnd = myDocument.getLineEndOffset(myLine - 1); - CharSequence text = myDocument.getCharsSequence(); - for (; prevLineIndentEnd < prevLineEnd; prevLineIndentEnd++) { - char c = text.charAt(prevLineIndentEnd); - if (c != '\t' && c != ' ') { - break; - } - } - return text.subSequence(prevLineStart, prevLineIndentEnd); - } - - @Override - public int getCurrentLineStartOffset() { - return myLineStartOffset; - } - - @NotNull - @Override - public CharSequence getCurrentLineIndent() { - int end = myDocument.getLineEndOffset(myLine); - CharSequence text = myDocument.getCharsSequence(); - for (int i = myLineStartOffset; i < end; i++) { - char c = text.charAt(i); - if (c != ' ' && c != '\t') { - return text.subSequence(myLineStartOffset, i); - } - } - return text.subSequence(myLineStartOffset, end); - } - - @Override - public void replace(int start, int end, @NotNull String newText) { - myDocument.replaceString(myLineStartOffset + start, myLineStartOffset + end, newText); - } - - public void setLine(int line) { - myLine = line; - myLineStartOffset = myDocument.getLineStartOffset(line); - } - } - - private static class AstHelper implements Helper { - - @NotNull private final TreeHelper myHelper; - @Nullable private ASTNode myCurrentIndentHolder; - - private int myLineStartOffset; - - AstHelper(@NotNull ASTNode startNode, @NotNull TreeHelper helper) { - myHelper = helper; - myCurrentIndentHolder = myHelper.firstLeaf(startNode); - if (startNode.getStartOffset() <= 0) { - return; - } - nextLine(); - } - - @SuppressWarnings("LoopStatementThatDoesntLoop") - @Override - public CharSequence getPrevLineIndent() { - if (myCurrentIndentHolder == null) { - return null; - } - - // Check if current white space is multiline. - int end = myLineStartOffset - 1; - CharSequence text = myCurrentIndentHolder.getChars(); - for (int i = end - 1; i >= 0; i--) { - if (text.charAt(i) == '\n') { - return text.subSequence(i + 1, end); - } - } - for (ASTNode prev = prevIndentNode(myCurrentIndentHolder); prev != null; prev = prevIndentNode(prev)) { - CharSequence chars = prev.getChars(); - for (int i = chars.length() - 1; i >= 0; i--) { - if (chars.charAt(i) == '\n') { - return chars.subSequence(i + 1, chars.length()); - } - } - return chars; - } - return null; - } - - @Override - public int getCurrentLineStartOffset() { - ASTNode whiteSpace = myCurrentIndentHolder; - return whiteSpace == null ? 0 : whiteSpace.getStartOffset() + myLineStartOffset; - } - - @SuppressWarnings("UnusedAssignment") - @NotNull - @Override - public CharSequence getCurrentLineIndent() { - if (myCurrentIndentHolder == null || myLineStartOffset < 0) { - return ""; - } - - CharSequence text = myCurrentIndentHolder.getChars(); - for (int i = myLineStartOffset; i < text.length(); i++) { - char c = text.charAt(i); - if (c == '\n' || (c != ' ' && c != '\t')) { - return text.subSequence(myLineStartOffset, i); - } - } - return text.subSequence(myLineStartOffset, text.length()); - } - - @Override - public void replace(int start, int end, @NotNull String newText) { - if (myCurrentIndentHolder != null) { - myHelper.replace(newText, TextRange.create(start, end).shiftRight(getCurrentLineStartOffset()), myCurrentIndentHolder); - } - } - - public boolean nextLine() { - if (myCurrentIndentHolder == null) { - return false; - } - for (ASTNode node = myHelper.nextLeaf(myCurrentIndentHolder); node != null; node = myHelper.nextLeaf(node)) { - if (myCurrentIndentHolder.getTextLength() <= 0) { - continue; - } - CharSequence text = node.getChars(); - for (myLineStartOffset = 0; myLineStartOffset < text.length(); myLineStartOffset++) { - char c = text.charAt(myLineStartOffset); - if (c == '\n' && myLineStartOffset < text.length() - 1) { - myCurrentIndentHolder = node; - myLineStartOffset++; - return true; - } - } - } - - myCurrentIndentHolder = null; - return false; - } - - @Nullable - private ASTNode prevIndentNode(@NotNull ASTNode current) { - for (ASTNode candidate = myHelper.prevLeaf(current); candidate != null; candidate = myHelper.prevLeaf(candidate)) { - if (candidate.getStartOffset() <= 0 || StringUtil.contains(candidate.getChars(), 0, candidate.getTextLength(), '\n')) { - return candidate; - } - } - return null; - } - } - - public interface TreeHelper { - @Nullable - ASTNode prevLeaf(@NotNull ASTNode current); - - @Nullable - ASTNode nextLeaf(@NotNull ASTNode current); - - @Nullable - ASTNode firstLeaf(@NotNull ASTNode startNode); - - void replace(@NotNull String newText, @NotNull TextRange range, @NotNull ASTNode leaf); - } - - private static class TreeHelperImpl implements TreeHelper { - - @Override - public ASTNode prevLeaf(@NotNull ASTNode current) { - return TreeUtil.prevLeaf(current); - } - - @Nullable - @Override - public ASTNode nextLeaf(@NotNull ASTNode current) { - return TreeUtil.nextLeaf(current); - } - - @Nullable - @Override - public ASTNode firstLeaf(@NotNull ASTNode startNode) { - return TreeUtil.findFirstLeaf(startNode); - } - - @Override - public void replace(@NotNull String newText, @NotNull TextRange range, @NotNull ASTNode leaf) { - FormatterUtil.replaceInnerWhiteSpace(newText, leaf, range); - } - } -} diff --git a/java/java-tests/testSrc/com/intellij/java/psi/formatter/java/JavadocFormatterTest.java b/java/java-tests/testSrc/com/intellij/java/psi/formatter/java/JavadocFormatterTest.java index 250b0a5e45ae..36b34e7b6c83 100644 --- a/java/java-tests/testSrc/com/intellij/java/psi/formatter/java/JavadocFormatterTest.java +++ b/java/java-tests/testSrc/com/intellij/java/psi/formatter/java/JavadocFormatterTest.java @@ -986,6 +986,22 @@ public class JavadocFormatterTest extends AbstractJavaFormatterTest { ); } + public void test_JdWithTabs() { + doClassTest( + "\t/**\n" + + "\t \t *\n" + + "\t \t *\n" + + "\t \t */\n" + + "\tvoid check() {\n" + + "\t}", + "/**\n" + + " *\n" + + " *\n" + + " */\n" + + "void check() {\n" + + "}" + ); + } } \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/psi/impl/source/codeStyle/TabPostFormatProcessorTest.java b/java/java-tests/testSrc/com/intellij/java/psi/impl/source/codeStyle/TabPostFormatProcessorTest.java deleted file mode 100644 index c53d9e602385..000000000000 --- a/java/java-tests/testSrc/com/intellij/java/psi/impl/source/codeStyle/TabPostFormatProcessorTest.java +++ /dev/null @@ -1,403 +0,0 @@ -/* - * Copyright 2000-2017 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.java.psi.impl.source.codeStyle; - -import com.intellij.lang.ASTNode; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.impl.LineSet; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.TextRange; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.TokenType; -import com.intellij.psi.impl.source.codeStyle.TabPostFormatProcessor; -import com.intellij.psi.tree.IElementType; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jmock.Expectations; -import org.jmock.Mockery; -import org.jmock.api.Invocation; -import org.jmock.integration.junit4.JMock; -import org.jmock.integration.junit4.JUnit4Mockery; -import org.jmock.lib.action.CustomAction; -import org.jmock.lib.legacy.ClassImposteriser; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.util.ArrayList; -import java.util.List; - -import static org.junit.Assert.assertEquals; - -/** - * @author Denis Zhdanov - * @since 08/01/2012 - */ -@RunWith(JMock.class) -public class TabPostFormatProcessorTest { - - private static final String START_RANGE_MARKER = ""; - private static final String END_RANGE_MARKER = ""; - - private Mockery myMockery; - private Document myDocument; - - @Before - public void setUp() { - myMockery = new JUnit4Mockery() {{ - setImposteriser(ClassImposteriser.INSTANCE); - }}; - myDocument = myMockery.mock(Document.class); - } - - @After - public void checkExpectations() { - myMockery.assertIsSatisfied(); - } - - @Test - public void spacesAndWholeLineInsideRange() { - doTestSpaces( - "line 1\n" + - " \t \tline2\n" + - "line 3", - 4, - "line 1\n" + - " line2\n" + - "line 3" - ); - } - @Test - public void spacesAndExactRange() { - doTestSpaces( - "line 1\n" + - " \t \tline2\n" + - "line 3", - 4, - "line 1\n" + - " line2\n" + - "line 3" - ); - } - - @Test - public void spacesAndHeadIntersection() { - doTestSpaces( - "line 1\n" + - " \t \tline2\n" + - "line 3", - 4, - "line 1\n" + - " \tline2\n" + - "line 3" - ); - } - - @Test - public void spacesAndTailIntersection() { - doTestSpaces( - "line 1\n" + - " \t \tline2\n" + - "line 3", - 4, - "line 1\n" + - " \t line2\n" + - "line 3" - ); - } - - @Test - public void spacesAndPartialIndentInsideRange() { - doTestSpaces( - "line 1\n" + - " \t \t \t line2\n" + - "line 3", - 4, - "line 1\n" + - " \t \t line2\n" + - "line 3" - ); - } - - @Test - public void tabsAndWholeLineInsideRange() { - doTestTabs( - "line 1\n" + - " \t line2\n" + - "line 3", - 4, - "line 1\n" + - "\t \t line2\n" + - "line 3" - ); - } - - @Test - public void tabsAndHeadIntersection() { - doTestTabs( - "line 1\n" + - " \t line2\n" + - "line 3", - 4, - "line 1\n" + - "\t\t line2\n" + - "line 3" - ); - } - - @Test - public void tabsAndTailIntersection() { - doTestTabs( - "line 1\n" + - " line2\n" + - "line 3", - 4, - "line 1\n" + - " \t line2\n" + - "line 3" - ); - } - - @Test - public void tabsAndPartialIndentInsideRange() { - doTestTabs( - "line 1\n" + - " line2\n" + - "line 3", - 4, - "line 1\n" + - " \t line2\n" + - "line 3" - ); - } - - @Test - public void smartTabsForTheFirstLine() { - doTestSmartTabs( - " line 1\n" + - "\t line 2", - 4, - "\t\t line 1\n" + - "\t line 2" - ); - } - - @Test - public void smartTabsFromUpperLine() { - doTestSmartTabs( - "\t\t line 1\n" + - " 2", - 4, - "\t\t line 1\n" + - "\t\t 2" - ); - } - - @Test - public void smartTabsExactReplacement() { - doTestSmartTabs( - "\tline 1\n" + - " line 2", - 4, - "\tline 1\n" + - "\tline 2" - ); - } - - @Test - public void smartTabsMismatchedIndent() { - doTestSmartTabs( - " \tline 1\n" + - "\t line 2", - 4, - " \tline 1\n" + - "\t line 2" - ); - } - - @Test - public void smartTabsPartialMatchedIndent() { - doTestSmartTabs( - "\t\tline 1\n" + - " line 2", - 4, - "\t\tline 1\n" + - " \tline 2" - ); - } - - @Test - public void smartTabsPartialMisMatchedIndent() { - doTestSmartTabs( - "\t\tline 1\n" + - " line 2", - 4, - "\t\tline 1\n" + - " line 2" - ); - } - - private void doTestSpaces(@NotNull String initial, final int tabWidth, @NotNull String expected) { - doTest(initial, expected, false, false, tabWidth); - } - - private void doTestTabs(@NotNull String initial, final int tabWidth, @NotNull String expected) { - doTest(initial, expected, true, false, tabWidth); - } - - private void doTestSmartTabs(@NotNull String initial, final int tabWidth, @NotNull String expected) { - doTest(initial, expected, true, true, tabWidth); - } - - private void doTest(@NotNull String initial, @NotNull String expected, boolean useTabs, boolean smartTabs, int tabWidth) { - doDocumentTest(initial, expected, useTabs, smartTabs, tabWidth); - doPsiTest(initial, expected, useTabs, smartTabs, tabWidth); - } - - private void doDocumentTest(@NotNull String initial, @NotNull String expected, boolean useTabs, boolean smartTabs, int tabWidth) { - Pair pair = parse(initial); - final StringBuilder text = new StringBuilder(pair.first); - final TextRange range = pair.second; - - myMockery.checking(new Expectations() {{ - allowing(myDocument).getCharsSequence(); will(returnValue(text.toString())); - allowing(myDocument).getTextLength(); will(returnValue(text.length())); - }}); - - final LineSet lines = LineSet.createLineSet(myDocument.getCharsSequence()); - myMockery.checking(new Expectations() {{ - allowing(myDocument).getLineNumber(with(any(int.class))); will(new CustomAction("getLineNumber()") { - @Override - public Object invoke(Invocation invocation) throws Throwable { - return lines.findLineIndex((Integer)invocation.getParameter(0)); - } - }); - allowing(myDocument).getLineStartOffset(with(any(int.class))); will(new CustomAction("getLineStartOffset()") { - @Override - public Object invoke(Invocation invocation) throws Throwable { - return lines.getLineStart((Integer)invocation.getParameter(0)); - } - }); - allowing(myDocument).getLineEndOffset(with(any(int.class))); will(new CustomAction("getLineEndOffset()") { - @Override - public Object invoke(Invocation invocation) throws Throwable { - return lines.getLineEnd((Integer)invocation.getParameter(0)); - } - }); - allowing(myDocument).replaceString(with(any(int.class)), with(any(int.class)), with(any(String.class))); - will(new CustomAction("replaceString") { - @Nullable - @Override - public Object invoke(Invocation invocation) throws Throwable { - int start = (Integer)invocation.getParameter(0); - int end = (Integer)invocation.getParameter(1); - String newText = (String)invocation.getParameter(2); - text.replace(start, end, newText); - return null; - } - }); - }}); - - TabPostFormatProcessor.processViaDocument(myDocument, range, useTabs, smartTabs, tabWidth); - assertEquals(expected, text.toString()); - } - - private static Pair parse(@NotNull String text) { - int rangeMarkerStart = text.indexOf(START_RANGE_MARKER); - int rangeMarkerEnd = text.indexOf(END_RANGE_MARKER); - final StringBuilder buffer = new StringBuilder(); - final TextRange range; - if (rangeMarkerStart >= 0 && rangeMarkerEnd >= 0) { - range = TextRange.create(rangeMarkerStart, rangeMarkerEnd - START_RANGE_MARKER.length()); - buffer.append(text.substring(0, rangeMarkerStart)) - .append(text.substring(rangeMarkerStart + START_RANGE_MARKER.length(), rangeMarkerEnd)) - .append(text.substring(rangeMarkerEnd + END_RANGE_MARKER.length())); - } - else { - range = TextRange.create(0, text.length()); - buffer.append(text); - } - return Pair.create(buffer.toString(), range); - } - - private void doPsiTest(@NotNull String initial, @NotNull String expected, boolean useTabs, boolean smartTabs, int tabWidth) { - final List children = new ArrayList<>(); - final List childrenText = new ArrayList<>(); - Pair pair = parse(initial); - final String text = pair.first; - int start = 0; - boolean inWhiteSpace = initial.charAt(0) == ' ' || initial.charAt(0) == '\t'; - for (int i = 1; i <= text.length(); i++) { - if (i == text.length() || ((StringUtil.isWhiteSpace(text.charAt(i))) ^ inWhiteSpace)) { - final int childIndex = children.size(); - final int startOffset = start; - childrenText.add(new StringBuilder(text.substring(start, i))); - final ASTNode child = myMockery.mock(ASTNode.class, "child" + childIndex); - children.add(child); - final IElementType type = inWhiteSpace ? TokenType.WHITE_SPACE : TokenType.CODE_FRAGMENT; - myMockery.checking(new Expectations() {{ - allowing(child).getElementType(); will(returnValue(type)); - allowing(child).getChars(); will(returnValue(childrenText.get(childIndex))); - allowing(child).getTextLength(); will(returnValue(childrenText.get(childIndex).length())); - allowing(child).getStartOffset(); will(returnValue(startOffset)); - }}); - inWhiteSpace = !inWhiteSpace; - start = i; - } - } - - final ASTNode root = myMockery.mock(ASTNode.class); - myMockery.checking(new Expectations() {{ - allowing(root).getFirstChildNode(); will(returnValue(children.get(0))); - allowing(root).getTextLength(); will(returnValue(text.length())); - allowing(root).getStartOffset(); will(returnValue(0)); - }}); - - TabPostFormatProcessor.TreeHelper helper = new TabPostFormatProcessor.TreeHelper() { - - @Override - public ASTNode prevLeaf(@NotNull ASTNode current) { - int i = children.indexOf(current); - return i > 0 ? children.get(i - 1) : null; - } - - @Override - public ASTNode nextLeaf(@NotNull ASTNode current) { - int i = children.indexOf(current); - return i < children.size() - 1 ? children.get(i + 1) : null; - } - - @Override - public ASTNode firstLeaf(@NotNull ASTNode startNode) { - return root == startNode ? children.get(0) : null; - } - - @Override - public void replace(@NotNull String newText, @NotNull TextRange range, @NotNull ASTNode leaf) { - int i = children.indexOf(leaf); - childrenText.get(i).replace(range.getStartOffset() - leaf.getStartOffset(), range.getEndOffset() - leaf.getStartOffset(), newText); - } - }; - - TabPostFormatProcessor.processViaPsi(root, pair.second, helper, useTabs, smartTabs, tabWidth); - StringBuilder actual = new StringBuilder(); - for (ASTNode child : children) { - actual.append(child.getChars()); - } - assertEquals(expected, actual.toString()); - } -} diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index dd92cd281200..b5fb9748e057 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -1452,7 +1452,6 @@ - From 6fbca226c9d93e4f4815c74dacc9d9fb13fd2d49 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Tue, 6 Jun 2017 17:38:21 +0700 Subject: [PATCH 126/136] UseCompareMethodInspection: members rearranged --- .../UseCompareMethodInspection.java | 60 +++++++++---------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/UseCompareMethodInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/UseCompareMethodInspection.java index e25ba55db6e0..154d14ccdfa4 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/UseCompareMethodInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/UseCompareMethodInspection.java @@ -217,6 +217,29 @@ public class UseCompareMethodInspection extends BaseJavaBatchLocalInspectionTool return null; } + @Contract("null -> null") + private static CompareInfo fromCall(PsiMethodCallExpression call) { + if (call == null) return null; + PsiElement nameElement = call.getMethodExpression().getReferenceNameElement(); + if (nameElement == null) return null; + String name = nameElement.getText(); + if (!"compareTo".equals(name)) return null; + PsiExpression[] args = call.getArgumentList().getExpressions(); + if (args.length != 1) return null; + PsiExpression arg = args[0]; + PsiExpression qualifier = call.getMethodExpression().getQualifierExpression(); + if (qualifier == null) return null; + PsiClassType boxedType = getBoxedType(call); + if (boxedType == null) return null; + PsiPrimitiveType primitiveType = PsiPrimitiveType.getUnboxedType(boxedType); + if (!isTypeConvertible(primitiveType, call)) return null; + PsiExpression left = extractPrimitive(boxedType, primitiveType, qualifier); + if (left == null) return null; + PsiExpression right = extractPrimitive(boxedType, primitiveType, arg); + if (right == null) return null; + return new CompareInfo(call, call, left, right, boxedType); + } + @Nullable static PsiClassType getBoxedType(PsiMethodCallExpression call) { PsiMethod method = call.resolveMethod(); @@ -264,6 +287,13 @@ public class UseCompareMethodInspection extends BaseJavaBatchLocalInspectionTool return expression != null && expression.getType() instanceof PsiPrimitiveType ? expression : null; } + @Contract("null, _ -> false") + private static boolean isTypeConvertible(PsiType type, PsiElement context) { + return type instanceof PsiPrimitiveType && (PsiType.DOUBLE.equals(type) || + PsiType.FLOAT.equals(type) || + PsiUtil.isLanguageLevel7OrHigher(context)); + } + static class CompareInfo { final PsiElement myTemplate; final PsiExpression myToReplace; @@ -294,36 +324,6 @@ public class UseCompareMethodInspection extends BaseJavaBatchLocalInspectionTool } } - @Contract("null -> null") - private static CompareInfo fromCall(PsiMethodCallExpression call) { - if (call == null) return null; - PsiElement nameElement = call.getMethodExpression().getReferenceNameElement(); - if (nameElement == null) return null; - String name = nameElement.getText(); - if (!"compareTo".equals(name)) return null; - PsiExpression[] args = call.getArgumentList().getExpressions(); - if (args.length != 1) return null; - PsiExpression arg = args[0]; - PsiExpression qualifier = call.getMethodExpression().getQualifierExpression(); - if (qualifier == null) return null; - PsiClassType boxedType = getBoxedType(call); - if (boxedType == null) return null; - PsiPrimitiveType primitiveType = PsiPrimitiveType.getUnboxedType(boxedType); - if (!isTypeConvertible(primitiveType, call)) return null; - PsiExpression left = extractPrimitive(boxedType, primitiveType, qualifier); - if (left == null) return null; - PsiExpression right = extractPrimitive(boxedType, primitiveType, arg); - if (right == null) return null; - return new CompareInfo(call, call, left, right, boxedType); - } - - @Contract("null, _ -> false") - private static boolean isTypeConvertible(PsiType type, PsiElement context) { - return type instanceof PsiPrimitiveType && (PsiType.DOUBLE.equals(type) || - PsiType.FLOAT.equals(type) || - PsiUtil.isLanguageLevel7OrHigher(context)); - } - private static class ReplaceWithPrimitiveCompareFix implements LocalQuickFix { private String myClassName; From 27a2f1517d4e40cfaf334ffcf941157648cd0392 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Mon, 5 Jun 2017 16:19:46 +0300 Subject: [PATCH 127/136] [groovy] use utility method instead of own implementation Use findChildrenByClass() in GrMethodCallExpressionImpl .getClosureArguments() --- .../expressions/path/GrMethodCallExpressionImpl.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrMethodCallExpressionImpl.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrMethodCallExpressionImpl.java index c5da5cc7b688..c9a82dd6e093 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrMethodCallExpressionImpl.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrMethodCallExpressionImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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,11 +17,9 @@ package org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.path; import com.intellij.lang.ASTNode; -import com.intellij.psi.PsiElement; import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; -import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes; import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; @@ -31,8 +29,6 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrC import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.GrMethodCallImpl; -import java.util.List; - /** * @author ilyas */ @@ -78,7 +74,6 @@ public class GrMethodCallExpressionImpl extends GrMethodCallImpl implements GrMe @Override @NotNull public GrClosableBlock[] getClosureArguments() { - final List children = findChildrenByType(GroovyElementTypes.CLOSABLE_BLOCK); - return children.toArray(new GrClosableBlock[children.size()]); + return findChildrenByClass(GrClosableBlock.class); } } From 2ede55bd8c0a4cce75e14f991666ecbd99512efa Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Tue, 6 Jun 2017 13:55:02 +0300 Subject: [PATCH 128/136] do not fail on int,byte -> atomic long type migration --- .../typeMigration/rules/AtomicConversionRule.java | 8 ++++---- .../refactoring/TypeMigrationByAtomicRuleTest.java | 4 ++++ .../literalMigration/after/Test.items | 8 ++++++++ .../literalMigration/after/Test.java | 5 +++++ .../literalMigration/before/Test.java | 3 +++ 5 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 java/typeMigration/testData/refactoring/typeMigrationByAtomic/literalMigration/after/Test.items create mode 100644 java/typeMigration/testData/refactoring/typeMigrationByAtomic/literalMigration/after/Test.java create mode 100644 java/typeMigration/testData/refactoring/typeMigrationByAtomic/literalMigration/before/Test.java diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/AtomicConversionRule.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/AtomicConversionRule.java index 86be70f05ee9..2acf82df2ac3 100644 --- a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/AtomicConversionRule.java +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/AtomicConversionRule.java @@ -40,15 +40,15 @@ public class AtomicConversionRule extends TypeConversionRule { } private static boolean isAtomicTypeMigration(PsiType from, PsiClassType to, PsiExpression context) { - if (PsiType.INT.equals(from) && to.getCanonicalText().equals(AtomicInteger.class.getName())) { + if (PsiType.INT.isAssignableFrom(from) && to.getCanonicalText().equals(AtomicInteger.class.getName())) { + return true; + } + if (PsiType.LONG.isAssignableFrom(from) && to.getCanonicalText().equals(AtomicLong.class.getName())) { return true; } if (from.equals(PsiType.INT.createArrayType()) && to.getCanonicalText().equals(AtomicIntegerArray.class.getName())) { return true; } - if (PsiType.LONG.equals(from) && to.getCanonicalText().equals(AtomicLong.class.getName())) { - return true; - } if (from.equals(PsiType.LONG.createArrayType()) && to.getCanonicalText().equals(AtomicLongArray.class.getName())) { return true; } diff --git a/java/typeMigration/test/com/intellij/refactoring/TypeMigrationByAtomicRuleTest.java b/java/typeMigration/test/com/intellij/refactoring/TypeMigrationByAtomicRuleTest.java index aeecfe6bfdc1..9e781175898f 100644 --- a/java/typeMigration/test/com/intellij/refactoring/TypeMigrationByAtomicRuleTest.java +++ b/java/typeMigration/test/com/intellij/refactoring/TypeMigrationByAtomicRuleTest.java @@ -100,4 +100,8 @@ public class TypeMigrationByAtomicRuleTest extends TypeMigrationTestBase{ public void testChainedInitialization() { doTestFieldType("a", myJavaFacade.getElementFactory().createTypeFromText("java.util.concurrent.atomic.AtomicInteger", null)); } + + public void testLiteralMigration() { + doTestFieldType("a", myJavaFacade.getElementFactory().createTypeFromText("java.util.concurrent.atomic.AtomicLong", null)); + } } \ No newline at end of file diff --git a/java/typeMigration/testData/refactoring/typeMigrationByAtomic/literalMigration/after/Test.items b/java/typeMigration/testData/refactoring/typeMigrationByAtomic/literalMigration/after/Test.items new file mode 100644 index 000000000000..33f6b9a33cf9 --- /dev/null +++ b/java/typeMigration/testData/refactoring/typeMigrationByAtomic/literalMigration/after/Test.items @@ -0,0 +1,8 @@ +Types: +PsiField:a : java.util.concurrent.atomic.AtomicLong + +Conversions: +100 -> new java.util.concurrent.atomic.AtomicLong($val$) $val$ 100 + +New expression type changes: +Fails: diff --git a/java/typeMigration/testData/refactoring/typeMigrationByAtomic/literalMigration/after/Test.java b/java/typeMigration/testData/refactoring/typeMigrationByAtomic/literalMigration/after/Test.java new file mode 100644 index 000000000000..ec7370c3df80 --- /dev/null +++ b/java/typeMigration/testData/refactoring/typeMigrationByAtomic/literalMigration/after/Test.java @@ -0,0 +1,5 @@ +import java.util.concurrent.atomic.AtomicLong; + +class Test { + AtomicLong a = new AtomicLong(100); +} \ No newline at end of file diff --git a/java/typeMigration/testData/refactoring/typeMigrationByAtomic/literalMigration/before/Test.java b/java/typeMigration/testData/refactoring/typeMigrationByAtomic/literalMigration/before/Test.java new file mode 100644 index 000000000000..e229caf913c4 --- /dev/null +++ b/java/typeMigration/testData/refactoring/typeMigrationByAtomic/literalMigration/before/Test.java @@ -0,0 +1,3 @@ +class Test { + long a = 100; +} \ No newline at end of file From 781137ab585d82424c3c7361ca4db66be226e32c Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Tue, 6 Jun 2017 14:10:53 +0300 Subject: [PATCH 129/136] move test for java 6 thread local to separated class --- .../ConvertToThreadLocalIntention6Test.java | 43 +++++++++++++++++++ .../refactoring/AllTypeMigrationTests.java | 2 + .../afterJava6.java | 0 .../beforeJava6.java | 0 4 files changed, 45 insertions(+) create mode 100644 java/typeMigration/test/com/intellij/codeInsight/ConvertToThreadLocalIntention6Test.java rename java/typeMigration/testData/intentions/{threadLocal => threadLocal6}/afterJava6.java (100%) rename java/typeMigration/testData/intentions/{threadLocal => threadLocal6}/beforeJava6.java (100%) diff --git a/java/typeMigration/test/com/intellij/codeInsight/ConvertToThreadLocalIntention6Test.java b/java/typeMigration/test/com/intellij/codeInsight/ConvertToThreadLocalIntention6Test.java new file mode 100644 index 000000000000..6b65b2c745dc --- /dev/null +++ b/java/typeMigration/test/com/intellij/codeInsight/ConvertToThreadLocalIntention6Test.java @@ -0,0 +1,43 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight; + +import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase; +import com.intellij.pom.java.LanguageLevel; +import com.intellij.testFramework.PlatformTestUtil; +import org.jetbrains.annotations.NotNull; + +public class ConvertToThreadLocalIntention6Test extends LightQuickFixParameterizedTestCase { + @Override + protected String getBasePath() { + return "/intentions/threadLocal6"; + } + + @NotNull + @Override + protected String getTestDataPath() { + return PlatformTestUtil.getCommunityPath() + "/java/typeMigration/testData"; + } + + public void test() throws Exception { + doAllTests(); + } + + @Override + protected LanguageLevel getLanguageLevel() { + return LanguageLevel.JDK_1_7; + } +} diff --git a/java/typeMigration/test/com/intellij/refactoring/AllTypeMigrationTests.java b/java/typeMigration/test/com/intellij/refactoring/AllTypeMigrationTests.java index 32640fc85dde..331e5e279a2a 100644 --- a/java/typeMigration/test/com/intellij/refactoring/AllTypeMigrationTests.java +++ b/java/typeMigration/test/com/intellij/refactoring/AllTypeMigrationTests.java @@ -1,6 +1,7 @@ package com.intellij.refactoring; import com.intellij.codeInsight.ConvertToAtomicIntentionTest; +import com.intellij.codeInsight.ConvertToThreadLocalIntention6Test; import com.intellij.codeInsight.ConvertToThreadLocalIntentionTest; import com.intellij.codeInsight.inspections.GuavaInspectionTest; import junit.framework.Test; @@ -19,6 +20,7 @@ public class AllTypeMigrationTests { suite.addTestSuite(WildcardTypeMigrationTest.class); suite.addTestSuite(ConvertToAtomicIntentionTest.class); suite.addTestSuite(ConvertToThreadLocalIntentionTest.class); + suite.addTestSuite(ConvertToThreadLocalIntention6Test.class); suite.addTestSuite(GuavaInspectionTest.class); return suite; } diff --git a/java/typeMigration/testData/intentions/threadLocal/afterJava6.java b/java/typeMigration/testData/intentions/threadLocal6/afterJava6.java similarity index 100% rename from java/typeMigration/testData/intentions/threadLocal/afterJava6.java rename to java/typeMigration/testData/intentions/threadLocal6/afterJava6.java diff --git a/java/typeMigration/testData/intentions/threadLocal/beforeJava6.java b/java/typeMigration/testData/intentions/threadLocal6/beforeJava6.java similarity index 100% rename from java/typeMigration/testData/intentions/threadLocal/beforeJava6.java rename to java/typeMigration/testData/intentions/threadLocal6/beforeJava6.java From 6487bb691395f96cacf384459a3d44792c938b97 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Tue, 6 Jun 2017 12:42:17 +0300 Subject: [PATCH 130/136] disable assign parameter to final field (IDEA-173815) for constructors with chaining calls, for non-constructors --- .../impl/AssignFieldFromParameterAction.java | 18 ++++++++++++++++++ .../beforePassedToThisCall.java | 13 +++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/assignFieldFromParameter/beforePassedToThisCall.java diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/AssignFieldFromParameterAction.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/AssignFieldFromParameterAction.java index fa578c172089..4514c4f4bd63 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/AssignFieldFromParameterAction.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/AssignFieldFromParameterAction.java @@ -48,6 +48,24 @@ public class AssignFieldFromParameterAction extends BaseIntentionAction { final PsiField field = findFieldToAssign(project, myParameter); if (field == null || type == null || !field.getType().isAssignableFrom(type)) return false; if (!field.getLanguage().isKindOf(JavaLanguage.INSTANCE)) return false; + PsiElement scope = myParameter.getDeclarationScope(); + if (scope instanceof PsiMethod && field.hasModifierProperty(PsiModifier.FINAL)) { + if (((PsiMethod)scope).isConstructor()) { + PsiCodeBlock body = ((PsiMethod)scope).getBody(); + LOG.assertTrue(body != null); + PsiStatement[] statements = body.getStatements(); + if (statements.length > 0 && statements[0] instanceof PsiExpressionStatement) { + PsiExpression expression = ((PsiExpressionStatement)statements[0]).getExpression(); + if (expression instanceof PsiMethodCallExpression && + PsiKeyword.THIS.equals(((PsiMethodCallExpression)expression).getMethodExpression().getReferenceName())) { + return false; + } + } + } + else { + return false; + } + } setText(CodeInsightBundle.message("intention.assign.field.from.parameter.text", field.getName())); return true; diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/assignFieldFromParameter/beforePassedToThisCall.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/assignFieldFromParameter/beforePassedToThisCall.java new file mode 100644 index 000000000000..586a4ed8bc53 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/assignFieldFromParameter/beforePassedToThisCall.java @@ -0,0 +1,13 @@ +// "Assign Parameter to Field 'myStr'" "false" + + +class Foo1 { + final String myStr; + Foo1(String str, int i) { + myStr = (str); + } + + Foo1(String str) { + this(str, 2); + } +} \ No newline at end of file From a19a52bf49563799307fb4affa26a01aafd987a3 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Tue, 6 Jun 2017 13:55:06 +0300 Subject: [PATCH 131/136] raw type to parameterized: warn about conflicts (IDEA-173770) --- .../RawTypeCanBeGenericInspection.java | 22 ++++++++++++------- .../typeMigration/TypeMigrationProcessor.java | 15 ++++++++----- .../makeTypeGeneric/Conflict.java | 8 +++++++ .../RawTypeCanBeGenericTest.java | 20 +++++++++++++++++ 4 files changed, 52 insertions(+), 13 deletions(-) rename java/{java-analysis-impl => java-impl}/src/com/intellij/codeInspection/miscGenerics/RawTypeCanBeGenericInspection.java (87%) create mode 100644 java/java-tests/testData/codeInspection/makeTypeGeneric/Conflict.java diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/miscGenerics/RawTypeCanBeGenericInspection.java b/java/java-impl/src/com/intellij/codeInspection/miscGenerics/RawTypeCanBeGenericInspection.java similarity index 87% rename from java/java-analysis-impl/src/com/intellij/codeInspection/miscGenerics/RawTypeCanBeGenericInspection.java rename to java/java-impl/src/com/intellij/codeInspection/miscGenerics/RawTypeCanBeGenericInspection.java index bd2202129b37..cefbd93158c4 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/miscGenerics/RawTypeCanBeGenericInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/miscGenerics/RawTypeCanBeGenericInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -18,8 +18,11 @@ package com.intellij.codeInspection.miscGenerics; import com.intellij.codeInspection.*; import com.intellij.openapi.project.Project; import com.intellij.psi.*; +import com.intellij.psi.search.PsiSearchHelper; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.TypeConversionUtil; +import com.intellij.refactoring.typeMigration.TypeMigrationProcessor; +import com.intellij.refactoring.typeMigration.TypeMigrationRules; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -103,18 +106,21 @@ public class RawTypeCanBeGenericInspection extends BaseJavaBatchLocalInspectionT return InspectionsBundle.message("inspection.raw.variable.type.can.be.generic.family.quickfix"); } + @Override + public boolean startInWriteAction() { + return false; + } + @Override public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { final PsiElement element = descriptor.getStartElement().getParent(); if (element instanceof PsiVariable) { final PsiVariable variable = (PsiVariable)element; - final PsiTypeElement typeElement = variable.getTypeElement(); - if (typeElement != null) { - final PsiType type = getSuggestedType(variable); - if (type != null) { - final PsiElementFactory factory = JavaPsiFacade.getInstance(variable.getProject()).getElementFactory(); - typeElement.replace(factory.createTypeElement(type)); - } + final PsiType type = getSuggestedType(variable); + if (type != null) { + final TypeMigrationRules rules = new TypeMigrationRules(); + rules.setBoundScope(PsiSearchHelper.SERVICE.getInstance(project).getUseScope(variable)); + TypeMigrationProcessor.runHighlightingTypeMigration(project, null, rules, variable, type, false); } } } 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 9ddcf9b03aa8..c48fb4c9558b 100644 --- a/java/java-impl/src/com/intellij/refactoring/typeMigration/TypeMigrationProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/typeMigration/TypeMigrationProcessor.java @@ -16,7 +16,6 @@ package com.intellij.refactoring.typeMigration; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Ref; @@ -35,12 +34,18 @@ 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.*; -import com.intellij.util.containers.*; +import com.intellij.util.Function; +import com.intellij.util.Functions; +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.SmartList; +import com.intellij.util.containers.ContainerUtil; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; import static com.intellij.util.ObjectUtils.assertNotNull; @@ -137,7 +142,7 @@ public class TypeMigrationProcessor extends BaseRefactoringProcessor { protected boolean preprocessUsages(@NotNull Ref refUsages) { if (hasFailedConversions()) { if (ApplicationManager.getApplication().isUnitTestMode()) { - throw new RuntimeException(StringUtil.join(myLabeler.getFailedConversionsReport(), "\n")); + throw new BaseRefactoringProcessor.ConflictsInTestsException(Arrays.asList(myLabeler.getFailedConversionsReport())); } FailedConversionsDialog dialog = new FailedConversionsDialog(myLabeler.getFailedConversionsReport(), myProject); if (!dialog.showAndGet()) { diff --git a/java/java-tests/testData/codeInspection/makeTypeGeneric/Conflict.java b/java/java-tests/testData/codeInspection/makeTypeGeneric/Conflict.java new file mode 100644 index 000000000000..405b5d3be415 --- /dev/null +++ b/java/java-tests/testData/codeInspection/makeTypeGeneric/Conflict.java @@ -0,0 +1,8 @@ +import java.util.*; + +public class F { + { + List list= new ArrayList(); + list.add(""); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/RawTypeCanBeGenericTest.java b/java/java-tests/testSrc/com/intellij/java/codeInspection/RawTypeCanBeGenericTest.java index dd4f63dcef20..2ba15e4ec490 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInspection/RawTypeCanBeGenericTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/RawTypeCanBeGenericTest.java @@ -20,8 +20,11 @@ import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInspection.InspectionsBundle; import com.intellij.codeInspection.miscGenerics.RawTypeCanBeGenericInspection; import com.intellij.openapi.roots.ModuleRootModificationUtil; +import com.intellij.refactoring.BaseRefactoringProcessor; import com.intellij.testFramework.IdeaTestUtil; +import com.intellij.testFramework.LightProjectDescriptor; import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; +import org.jetbrains.annotations.NotNull; import java.util.List; @@ -63,6 +66,17 @@ public class RawTypeCanBeGenericTest extends LightCodeInsightFixtureTestCase { doTest(getMessage("list", "List")); } + public void testConflict() { + try { + doTest(getMessage("list", "List")); + fail("No conflict detected"); + } + catch (BaseRefactoringProcessor.ConflictsInTestsException e) { + assertEquals("Cannot convert type of expression "" from java.lang.String to T
", + e.getMessage()); + } + } + public void testAtInitializer() { assertIntentionNotAvailable(getMessagePrefix()); } @@ -92,4 +106,10 @@ public class RawTypeCanBeGenericTest extends LightCodeInsightFixtureTestCase { String message = InspectionsBundle.message("inspection.raw.variable.type.can.be.generic.quickfix", "@", "@"); return message.substring(0, message.indexOf("@")); } + + @NotNull + @Override + protected LightProjectDescriptor getProjectDescriptor() { + return JAVA_1_6; + } } From 8b891ddee097136227274d77565c0888ce0d75de Mon Sep 17 00:00:00 2001 From: Konstantin Ulitin Date: Tue, 6 Jun 2017 15:19:29 +0300 Subject: [PATCH 132/136] Breadcrumbs: move method to have it available in tests --- .../ui/breadcrumbs/BreadcrumbsUtil.java | 37 +++++++++++++++++++ .../impl/CodeInsightTestFixtureImpl.java | 5 ++- .../breadcrumbs/BreadcrumbsXmlWrapper.java | 23 ++---------- 3 files changed, 44 insertions(+), 21 deletions(-) create mode 100644 platform/editor-ui-api/src/com/intellij/ui/breadcrumbs/BreadcrumbsUtil.java diff --git a/platform/editor-ui-api/src/com/intellij/ui/breadcrumbs/BreadcrumbsUtil.java b/platform/editor-ui-api/src/com/intellij/ui/breadcrumbs/BreadcrumbsUtil.java new file mode 100644 index 000000000000..3393e3f517e8 --- /dev/null +++ b/platform/editor-ui-api/src/com/intellij/ui/breadcrumbs/BreadcrumbsUtil.java @@ -0,0 +1,37 @@ +/* + * Copyright 2000-2017 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.breadcrumbs; + +import com.intellij.lang.Language; +import org.jetbrains.annotations.NotNull; + +public class BreadcrumbsUtil { + + public static BreadcrumbsProvider getInfoProvider(@NotNull Language language) { + BreadcrumbsProvider[] providers = BreadcrumbsProvider.EP_NAME.getExtensions(); + while (language != null) { + for (BreadcrumbsProvider provider : providers) { + for (Language supported : provider.getLanguages()) { + if (language.is(supported)) { + return provider; + } + } + } + language = language.getBaseLanguage(); + } + return null; + } +} diff --git a/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java b/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java index dd3fd78053ae..cda617917d11 100644 --- a/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java +++ b/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java @@ -117,6 +117,7 @@ import com.intellij.testFramework.*; import com.intellij.testFramework.fixtures.*; import com.intellij.testFramework.utils.inlays.InlayHintsChecker; import com.intellij.ui.breadcrumbs.BreadcrumbsProvider; +import com.intellij.ui.breadcrumbs.BreadcrumbsUtil; import com.intellij.ui.components.breadcrumbs.Crumb; import com.intellij.usageView.UsageInfo; import com.intellij.util.*; @@ -1791,8 +1792,8 @@ public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsig } final Language language = element.getContainingFile().getLanguage(); - final BreadcrumbsProvider provider = ContainerUtil.find(BreadcrumbsProvider.EP_NAME.getExtensions(), - p -> Arrays.asList(p.getLanguages()).contains(language)); + final BreadcrumbsProvider provider = BreadcrumbsUtil.getInfoProvider(language); + if (provider == null) { return Collections.emptyList(); } diff --git a/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java b/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java index 5f0d72f7af76..6006f1555319 100644 --- a/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java +++ b/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java @@ -48,6 +48,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.ui.Gray; import com.intellij.ui.breadcrumbs.BreadcrumbsProvider; +import com.intellij.ui.breadcrumbs.BreadcrumbsUtil; import com.intellij.ui.components.breadcrumbs.Crumb; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.MouseEventAdapter; @@ -241,7 +242,7 @@ public class BreadcrumbsXmlWrapper extends JComponent implements Disposable { private static BreadcrumbsProvider findProviderForElement(@NotNull PsiElement element, BreadcrumbsProvider defaultProvider) { Language language = element.getLanguage(); if (!EditorSettingsExternalizable.getInstance().isBreadcrumbsShownFor(language.getID())) return defaultProvider; - BreadcrumbsProvider provider = getInfoProvider(language); + BreadcrumbsProvider provider = BreadcrumbsUtil.getInfoProvider(language); return provider == null ? defaultProvider : provider; } @@ -418,11 +419,11 @@ public class BreadcrumbsXmlWrapper extends JComponent implements Disposable { Language baseLang = viewProvider.getBaseLanguage(); if (checkSettings && !settings.isBreadcrumbsShownFor(baseLang.getID())) return null; - BreadcrumbsProvider provider = getInfoProvider(baseLang); + BreadcrumbsProvider provider = BreadcrumbsUtil.getInfoProvider(baseLang); if (provider == null) { for (Language language : viewProvider.getLanguages()) { if (!checkSettings || settings.isBreadcrumbsShownFor(language.getID())) { - provider = getInfoProvider(language); + provider = BreadcrumbsUtil.getInfoProvider(language); if (provider != null) break; } } @@ -491,22 +492,6 @@ public class BreadcrumbsXmlWrapper extends JComponent implements Disposable { breadcrumbs.setCrumbs(null); } - @Nullable - private static BreadcrumbsProvider getInfoProvider(@NotNull Language language) { - BreadcrumbsProvider[] providers = BreadcrumbsProvider.EP_NAME.getExtensions(); - while (language != null) { - for (BreadcrumbsProvider provider : providers) { - for (Language supported : provider.getLanguages()) { - if (language.is(supported)) { - return provider; - } - } - } - language = language.getBaseLanguage(); - } - return null; - } - private static class MyUpdate extends Update { private final BreadcrumbsXmlWrapper myBreadcrumbsComponent; From aed6b68d441ef552ec86aa0a18ed3cbc19adf788 Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Tue, 6 Jun 2017 15:40:18 +0300 Subject: [PATCH 133/136] remember 100 last debugger tree states to keep expansion across frames --- .../util/resources/misc/registry.properties | 1 + .../impl/frame/XVariablesViewBase.java | 31 ++++++++++++------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index 429de9f36601..1e07581f188c 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -277,6 +277,7 @@ debugger.capture.points.annotations=false debugger.resume.yourkit.threads=false debugger.keep.step.requests=false debugger.enable.memory.view=true +debugger.tree.states.depth=100 analyze.exceptions.on.the.fly=false analyze.exceptions.on.the.fly.description=Automatically analyze clipboard on frame activation,\ diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XVariablesViewBase.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XVariablesViewBase.java index 2474d73f95b3..205f176968c0 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XVariablesViewBase.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XVariablesViewBase.java @@ -51,18 +51,24 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; +import java.util.LinkedHashMap; +import java.util.Map; /** * @author nik */ public abstract class XVariablesViewBase extends XDebugView { private final XDebuggerTreePanel myTreePanel; - private XDebuggerTreeState myTreeState; - private XDebuggerTreeRestorer myTreeRestorer; - - private Object myFrameEqualityObject; private MySelectionListener mySelectionListener; + private XDebuggerTreeRestorer myTreeRestorer; + private final Map myTreeStates = new LinkedHashMap() { + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > Registry.get("debugger.tree.states.depth").asInteger(); + } + }; + protected XVariablesViewBase(@NotNull Project project, @NotNull XDebuggerEditorsProvider editorsProvider, @Nullable XValueMarkers markers) { myTreePanel = new XDebuggerTreePanel( project, editorsProvider, this, null, this instanceof XWatchesView ? XDebuggerActions.WATCHES_TREE_POPUP_GROUP : XDebuggerActions.VARIABLES_TREE_POPUP_GROUP, markers); @@ -79,11 +85,14 @@ public abstract class XVariablesViewBase extends XDebugView { project.putUserData(XVariablesView.DEBUG_VARIABLES, new XVariablesView.InlineVariablesInfo()); clearInlays(tree); Object newEqualityObject = stackFrame.getEqualityObject(); - if (myFrameEqualityObject != null && newEqualityObject != null && myFrameEqualityObject.equals(newEqualityObject) - && myTreeState != null) { - disposeTreeRestorer(); - myTreeRestorer = myTreeState.restoreState(tree); + if (newEqualityObject != null) { + XDebuggerTreeState state = myTreeStates.get(newEqualityObject); + if (state != null) { + disposeTreeRestorer(); + myTreeRestorer = state.restoreState(tree); + } } + if (position != null && Registry.is("debugger.valueTooltipAutoShowOnSelection")) { registerInlineEvaluator(stackFrame, position, project); } @@ -120,9 +129,9 @@ public abstract class XVariablesViewBase extends XDebugView { protected void saveCurrentTreeState(@Nullable XStackFrame stackFrame) { removeSelectionListener(); - myFrameEqualityObject = stackFrame != null ? stackFrame.getEqualityObject() : null; - if (myTreeRestorer == null || myTreeRestorer.isFinished()) { - myTreeState = XDebuggerTreeState.saveState(getTree()); + Object equalityObject = stackFrame != null ? stackFrame.getEqualityObject() : null; + if (equalityObject != null && (myTreeRestorer == null || myTreeRestorer.isFinished())) { + myTreeStates.put(equalityObject, XDebuggerTreeState.saveState(getTree())); } disposeTreeRestorer(); } From 1af811e3c1cfdc7746dfdc859f9c40332bb55487 Mon Sep 17 00:00:00 2001 From: Rustam Vishnyakov Date: Tue, 6 Jun 2017 15:52:53 +0300 Subject: [PATCH 134/136] Minor insets/gaps UI adjustments --- .../options/colors/AbstractFontOptionsPanel.java | 15 +++++++++++++-- .../application/options/colors/FontOptions.java | 4 ++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/application/options/colors/AbstractFontOptionsPanel.java b/platform/lang-impl/src/com/intellij/application/options/colors/AbstractFontOptionsPanel.java index 509478b12a95..3c2d23f53478 100644 --- a/platform/lang-impl/src/com/intellij/application/options/colors/AbstractFontOptionsPanel.java +++ b/platform/lang-impl/src/com/intellij/application/options/colors/AbstractFontOptionsPanel.java @@ -85,10 +85,12 @@ public abstract class AbstractFontOptionsPanel extends JPanel implements Options @SuppressWarnings("unchecked") protected final JPanel createFontSettingsPanel() { + Insets baseInsets = getInsets(0, 0); + JPanel fontPanel = new JPanel(new GridBagLayout()); GridBagConstraints c = new GridBagConstraints(); c.anchor = GridBagConstraints.WEST; - c.insets = JBUI.insets(BASE_INSET, BASE_INSET, 0, 0); + c.insets = baseInsets; c.gridx = 0; c.gridy = 0; @@ -99,10 +101,12 @@ public abstract class AbstractFontOptionsPanel extends JPanel implements Options fontPanel.add(myPrimaryCombo, c); c.gridx = 2; + c.insets = getInsets(0, BASE_INSET); fontPanel.add(myOnlyMonospacedCheckBox, c); c.gridx = 0; c.gridy = 1; + c.insets = baseInsets; mySizeLabel = new JLabel(ApplicationBundle.message("editbox.font.size")); fontPanel.add(mySizeLabel, c); @@ -119,13 +123,14 @@ public abstract class AbstractFontOptionsPanel extends JPanel implements Options c.gridy = 3; c.gridx = 0; - c.insets = JBUI.insets(BASE_INSET + ADDITIONAL_VERTICAL_GAP, BASE_INSET, 0, 0); + c.insets = getInsets(ADDITIONAL_VERTICAL_GAP, 0); mySecondaryFontLabel = new JLabel(ApplicationBundle.message("secondary.font")); mySecondaryFontLabel.setLabelFor(mySecondaryCombo); fontPanel.add(mySecondaryFontLabel, c); c.gridx = 1; fontPanel.add(mySecondaryCombo, c); c.gridx = 2; + c.insets = getInsets(ADDITIONAL_VERTICAL_GAP, BASE_INSET); JLabel fallbackLabel = new JLabel(ApplicationBundle.message("label.fallback.fonts.list.description")); fallbackLabel.setEnabled(false); fontPanel.add(fallbackLabel, c); @@ -145,6 +150,8 @@ public abstract class AbstractFontOptionsPanel extends JPanel implements Options c.gridx = 0; c.gridy = 4; c.gridwidth = 2; + c.insets = getInsets(ADDITIONAL_VERTICAL_GAP, 0); + c.insets.bottom = BASE_INSET; fontPanel.add(panel, c); myOnlyMonospacedCheckBox.setBorder(null); @@ -233,6 +240,10 @@ public abstract class AbstractFontOptionsPanel extends JPanel implements Options return fontPanel; } + private static Insets getInsets(int extraTopSpacing, int extraLeftSpacing) { + return JBUI.insets(BASE_INSET + extraTopSpacing, BASE_INSET + extraLeftSpacing, 0, 0); + } + protected void setDelegatingPreferences(boolean isDelegating) { } diff --git a/platform/lang-impl/src/com/intellij/application/options/colors/FontOptions.java b/platform/lang-impl/src/com/intellij/application/options/colors/FontOptions.java index be16c024c59e..fb25dafa7024 100644 --- a/platform/lang-impl/src/com/intellij/application/options/colors/FontOptions.java +++ b/platform/lang-impl/src/com/intellij/application/options/colors/FontOptions.java @@ -69,7 +69,7 @@ public class FontOptions extends AbstractFontOptionsPanel { c.gridx = 0; c.gridy = 0; c.gridwidth = 2; - c.insets = JBUI.insets(BASE_INSET, BASE_INSET, ADDITIONAL_VERTICAL_GAP, 0); + c.insets = JBUI.insets(BASE_INSET * 2, BASE_INSET, ADDITIONAL_VERTICAL_GAP, 0); c.anchor = GridBagConstraints.LINE_START; topPanel.add(inheritBox, c); c.gridy = 1; @@ -102,7 +102,7 @@ public class FontOptions extends AbstractFontOptionsPanel { }); inheritPanel.add(myInheritFontCheckbox); inheritPanel.add(new JLabel(getOverwriteFontTitle())); - inheritPanel.add(Box.createRigidArea(JBDimension.create(new Dimension(5,0)))); + inheritPanel.add(Box.createRigidArea(JBDimension.create(new Dimension(10,0)))); inheritPanel.add(grayed(new JLabel("("))); inheritPanel.add(grayed(createHyperlinkLabel())); inheritPanel.add(grayed(new JLabel(": "))); From e04255f4db3a9442fb06f19889dd297f07daf074 Mon Sep 17 00:00:00 2001 From: "alexey.afanasiev" Date: Tue, 6 Jun 2017 16:11:35 +0300 Subject: [PATCH 135/136] IDEA-170804 Idea does't support @Newify annotation. IDEA-CR-21405 --- .../GroovyApplicabilityProvider.java | 54 +++++++++++ .../GroovyNamedArgumentProvider.java | 14 ++- ...roovyConstructorNamedArgumentProvider.java | 3 +- .../GroovyNewExprNamedArgumentProvider.kt | 2 +- .../plugins/groovy/lang/psi/util/PsiUtil.java | 36 ++------ .../ConstructorMapApplicabilityProvider.kt | 50 +++++++++++ .../GroovyNewifyNamedArgumentProvider.kt | 12 ++- .../NewifyConstructorApplicabilityProvider.kt | 27 ++++++ .../NewifyMemberContributor.kt | 90 +++++++++++-------- plugins/groovy/src/META-INF/plugin.xml | 7 +- .../lang/resolve/NewifySupportTest.groovy | 69 ++++++++++++++ .../lang/resolve/ResolveMethodTest.groovy | 19 ++++ 12 files changed, 297 insertions(+), 86 deletions(-) create mode 100644 plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/extensions/GroovyApplicabilityProvider.java create mode 100644 plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ConstructorMapApplicabilityProvider.kt rename plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/{ => resolve/newify}/GroovyNewifyNamedArgumentProvider.kt (65%) create mode 100644 plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/newify/NewifyConstructorApplicabilityProvider.kt rename plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/{noncode => newify}/NewifyMemberContributor.kt (52%) diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/extensions/GroovyApplicabilityProvider.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/extensions/GroovyApplicabilityProvider.java new file mode 100644 index 000000000000..cf42409d8edb --- /dev/null +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/extensions/GroovyApplicabilityProvider.java @@ -0,0 +1,54 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.plugins.groovy.extensions; + +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiSubstitutor; +import com.intellij.psi.PsiType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.groovy.lang.psi.impl.signatures.GrClosureSignatureUtil.ApplicabilityResult; + +public abstract class GroovyApplicabilityProvider { + + public static final ExtensionPointName EP_NAME = + ExtensionPointName.create("org.intellij.groovy.applicabilityProvider"); + + /** + * @return null if provider could not be applied in this case + */ + @Nullable + public abstract ApplicabilityResult isApplicable(@NotNull PsiType[] argumentTypes, + @NotNull PsiMethod method, + @Nullable PsiSubstitutor substitutor, + @Nullable PsiElement place, + final boolean eraseParameterTypes); + + @Nullable + public static ApplicabilityResult checkProviders(@NotNull PsiType[] argumentTypes, + @NotNull PsiMethod method, + @Nullable PsiSubstitutor substitutor, + @Nullable PsiElement place, + final boolean eraseParameterTypes) { + for (GroovyApplicabilityProvider applicabilityProvider : EP_NAME.getExtensions()) { + ApplicabilityResult result = applicabilityProvider.isApplicable(argumentTypes, method, substitutor, place, eraseParameterTypes); + if (result != null) return result; + } + return null; + } +} diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/extensions/GroovyNamedArgumentProvider.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/extensions/GroovyNamedArgumentProvider.java index 6e591c3e2771..cc58a9cc5925 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/extensions/GroovyNamedArgumentProvider.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/extensions/GroovyNamedArgumentProvider.java @@ -80,8 +80,8 @@ public abstract class GroovyNamedArgumentProvider { @Nullable public static Map getNamedArgumentsFromAllProviders(@NotNull GrCall call, - @Nullable String argumentName, - boolean forCompletion) { + @Nullable String argumentName, + boolean forCompletion) { Map namedArguments = new HashMap() { @Override public NamedArgumentDescriptor put(String key, NamedArgumentDescriptor value) { @@ -105,15 +105,17 @@ public abstract class GroovyNamedArgumentProvider { else { boolean mapExpected = false; for (GroovyResolveResult result : callVariants) { + for (GroovyNamedArgumentProvider namedArgumentProvider : EP_NAME.getExtensions()) { + namedArgumentProvider.getNamedArguments(call, result, argumentName, forCompletion, namedArguments); + } PsiElement element = result.getElement(); if (element instanceof GrAccessorMethod) continue; if (element instanceof PsiMethod) { PsiMethod method = (PsiMethod)element; - PsiMethod mirror = PsiUtil.handleMirrorMethod(method); PsiParameter[] parameters = method.getParameterList().getParameters(); - if (!mirror.isConstructor() && !(parameters.length > 0 && canBeMap(parameters[0]))) continue; + if (!method.isConstructor() && !(parameters.length > 0 && canBeMap(parameters[0]))) continue; mapExpected = true; @@ -131,10 +133,6 @@ public abstract class GroovyNamedArgumentProvider { } } - for (GroovyNamedArgumentProvider namedArgumentProvider : EP_NAME.getExtensions()) { - namedArgumentProvider.getNamedArguments(call, result, argumentName, forCompletion, namedArguments); - } - if (element instanceof GrVariable && InheritanceUtil.isInheritor(((GrVariable)element).getTypeGroovy(), GroovyCommonClassNames.GROOVY_LANG_CLOSURE)) { mapExpected = true; diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/GroovyConstructorNamedArgumentProvider.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/GroovyConstructorNamedArgumentProvider.java index 06914787dcca..b49193fe7f1e 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/GroovyConstructorNamedArgumentProvider.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/GroovyConstructorNamedArgumentProvider.java @@ -52,7 +52,7 @@ public abstract class GroovyConstructorNamedArgumentProvider extends GroovyNamed private static final String METACLASS = "metaClass"; @NotNull - abstract List getCorrespondingClasses(@NotNull GrCall call, @NotNull GroovyResolveResult resolveResult); + public abstract List getCorrespondingClasses(@NotNull GrCall call, @NotNull GroovyResolveResult resolveResult); @Override public void getNamedArguments(@NotNull GrCall call, @@ -74,7 +74,6 @@ public abstract class GroovyConstructorNamedArgumentProvider extends GroovyNamed processClass(call, classType, argumentName, result); } - } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/GroovyNewExprNamedArgumentProvider.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/GroovyNewExprNamedArgumentProvider.kt index ffe6eaf95967..b2533d265c1f 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/GroovyNewExprNamedArgumentProvider.kt +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/GroovyNewExprNamedArgumentProvider.kt @@ -23,7 +23,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrNewExp class GroovyNewExprNamedArgumentProvider : GroovyConstructorNamedArgumentProvider() { - internal override fun getCorrespondingClasses(call: GrCall, resolveResult: GroovyResolveResult): List { + override fun getCorrespondingClasses(call: GrCall, resolveResult: GroovyResolveResult): List { val newExpr = call as? GrNewExpression ?: return emptyList() val resolve = resolveResult.element (resolve as? PsiMethod)?.let { diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java index 69bc8c4ef32b..6e1850d55269 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java @@ -40,6 +40,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils; import org.jetbrains.plugins.groovy.config.GroovyConfigUtils; +import org.jetbrains.plugins.groovy.extensions.GroovyApplicabilityProvider; import org.jetbrains.plugins.groovy.lang.groovydoc.psi.api.GrDocComment; import org.jetbrains.plugins.groovy.lang.lexer.GroovyLexer; import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; @@ -166,7 +167,7 @@ public class PsiUtil { } public static boolean isApplicable(@Nullable PsiType[] argumentTypes, - PsiMethod method, + @NotNull PsiMethod method, PsiSubstitutor substitutor, PsiElement place, final boolean eraseParameterTypes) { @@ -175,30 +176,18 @@ public class PsiUtil { } public static GrClosureSignatureUtil.ApplicabilityResult isApplicableConcrete(@Nullable PsiType[] argumentTypes, - PsiMethod method, + @NotNull PsiMethod method, PsiSubstitutor substitutor, PsiElement place, final boolean eraseParameterTypes) { if (argumentTypes == null) return GrClosureSignatureUtil.ApplicabilityResult.canBeApplicable; GrClosureSignature signature = GrClosureSignatureUtil.createSignature(method, substitutor, eraseParameterTypes); - //check for default constructor - if (method.isConstructor() || handleMirrorMethod(method).isConstructor()) { - final PsiParameter[] parameters = method.getParameterList().getParameters(); - if (parameters.length == 0 && argumentTypes.length == 1) { - return InheritanceUtil.isInheritor(argumentTypes[0], CommonClassNames.JAVA_UTIL_MAP) - ? GrClosureSignatureUtil.ApplicabilityResult.applicable - : GrClosureSignatureUtil.ApplicabilityResult.inapplicable; - } - if (parameters.length == 1 && - argumentTypes.length == 0 && - InheritanceUtil.isInheritor(parameters[0].getType(), CommonClassNames.JAVA_UTIL_MAP)) { - return GrClosureSignatureUtil.ApplicabilityResult.inapplicable; - } - } - LOG.assertTrue(signature != null); GrClosureSignatureUtil.ApplicabilityResult result = - GrClosureSignatureUtil.isSignatureApplicableConcrete(signature, argumentTypes, place); + GroovyApplicabilityProvider.checkProviders(argumentTypes, method, substitutor, place, eraseParameterTypes); + if (result != null) return result; + + result = GrClosureSignatureUtil.isSignatureApplicableConcrete(signature, argumentTypes, place); if (result != GrClosureSignatureUtil.ApplicabilityResult.inapplicable) { return result; } @@ -213,17 +202,6 @@ public class PsiUtil { return GrClosureSignatureUtil.ApplicabilityResult.inapplicable; } - @NotNull - public static PsiMethod handleMirrorMethod(@NotNull PsiMethod method) { - if (method instanceof PsiMirrorElement) { - PsiElement prototype = ((PsiMirrorElement)method).getPrototype(); - if (prototype instanceof PsiMethod) { - return (PsiMethod)prototype; - } - } - return method; - } - public static boolean isApplicable(@Nullable PsiType[] argumentTypes, GrClosureType type, GroovyPsiElement context) { diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ConstructorMapApplicabilityProvider.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ConstructorMapApplicabilityProvider.kt new file mode 100644 index 000000000000..9f418ff99e9b --- /dev/null +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ConstructorMapApplicabilityProvider.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package org.jetbrains.plugins.groovy.lang.resolve + +import com.intellij.psi.* +import com.intellij.psi.util.InheritanceUtil.isInheritor +import org.jetbrains.plugins.groovy.extensions.GroovyApplicabilityProvider +import org.jetbrains.plugins.groovy.lang.psi.impl.signatures.GrClosureSignatureUtil.ApplicabilityResult +import org.jetbrains.plugins.groovy.lang.psi.impl.signatures.GrClosureSignatureUtil.ApplicabilityResult.applicable +import org.jetbrains.plugins.groovy.lang.psi.impl.signatures.GrClosureSignatureUtil.ApplicabilityResult.inapplicable + + +open class ConstructorMapApplicabilityProvider : GroovyApplicabilityProvider() { + + open fun isConstructor(method: PsiMethod): Boolean { + return method.isConstructor + } + + override fun isApplicable(argumentTypes: Array, + method: PsiMethod, + substitutor: PsiSubstitutor?, + place: PsiElement?, + eraseParameterTypes: Boolean): ApplicabilityResult? { + if (!isConstructor(method)) return null + + val parameters = method.parameterList.parameters + if (parameters.isEmpty() && argumentTypes.size == 1) { + return if (isInheritor(argumentTypes[0], CommonClassNames.JAVA_UTIL_MAP)) applicable else inapplicable + } + if (parameters.size == 1 && argumentTypes.isEmpty() && isInheritor(parameters[0].type, CommonClassNames.JAVA_UTIL_MAP)) { + return inapplicable + } + return null + } +} \ No newline at end of file diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/GroovyNewifyNamedArgumentProvider.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/newify/GroovyNewifyNamedArgumentProvider.kt similarity index 65% rename from plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/GroovyNewifyNamedArgumentProvider.kt rename to plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/newify/GroovyNewifyNamedArgumentProvider.kt index fe7f9dea4ab8..36bddb5d5a8e 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/GroovyNewifyNamedArgumentProvider.kt +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/newify/GroovyNewifyNamedArgumentProvider.kt @@ -13,18 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.plugins.groovy.lang +package org.jetbrains.plugins.groovy.lang.resolve.newify import com.intellij.psi.PsiClass -import com.intellij.psi.PsiMethod -import com.intellij.psi.PsiMirrorElement +import org.jetbrains.plugins.groovy.lang.GroovyConstructorNamedArgumentProvider import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrCall class GroovyNewifyNamedArgumentProvider : GroovyConstructorNamedArgumentProvider() { - internal override fun getCorrespondingClasses(call: GrCall, resolveResult: GroovyResolveResult): List { - val prototype = (resolveResult.element as? PsiMirrorElement)?.prototype as? PsiMethod ?: return emptyList() - if (!prototype.isConstructor) return emptyList() - return prototype.containingClass?.let { listOf(it) } ?: emptyList() + override fun getCorrespondingClasses(call: GrCall, resolveResult: GroovyResolveResult): List { + val resolved = (resolveResult.element as? NewifyMemberContributor.NewifiedConstructor) ?: return emptyList() + return resolved.containingClass?.let { listOf(it) } ?: emptyList() } } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/newify/NewifyConstructorApplicabilityProvider.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/newify/NewifyConstructorApplicabilityProvider.kt new file mode 100644 index 000000000000..853ff7dffcd7 --- /dev/null +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/newify/NewifyConstructorApplicabilityProvider.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package org.jetbrains.plugins.groovy.lang.resolve.newify + +import com.intellij.psi.PsiMethod +import org.jetbrains.plugins.groovy.lang.resolve.ConstructorMapApplicabilityProvider + +class NewifyConstructorApplicabilityProvider : ConstructorMapApplicabilityProvider() { + override fun isConstructor(method: PsiMethod): Boolean { + return method is NewifyMemberContributor.NewifiedConstructor + } +} \ No newline at end of file diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/noncode/NewifyMemberContributor.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/newify/NewifyMemberContributor.kt similarity index 52% rename from plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/noncode/NewifyMemberContributor.kt rename to plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/newify/NewifyMemberContributor.kt index 871a65d737fc..2e0d648ea065 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/noncode/NewifyMemberContributor.kt +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/newify/NewifyMemberContributor.kt @@ -13,20 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.plugins.groovy.lang.resolve.noncode +package org.jetbrains.plugins.groovy.lang.resolve.newify -import com.intellij.lang.java.JavaLanguage import com.intellij.psi.* -import com.intellij.psi.impl.light.LightMethod -import com.intellij.psi.impl.light.LightModifierList +import com.intellij.psi.impl.light.LightMethodBuilder import com.intellij.psi.scope.PsiScopeProcessor import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression import org.jetbrains.plugins.groovy.lang.psi.impl.GrAnnotationUtil +import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil import org.jetbrains.plugins.groovy.lang.psi.util.getParents import org.jetbrains.plugins.groovy.lang.resolve.NonCodeMembersContributor import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil internal val newifyAnnotationFqn = "groovy.lang.Newify" +internal val newifyOriginInfo = "by @Newify" class NewifyMemberContributor : NonCodeMembersContributor() { override fun processDynamicElements(qualifierType: PsiType, @@ -38,14 +38,16 @@ class NewifyMemberContributor : NonCodeMembersContributor() { val qualifier = getQualifier(place) for (annotation in listNewifyAnnotations(place)) { val newifiedClasses = GrAnnotationUtil.getClassArrayValue(annotation, "value", true) - qualifier ?: newifiedClasses.flatMap { it.constructors.asList() }.forEach { - ResolveUtil.processElement(processor, NewifiedConstructor(it, "by @Newify", it.name, true), state) + + qualifier ?: newifiedClasses.flatMap { buildConstructors(it, it.name, true) }.forEach { + ResolveUtil.processElement(processor, it, state) } val createNewMethods = GrAnnotationUtil.inferBooleanAttributeNotNull(annotation, "auto") val type = (qualifier as? GrReferenceExpression)?.resolve() as? PsiClass if (type != null && createNewMethods) { - type.constructors.forEach { - ResolveUtil.processElement(processor, NewifiedConstructor(it, "by @Newify", "new", false), state) + val constructors = buildConstructors(type, "new", false) + constructors.forEach { + ResolveUtil.processElement(processor, it, state) } } } @@ -67,36 +69,50 @@ class NewifyMemberContributor : NonCodeMembersContributor() { return (elem as? GrReferenceExpression)?.qualifierExpression } - class NewifiedConstructor(val myPrototype: PsiMethod, - val myOriginInfo: String, - val newName: String, - val asConstructor: Boolean) - : LightMethod(myPrototype.manager, myPrototype, myPrototype.containingClass!!), OriginInfoAwareElement, PsiMirrorElement { - override fun getPrototype(): PsiElement { - return myPrototype + fun buildConstructors(clazz: PsiClass, newName: String?, asConstructor: Boolean): List { + newName ?: return emptyList() + val constructors = clazz.constructors + if (constructors.isNotEmpty()) { + return constructors.mapNotNull { buildNewifiedConstructor(it, newName, asConstructor) } } - - val myModifierList: LightModifierList = LightModifierList(myPrototype.manager, JavaLanguage.INSTANCE, PsiModifier.STATIC) - - - override fun getName(): String { - return newName - } - - override fun getOriginInfo(): String { - return myOriginInfo - } - - override fun hasModifierProperty(name: String): Boolean { - return myModifierList.hasModifierProperty(name) - } - - override fun getModifierList(): PsiModifierList { - return myModifierList - } - - override fun isConstructor(): Boolean { - return asConstructor + else { + return listOf(buildNewifiedConstructor(clazz, newName, asConstructor)) } } + + fun buildNewifiedConstructor(myPrototype: PsiMethod, newName: String, asConstructor: Boolean): NewifiedConstructor? { + val builder = NewifiedConstructor(myPrototype.manager, newName) + val psiClass = myPrototype.containingClass ?: return null + builder.containingClass = psiClass + builder.setMethodReturnType(TypesUtil.createType(psiClass)) + builder.navigationElement = myPrototype + builder.isConstructor = asConstructor + myPrototype.parameterList.parameters.forEach { + builder.addParameter(it) + } + myPrototype.throwsList.referencedTypes.forEach { + builder.addException(it) + } + myPrototype.typeParameters.forEach { + builder.addTypeParameter(it) + } + return builder + } + + fun buildNewifiedConstructor(myPrototype: PsiClass, newName: String, asConstructor: Boolean): NewifiedConstructor { + val builder = NewifiedConstructor(myPrototype.manager, newName) + builder.containingClass = myPrototype + builder.setMethodReturnType(TypesUtil.createType(myPrototype)) + builder.navigationElement = myPrototype + builder.isConstructor = asConstructor + return builder + } + + class NewifiedConstructor(val myManager: PsiManager, val newName: String) : LightMethodBuilder(myManager, newName) { + init { + addModifier(PsiModifier.STATIC) + originInfo = newifyOriginInfo + } + + } } \ No newline at end of file diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index 16716de5643e..e07e767658ec 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -46,6 +46,7 @@ + @@ -99,7 +100,7 @@ - + @@ -179,10 +180,12 @@ - + + + diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/NewifySupportTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/NewifySupportTest.groovy index 91e8f17474ff..db42914c109d 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/NewifySupportTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/NewifySupportTest.groovy @@ -38,6 +38,11 @@ public class A { public A(){} public A(String name){} } + +public class A2 { + String name; + int age; +} ''') } @@ -84,6 +89,57 @@ class B { """ } + void testAutoNewifyImplicitConstructor() { + testHighlighting """ +@Newify +class B { + def a = A2.new() +} +""" + testHighlighting """ +@Newify +class B { + def a = A2.new("B") +} +""" + + testHighlighting """ +@Newify +class B { + def a = A2.new(name :"bar") +} +""" + + testHighlighting """ +class B { + @Newify(B) + def b = B() +} +""" + + testHighlighting """ +class B { + @Newify + def a = B.new() +} +""" + + testHighlighting """ +class B2 { + String str + @Newify + def a = B2.new(str: "B2") +} +""" + + testHighlighting """ +class B { + @Newify(value = A2, auto = false) + def a (){ return A2.new()} +} +""" + } + void testNewifyByClass() { testHighlighting """ @Newify([A, Integer]) @@ -155,6 +211,19 @@ class B { } } + void testNewifyLookupImplicitConstructor() { + fixture.configureByText 'a.groovy', """ +@Newify +class B { + def b = B. +} +""" + fixture.completeBasic() + fixture.lookupElementStrings.with { + assert contains("new") + } + } + void testNewifyAutoMapLookup() { testHighlighting """ @Newify(A) diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveMethodTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveMethodTest.groovy index 7d2c92162a13..285408914c63 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveMethodTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveMethodTest.groovy @@ -885,6 +885,12 @@ class Aa { String name; public Aa(){} } +''') + + fixture.addClass(''' +class Cc { + String name; +} ''') def resolved = configureByText(""" @@ -894,6 +900,16 @@ class B { } """).resolve() assertInstanceOf(resolved, PsiMethod) + assertEquals "Aa", (resolved as PsiMethod).returnType.canonicalText + + resolved = configureByText(""" +@Newify(Cc) +class B { + def a = Cc() +} +""").resolve() + assertInstanceOf(resolved, PsiMethod) + assertEquals "Cc", (resolved as PsiMethod).returnType.canonicalText resolved = configureByText(""" @Newify(Aa) @@ -903,6 +919,7 @@ class B { """).resolve() assertInstanceOf(resolved, PsiMethod) + assertEquals "Aa", (resolved as PsiMethod).returnType.canonicalText resolved = configureByText(""" class B { @@ -912,6 +929,7 @@ class B { """).resolve() assertInstanceOf(resolved, PsiMethod) + assertEquals "Aa", (resolved as PsiMethod).returnType.canonicalText resolved = configureByText(""" class B { @@ -921,6 +939,7 @@ class B { """).resolve() assertInstanceOf(resolved, PsiMethod) + assertEquals "Aa", (resolved as PsiMethod).returnType.canonicalText resolved = configureByText(""" class B { From 341f921fa4da64b0d33be8fa305b53cbcdbc2c08 Mon Sep 17 00:00:00 2001 From: Alexander Zolotov Date: Tue, 6 Jun 2017 16:24:52 +0300 Subject: [PATCH 136/136] Unwrap suppression actions while highlighting suppressing container (IDEA-174005) --- .../codeInsight/intention/impl/IntentionHintComponent.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/IntentionHintComponent.java b/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/IntentionHintComponent.java index 62be6049a293..533398096195 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/IntentionHintComponent.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/IntentionHintComponent.java @@ -21,6 +21,7 @@ import com.intellij.codeInsight.daemon.impl.ShowIntentionsPass; import com.intellij.codeInsight.hint.*; import com.intellij.codeInsight.intention.HighPriorityAction; import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.codeInsight.intention.IntentionActionDelegate; import com.intellij.codeInsight.intention.impl.config.IntentionActionWrapper; import com.intellij.codeInsight.intention.impl.config.IntentionManagerSettings; import com.intellij.codeInsight.intention.impl.config.IntentionSettingsConfigurable; @@ -479,7 +480,10 @@ public class IntentionHintComponent implements Disposable, ScrollAwareHint { if (source instanceof DataProvider) { final Object selectedItem = PlatformDataKeys.SELECTED_ITEM.getData((DataProvider)source); if (selectedItem instanceof IntentionActionWithTextCaching) { - final IntentionAction action = ((IntentionActionWithTextCaching)selectedItem).getAction(); + IntentionAction action = ((IntentionActionWithTextCaching)selectedItem).getAction(); + if (action instanceof IntentionActionDelegate) { + action = ((IntentionActionDelegate)action).getDelegate(); + } if (action instanceof SuppressIntentionActionFromFix) { if (injectedFile != null && ((SuppressIntentionActionFromFix)action).isShouldBeAppliedToInjectionHost() == ThreeState.NO) { final PsiElement at = injectedFile.findElementAt(injectedEditor.getCaretModel().getOffset());