diff --git a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DockablePopupManager.java b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DockablePopupManager.java index 5881498247f7..f0cdd35e6ffd 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DockablePopupManager.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DockablePopupManager.java @@ -47,7 +47,6 @@ import java.awt.*; public abstract class DockablePopupManager { protected ToolWindow myToolWindow; - private boolean myAutoUpdateDocumentation = PropertiesComponent.getInstance().isTrueValue(getAutoUpdateEnabledProperty()); private Runnable myAutoUpdateRequest; @NotNull protected final Project myProject; @@ -119,7 +118,7 @@ public abstract class DockablePopupManager { new UiNotifyConnector(component, new Activatable() { @Override public void showNotify() { - restartAutoUpdate(myAutoUpdateDocumentation); + restartAutoUpdate(PropertiesComponent.getInstance().getBoolean(getAutoUpdateEnabledProperty())); } @Override @@ -144,13 +143,12 @@ public abstract class DockablePopupManager { AllIcons.General.AutoscrollFromSource) { @Override public boolean isSelected(AnActionEvent e) { - return myAutoUpdateDocumentation; + return PropertiesComponent.getInstance().getBoolean(getAutoUpdateEnabledProperty()); } @Override public void setSelected(AnActionEvent e, boolean state) { PropertiesComponent.getInstance().setValue(getAutoUpdateEnabledProperty(), state); - myAutoUpdateDocumentation = state; restartAutoUpdate(state); } }; @@ -222,7 +220,7 @@ public abstract class DockablePopupManager { } - protected void restorePopupBehavior() { + public void restorePopupBehavior() { if (myToolWindow != null) { PropertiesComponent.getInstance().setValue(getShowInToolWindowProperty(), Boolean.FALSE.toString()); ToolWindowManagerEx toolWindowManagerEx = ToolWindowManagerEx.getInstanceEx(myProject); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java index 4943255ec236..a00286221c87 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java @@ -145,7 +145,7 @@ public class DocumentationManager extends DockablePopupManager + + + + @@ -867,6 +871,12 @@ + + + com.jetbrains.python.actions.PySciProjectComponent + + + com.jetbrains.python.testing.VFSTestFrameworkListener diff --git a/python/src/com/jetbrains/python/actions/PySciProjectComponent.java b/python/src/com/jetbrains/python/actions/PySciProjectComponent.java new file mode 100644 index 000000000000..38d8ff0b95c9 --- /dev/null +++ b/python/src/com/jetbrains/python/actions/PySciProjectComponent.java @@ -0,0 +1,151 @@ +/* + * 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.actions; + +import com.intellij.codeInsight.documentation.DocumentationManager; +import com.intellij.ide.DataManager; +import com.intellij.notification.NotificationDisplayType; +import com.intellij.notification.NotificationGroup; +import com.intellij.notification.NotificationListener; +import com.intellij.notification.NotificationType; +import com.intellij.openapi.actionSystem.ActionManager; +import com.intellij.openapi.actionSystem.ActionPlaces; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.components.AbstractProjectComponent; +import com.intellij.openapi.components.PersistentStateComponent; +import com.intellij.openapi.components.State; +import com.intellij.openapi.components.Storage; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleUtilCore; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.startup.StartupManager; +import com.intellij.psi.PsiDirectory; +import com.intellij.psi.PsiManager; +import com.jetbrains.python.packaging.PyPackage; +import com.jetbrains.python.packaging.PyPackageUtil; +import com.jetbrains.python.sdk.PythonSdkType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +import static com.jetbrains.python.actions.PySciViewAction.ACTION_ID; + +@State(name = "PySciProjectComponent", storages = @Storage("other.xml")) +public class PySciProjectComponent extends AbstractProjectComponent implements PersistentStateComponent { + private static final NotificationGroup BALLOON_NOTIFICATIONS = new NotificationGroup("Python Scientific View Advertiser", + NotificationDisplayType.STICKY_BALLOON, + false); + private State myState = new State(); + + protected PySciProjectComponent(Project project) { + super(project); + } + + public static PySciProjectComponent getInstance(Project project) { + return project.getComponent(PySciProjectComponent.class); + } + + public void useSciView(boolean useSciView) { + myState.PY_SCI_VIEW = useSciView; + } + + public boolean useSciView() { + return myState.PY_SCI_VIEW; + } + + @Override + public void projectOpened() { + if (myState.PY_SCI_VIEW) { + StartupManager.getInstance(myProject).runWhenProjectIsInitialized(() -> { + final PsiDirectory directory = PsiManager.getInstance(myProject).findDirectory(myProject.getBaseDir()); + if (directory != null) { + DocumentationManager.getInstance(myProject).showJavaDocInfo(directory, directory); + } + }); + } + else if (!myState.PY_SCI_VIEW_SUGGESTED) { + StartupManager.getInstance(myProject).runWhenProjectIsInitialized(() -> { + final PsiDirectory directory = PsiManager.getInstance(myProject).findDirectory(myProject.getBaseDir()); + if (directory != null) { + final Module module = ModuleUtilCore.findModuleForPsiElement(directory); + if (module != null) { + final Sdk sdk = PythonSdkType.findPythonSdk(module); + if (sdk != null) { + final List packages = PyPackageUtil.refreshAndGetPackagesModally(sdk); + final PyPackage numpy = PyPackageUtil.findPackage(packages, "numpy"); + + if (numpy != null) { + showInspectionAdvertisement(myProject); + } + } + } + } + }); + } + } + + private void showInspectionAdvertisement(@NotNull Project project) { + final String msg = "Your source code imports the 'numpy' package." + + "
Would you like to enable Scientific View?
" + + "Yes  No"; + showSingletonNotification(project, msg, NotificationType.INFORMATION, (notification, event) -> { + myState.PY_SCI_VIEW_SUGGESTED = true; + final boolean enabled = "#yes".equals(event.getDescription()); + if (enabled) { + final AnAction action = ActionManager.getInstance().getAction(ACTION_ID); + if (action instanceof PySciViewAction) { + final AnActionEvent anActionEvent = AnActionEvent.createFromDataContext( + ActionPlaces.UNKNOWN, null, DataManager.getInstance().getDataContextFromFocus().getResult()); + ((PySciViewAction)action).setSelected(anActionEvent, true); + } + } + }); + } + + private static void showSingletonNotification(@NotNull Project project, + @NotNull String htmlContent, + @NotNull NotificationType type, + @NotNull NotificationListener listener) { + BALLOON_NOTIFICATIONS.createNotification("Scientific View", htmlContent, type, (notification, event) -> { + try { + listener.hyperlinkUpdate(notification, event); + } + finally { + notification.expire(); + } + }).notify(project); + } + + @Nullable + @Override + public PySciProjectComponent.State getState() { + return myState; + } + + @Override + public void loadState(PySciProjectComponent.State state) { + myState.PY_SCI_VIEW = state.PY_SCI_VIEW; + myState.PY_SCI_VIEW_SUGGESTED = state.PY_SCI_VIEW_SUGGESTED; + } + + public static class State { + public boolean PY_SCI_VIEW = false; + public boolean PY_SCI_VIEW_SUGGESTED = false; + } +} diff --git a/python/src/com/jetbrains/python/actions/PySciViewAction.java b/python/src/com/jetbrains/python/actions/PySciViewAction.java new file mode 100644 index 000000000000..a885cbf09895 --- /dev/null +++ b/python/src/com/jetbrains/python/actions/PySciViewAction.java @@ -0,0 +1,151 @@ +/* + * 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.actions; + +import com.intellij.codeInsight.documentation.DocumentationManager; +import com.intellij.ide.util.PropertiesComponent; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.actionSystem.Presentation; +import com.intellij.openapi.actionSystem.ToggleAction; +import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.popup.JBPopup; +import com.intellij.openapi.wm.*; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiManager; +import com.jetbrains.python.console.PythonConsoleToolWindow; +import org.jetbrains.annotations.NotNull; + +import static com.jetbrains.python.debugger.containerview.PyDataView.DATA_VIEWER_ID; + +public class PySciViewAction extends ToggleAction implements DumbAware { + private static final String TEXT_SCI_VIEW = "Scientific Mode"; + + private boolean wasConsoleVisible = false; + private ToolWindowType myDocumentationType = ToolWindowType.FLOATING; + private boolean myDocumentationAutoHide = true; + + public static final String ACTION_ID = "PySciView"; + private ToolWindowType myDataViewType = ToolWindowType.FLOATING; + + public PySciViewAction() { + super(TEXT_SCI_VIEW); + } + + @Override + public boolean isSelected(AnActionEvent e) { + return e.getProject() != null && PySciProjectComponent.getInstance(e.getProject()).useSciView(); + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + final Project project = e.getProject(); + if (project == null) return; + PySciProjectComponent.getInstance(project).useSciView(state); + + final PsiElement element = getPsiElement(e, project); + + if (state) { + showConsoleToolwindow(project); + showDocumentationToolwindow(project, element); + showDataViewAsToolwindow(project); + } + else { + hideConsoleToolwindow(project); + restoreDocumentationPopup(project); + hideDataViewer(project); + } + } + + private void showConsoleToolwindow(@NotNull final Project project) { + final ToolWindow consoleToolWindow = PythonConsoleToolWindow.getInstance(project).getToolWindow(); + wasConsoleVisible = consoleToolWindow.isVisible(); + consoleToolWindow.show(null); + } + + private void hideConsoleToolwindow(@NotNull final Project project) { + if (!wasConsoleVisible) { + PythonConsoleToolWindow.getInstance(project).getToolWindow().hide(null); + } + } + + private void showDocumentationToolwindow(Project project, PsiElement element) { + final String showInToolWindowProperty = DocumentationManager.getInstance(project).getShowInToolWindowProperty(); + + PropertiesComponent.getInstance().setValue(showInToolWindowProperty, true); + PropertiesComponent.getInstance().setValue(DocumentationManager.getInstance(project).getAutoUpdateEnabledProperty(), true); + + if (element != null) { + DocumentationManager.getInstance(project).showJavaDocInfo(element, element); + } + final ToolWindow window = ToolWindowManager.getInstance(project).getToolWindow(ToolWindowId.DOCUMENTATION); + if (window != null) { + window.setAnchor(ToolWindowAnchor.RIGHT, null); + myDocumentationType = window.getType(); + myDocumentationAutoHide = window.isAutoHide(); + window.setType(ToolWindowType.DOCKED, null); + window.setAutoHide(false); + } + } + + private void restoreDocumentationPopup(Project project) { + PropertiesComponent.getInstance().setValue(DocumentationManager.getInstance(project).getAutoUpdateEnabledProperty(), false); + + final ToolWindow window = ToolWindowManager.getInstance(project).getToolWindow(ToolWindowId.DOCUMENTATION); + if (window != null) { + window.setType(myDocumentationType, null); + window.setAutoHide(myDocumentationAutoHide); + DocumentationManager.getInstance(project).restorePopupBehavior(); + final JBPopup hint = DocumentationManager.getInstance(project).getDocInfoHint(); + if (hint != null) { + hint.cancel(); + } + } + } + + private void showDataViewAsToolwindow(@NotNull final Project project) { + ToolWindow dataViewToolWindow = ToolWindowManager.getInstance(project).getToolWindow(DATA_VIEWER_ID); + myDataViewType = dataViewToolWindow.getType(); + dataViewToolWindow.setType(ToolWindowType.DOCKED, null); + dataViewToolWindow.setShowStripeButton(true); + } + + private void hideDataViewer(Project project) { + final ToolWindow dataViewer = ToolWindowManager.getInstance(project).getToolWindow(DATA_VIEWER_ID); + dataViewer.setType(myDataViewType, null); + dataViewer.setShowStripeButton(false); + } + + private static PsiElement getPsiElement(AnActionEvent e, Project project) { + PsiElement element = CommonDataKeys.PSI_ELEMENT.getData(e.getDataContext()); + if (element == null) { + element = CommonDataKeys.PSI_FILE.getData(e.getDataContext()); + } + if (element == null) { + element = PsiManager.getInstance(project).findDirectory(project.getBaseDir()); + } + return element; + } + + @Override + public void update(@NotNull AnActionEvent e) { + super.update(e); + final Project project = e.getProject(); + final Presentation presentation = e.getPresentation(); + presentation.setEnabledAndVisible(project != null); + } +} diff --git a/python/src/com/jetbrains/python/console/PythonConsoleView.java b/python/src/com/jetbrains/python/console/PythonConsoleView.java index c84deff5f118..10c6fe539502 100644 --- a/python/src/com/jetbrains/python/console/PythonConsoleView.java +++ b/python/src/com/jetbrains/python/console/PythonConsoleView.java @@ -95,6 +95,7 @@ public class PythonConsoleView extends LanguageConsoleImpl implements Observable private XStandaloneVariablesView mySplitView; private ActionCallback myInitialized = new ActionCallback(); + private boolean isShowVars = true; public PythonConsoleView(final Project project, final String title, final Sdk sdk) { super(project, title, PythonLanguage.getInstance()); @@ -115,6 +116,10 @@ public class PythonConsoleView extends LanguageConsoleImpl implements Observable public void setConsoleCommunication(final ConsoleCommunication communication) { getFile().putCopyableUserData(PydevConsoleRunner.CONSOLE_KEY, communication); + + if (isShowVars && communication instanceof PydevConsoleCommunication) { + showVariables((PydevConsoleCommunication)communication); + } } private PyConsoleStartFolding createConsoleFolding() { @@ -491,4 +496,12 @@ public class PythonConsoleView extends LanguageConsoleImpl implements Observable public void initialized() { myInitialized.setDone(); } + + public void setShowVars(boolean showVars) { + isShowVars = showVars; + } + + public boolean isShowVars() { + return isShowVars; + } } diff --git a/python/src/com/jetbrains/python/console/actions/ShowVarsAction.kt b/python/src/com/jetbrains/python/console/actions/ShowVarsAction.kt index 5485784d8110..a1bdc155040c 100644 --- a/python/src/com/jetbrains/python/console/actions/ShowVarsAction.kt +++ b/python/src/com/jetbrains/python/console/actions/ShowVarsAction.kt @@ -26,16 +26,15 @@ import com.jetbrains.python.console.PythonConsoleView * Created by Yuli Fiterman on 9/18/2016. */ class ShowVarsAction(private val consoleView: PythonConsoleView, private val consoleComm: PydevConsoleCommunication) : ToggleAction("Show Variables", "Shows active console variables", AllIcons.Debugger.Watches), DumbAware { - private var mySelected = false override fun isSelected(e: AnActionEvent): Boolean { - return mySelected + return consoleView.isShowVars } override fun setSelected(e: AnActionEvent, state: Boolean) { - mySelected = state + consoleView.isShowVars = state - if (mySelected) { + if (state) { consoleView.showVariables(consoleComm) } else {