diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/LiveVariablesAnalyzer.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/LiveVariablesAnalyzer.java index 0c559889e31d..18f2f5f36fc0 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/LiveVariablesAnalyzer.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/LiveVariablesAnalyzer.java @@ -146,8 +146,9 @@ public class LiveVariablesAnalyzer { BitSet set = result.get(instruction); if (set != null) { set.or(liveVars); - return set; - } else { + return (BitSet)set.clone(); + } + else if (!liveVars.isEmpty()) { result.put((FinishElementInstruction)instruction, (BitSet)liveVars.clone()); } } 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/DocumentationComponent.java b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationComponent.java index 0e695ee8c1ca..df91737f02f5 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationComponent.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationComponent.java @@ -104,7 +104,7 @@ public class DocumentationComponent extends JPanel implements Disposable, DataPr private SmartPsiElementPointer myElement; private long myModificationCount; - private static final String QUICK_DOC_FONT_SIZE_PROPERTY = "quick.doc.font.size"; + public static final String QUICK_DOC_FONT_SIZE_PROPERTY = "quick.doc.font.size"; private final Stack myBackStack = new Stack<>(); private final Stack myForwardStack = new Stack<>(); 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 void applyConfigValueToUserData(VirtualFile file, Key userDataKey, String editorConfigKey, String configValue, Map configMap) { - if (configValue.isEmpty()) return; + if (configValue.isEmpty()) { + file.putUserData(userDataKey, null); + return; + } final T data = configMap.get(configValue); if (data == null) { diff --git a/python/python-community.iml b/python/python-community.iml index b5f980a97e1a..a1d6fc61d2b9 100644 --- a/python/python-community.iml +++ b/python/python-community.iml @@ -26,6 +26,15 @@ + + + + + + + + + + - - + \ No newline at end of file diff --git a/python/src/META-INF/python-core-common.xml b/python/src/META-INF/python-core-common.xml index 0f77daa4edaf..6b1921eb801c 100644 --- a/python/src/META-INF/python-core-common.xml +++ b/python/src/META-INF/python-core-common.xml @@ -809,6 +809,10 @@ + + + + @@ -860,6 +864,10 @@ + + + + @@ -867,6 +875,12 @@ + + + com.jetbrains.python.actions.PySciProjectComponent + + + com.jetbrains.python.testing.VFSTestFrameworkListener diff --git a/python/src/com/jetbrains/python/actions/PyRunFileInConsoleAction.java b/python/src/com/jetbrains/python/actions/PyRunFileInConsoleAction.java new file mode 100644 index 000000000000..d0817b6a0fd2 --- /dev/null +++ b/python/src/com/jetbrains/python/actions/PyRunFileInConsoleAction.java @@ -0,0 +1,73 @@ +/* + * 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.execution.ExecutionManager; +import com.intellij.execution.RunManager; +import com.intellij.execution.RunnerAndConfigurationSettings; +import com.intellij.execution.actions.ConfigurationContext; +import com.intellij.execution.actions.ConfigurationFromContext; +import com.intellij.execution.actions.RunConfigurationProducer; +import com.intellij.execution.executors.DefaultRunExecutor; +import com.intellij.execution.runners.ExecutionEnvironmentBuilder; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.actionSystem.Presentation; +import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiFile; +import com.jetbrains.python.psi.PyFile; +import com.jetbrains.python.run.PythonRunConfigurationParams; +import com.jetbrains.python.run.PythonRunConfigurationProducer; +import org.jetbrains.annotations.NotNull; + +public class PyRunFileInConsoleAction extends AnAction implements DumbAware { + public PyRunFileInConsoleAction() { + super("Run File in Console"); + } + + @Override + public void update(@NotNull AnActionEvent e) { + super.update(e); + final Project project = e.getProject(); + final PsiFile psiFile = CommonDataKeys.PSI_FILE.getData(e.getDataContext()); + final Presentation presentation = e.getPresentation(); + presentation.setEnabledAndVisible(project != null && psiFile instanceof PyFile); + } + + @Override + public void actionPerformed(AnActionEvent e) { + final PsiFile file = CommonDataKeys.PSI_FILE.getData(e.getDataContext()); + if (file == null) return; + final Project project = e.getProject(); + if (project == null) return; + final ConfigurationContext context = ConfigurationContext.getFromContext(e.getDataContext()); + final ConfigurationFromContext fromContext = + RunConfigurationProducer.getInstance(PythonRunConfigurationProducer.class).createConfigurationFromContext(context); + if (fromContext == null) return; + final RunnerAndConfigurationSettings settings = fromContext.getConfigurationSettings(); + final PythonRunConfigurationParams configuration = (PythonRunConfigurationParams)settings.getConfiguration(); + configuration.setShowCommandLineAfterwards(true); + RunManager runManager = RunManager.getInstance(project); + runManager.setTemporaryConfiguration(settings); + runManager.setSelectedConfiguration(settings); + ExecutionEnvironmentBuilder builder = ExecutionEnvironmentBuilder.createOrNull(DefaultRunExecutor.getRunExecutorInstance(), settings); + if (builder != null) { + ExecutionManager.getInstance(project).restartRunProfile(builder.build()); + } + } +} 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..206e892da398 --- /dev/null +++ b/python/src/com/jetbrains/python/actions/PySciProjectComponent.java @@ -0,0 +1,158 @@ +/* + * 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.editor.colors.EditorColorsManager; +import com.intellij.openapi.editor.colors.EditorColorsScheme; +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.intellij.util.ui.UIUtil; +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.intellij.codeInsight.documentation.DocumentationComponent.COLOR_KEY; +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(() -> { + EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme(); + scheme.setColor(COLOR_KEY, UIUtil.getEditorPaneBackground()); + + 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..33364d0ee0b0 --- /dev/null +++ b/python/src/com/jetbrains/python/actions/PySciViewAction.java @@ -0,0 +1,213 @@ +/* + * 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.execution.RunManager; +import com.intellij.execution.RunnerAndConfigurationSettings; +import com.intellij.execution.configurations.RunConfiguration; +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.editor.colors.EditorColorsManager; +import com.intellij.openapi.editor.colors.EditorColorsScheme; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleUtilCore; +import com.intellij.openapi.options.FontSize; +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.intellij.util.ui.UIUtil; +import com.jetbrains.python.console.PythonConsoleToolWindow; +import com.jetbrains.python.documentation.PyDocumentationSettings; +import com.jetbrains.python.run.PythonConfigurationType; +import com.jetbrains.python.run.PythonRunConfiguration; +import org.jetbrains.annotations.NotNull; + +import java.awt.*; + +import static com.intellij.codeInsight.documentation.DocumentationComponent.COLOR_KEY; +import static com.intellij.codeInsight.documentation.DocumentationComponent.QUICK_DOC_FONT_SIZE_PROPERTY; +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; + private Color myDocumentationBackgroundColor = EditorColorsManager.getInstance().getGlobalScheme().getColor(COLOR_KEY); + + 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); + showCommandLineInRunConfiguration(project, true); + renderExternalDocumentation(element, true); + } + else { + hideConsoleToolwindow(project); + restoreDocumentationPopup(project); + hideDataViewer(project); + showCommandLineInRunConfiguration(project, false); + renderExternalDocumentation(element, false); + } + } + + private static void renderExternalDocumentation(PsiElement element, boolean render) { + final Module module = ModuleUtilCore.findModuleForPsiElement(element); + if (module != null) { + PyDocumentationSettings.getInstance(module).setRenderExternalDocumentation(render); + } + } + + private static void showCommandLineInRunConfiguration(Project project, boolean show) { + final RunnerAndConfigurationSettings template = + RunManager.getInstance(project).getConfigurationTemplate(PythonConfigurationType.getInstance().getFactory()); + final RunConfiguration configuration = template.getConfiguration(); + if (configuration instanceof PythonRunConfiguration) { + ((PythonRunConfiguration)configuration).setShowCommandLineAfterwards(show); + } + } + + 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(); + + EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme(); + myDocumentationBackgroundColor = scheme.getColor(COLOR_KEY); + scheme.setColor(COLOR_KEY, UIUtil.getEditorPaneBackground()); + + PropertiesComponent.getInstance().setValue(showInToolWindowProperty, true); + PropertiesComponent.getInstance().setValue(DocumentationManager.getInstance(project).getAutoUpdateEnabledProperty(), true); + + if (element != null) { + DocumentationManager.getInstance(project).showJavaDocInfo(element, element); + } + setDocFontSize(); + + 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 static void setDocFontSize() { + EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme(); + final int editorFontSize = scheme.getEditorFontSize(); + final FontSize[] values = FontSize.values(); + FontSize fontSize = FontSize.MEDIUM; + for (FontSize value : values) { + if (value.getSize() > editorFontSize) { + break; + } + fontSize = value; + } + + PropertiesComponent.getInstance().setValue(QUICK_DOC_FONT_SIZE_PROPERTY, String.valueOf(fontSize.toString())); + } + + private void restoreDocumentationPopup(Project project) { + PropertiesComponent.getInstance().setValue(DocumentationManager.getInstance(project).getAutoUpdateEnabledProperty(), false); + EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme(); + scheme.setColor(COLOR_KEY, myDocumentationBackgroundColor); + + 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.setAutoHide(false); + dataViewToolWindow.setShowStripeButton(true); + } + + private void hideDataViewer(Project project) { + final ToolWindow dataViewer = ToolWindowManager.getInstance(project).getToolWindow(DATA_VIEWER_ID); + dataViewer.setType(myDataViewType, null); + dataViewer.setAutoHide(true); + 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/codeInsight/stdlib/PyStdlibDocumentationLinkProvider.java b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibDocumentationLinkProvider.java index e982087327e5..9325337c3ecc 100644 --- a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibDocumentationLinkProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibDocumentationLinkProvider.java @@ -22,6 +22,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFileSystemItem; import com.intellij.psi.PsiNamedElement; +import com.intellij.psi.util.QualifiedName; import com.jetbrains.python.PyNames; import com.jetbrains.python.documentation.PythonDocumentationLinkProvider; import com.jetbrains.python.documentation.PythonDocumentationProvider; @@ -29,7 +30,6 @@ import com.jetbrains.python.psi.PyClass; import com.jetbrains.python.psi.PyFile; import com.jetbrains.python.psi.PyFunction; import com.jetbrains.python.psi.impl.PyBuiltinCache; -import com.intellij.psi.util.QualifiedName; import com.jetbrains.python.psi.resolve.QualifiedNameFinder; import com.jetbrains.python.sdk.PythonSdkType; @@ -544,6 +544,7 @@ public class PyStdlibDocumentationLinkProvider implements PythonDocumentationLin @Override public String getExternalDocumentationUrl(PsiElement element, PsiElement originalElement) { + if (PyBuiltinCache.getInstance(element).isBuiltin(element)) return null; PsiFileSystemItem file = element instanceof PsiFileSystemItem ? (PsiFileSystemItem) element : element.getContainingFile(); if (PyNames.INIT_DOT_PY.equals(file.getName())) { file = file.getParent(); diff --git a/python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.form b/python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.form index 5aacd414fb03..50e117174f63 100644 --- a/python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.form +++ b/python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.form @@ -49,7 +49,7 @@
- + @@ -82,6 +82,14 @@ + + + + + + + + diff --git a/python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.java b/python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.java index dc3d88ca5cd3..c519eda530ce 100644 --- a/python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.java +++ b/python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.java @@ -39,7 +39,6 @@ import com.intellij.ui.ListCellRendererWrapper; import com.intellij.util.FileContentUtil; import com.intellij.util.FileContentUtilCore; import com.jetbrains.python.PyBundle; -import com.jetbrains.python.PyNames; import com.jetbrains.python.PythonFileType; import com.jetbrains.python.ReSTService; import com.jetbrains.python.documentation.PyDocumentationSettings; @@ -78,6 +77,7 @@ public class PyIntegratedToolsConfigurable implements SearchableConfigurable { private JCheckBox analyzeDoctest; private JPanel myDocStringsPanel; private JPanel myRestPanel; + private JCheckBox renderExternal; public PyIntegratedToolsConfigurable(@NotNull Module module) { myModule = module; @@ -99,6 +99,7 @@ public class PyIntegratedToolsConfigurable implements SearchableConfigurable { myWorkDir.setText(service.getWorkdir()); txtIsRst.setSelected(service.txtIsRst()); analyzeDoctest.setSelected(myDocumentationSettings.isAnalyzeDoctest()); + renderExternal.setSelected(myDocumentationSettings.isRenderExternalDocumentation()); myRequirementsPathField.addBrowseFolderListener("Choose path to the package requirements file:", null, myProject, FileChooserDescriptorFactory.createSingleLocalFileDescriptor()); myRequirementsPathField.setText(getRequirementsPath()); @@ -207,6 +208,9 @@ public class PyIntegratedToolsConfigurable implements SearchableConfigurable { if (analyzeDoctest.isSelected() != myDocumentationSettings.isAnalyzeDoctest()) { return true; } + if (renderExternal.isSelected() != myDocumentationSettings.isRenderExternalDocumentation()) { + return true; + } if (!ReSTService.getInstance(myModule).getWorkdir().equals(myWorkDir.getText())) { return true; } @@ -235,6 +239,7 @@ public class PyIntegratedToolsConfigurable implements SearchableConfigurable { FileContentUtil.reparseFiles(myProject, Lists.newArrayList(files), false); } myModel.apply(); + myDocumentationSettings.setRenderExternalDocumentation(renderExternal.isSelected()); myDocumentationSettings.setFormat((DocStringFormat)myDocstringFormatComboBox.getSelectedItem()); final ReSTService reSTService = ReSTService.getInstance(myModule); reSTService.setWorkdir(myWorkDir.getText()); @@ -271,6 +276,7 @@ public class PyIntegratedToolsConfigurable implements SearchableConfigurable { myWorkDir.setText(ReSTService.getInstance(myModule).getWorkdir()); txtIsRst.setSelected(ReSTService.getInstance(myModule).txtIsRst()); analyzeDoctest.setSelected(myDocumentationSettings.isAnalyzeDoctest()); + renderExternal.setSelected(myDocumentationSettings.isRenderExternalDocumentation()); myRequirementsPathField.setText(getRequirementsPath()); } 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 { diff --git a/python/src/com/jetbrains/python/documentation/PyDocumentationSettings.java b/python/src/com/jetbrains/python/documentation/PyDocumentationSettings.java index fd43b67011b1..9e21a47b6c43 100644 --- a/python/src/com/jetbrains/python/documentation/PyDocumentationSettings.java +++ b/python/src/com/jetbrains/python/documentation/PyDocumentationSettings.java @@ -47,6 +47,7 @@ public class PyDocumentationSettings implements PersistentStateComponent docUrls) { - return null; + final Module module = ModuleUtilCore.findModuleForPsiElement(element); + if (module != null && !PyDocumentationSettings.getInstance(module).isRenderExternalDocumentation()) return null; + return ApplicationManager.getApplication().runReadAction((Computable)() -> { + PsiFileSystemItem file = element instanceof PsiFileSystemItem ? (PsiFileSystemItem)element : element.getContainingFile(); + if (file == null) return null; + if (PyNames.INIT_DOT_PY.equals(file.getName())) { + file = file.getParent(); + assert file != null; + } + final Sdk sdk = PyBuiltinCache.findSdkForFile(file); + if (sdk == null) { + return null; + } + + final QualifiedName moduleQName = QualifiedNameFinder.findCanonicalImportPath(element, element); + if (moduleQName == null) { + return null; + } + PsiNamedElement namedElement = (element instanceof PsiNamedElement && !(element instanceof PsiFileSystemItem)) + ? (PsiNamedElement)element + : null; + if (namedElement instanceof PyFunction && PyNames.INIT.equals(namedElement.getName())) { + final PyClass containingClass = ((PyFunction)namedElement).getContainingClass(); + if (containingClass != null) { + namedElement = containingClass; + } + } + final PyStdlibDocumentationLinkProvider stdlibDocumentationLinkProvider = + Extensions.findExtension(PythonDocumentationLinkProvider.EP_NAME, PyStdlibDocumentationLinkProvider.class); + final String url = stdlibDocumentationLinkProvider.getExternalDocumentationUrl(element, element); + if (url == null) { + return null; + } + + try { + final Document document = Jsoup.parse(new URL(url), 1000); + final String elementId = namedElement != null ? moduleQName + "." + namedElement.getName() : "module-" + moduleQName; + document.select("a.headerlink").remove(); + final Elements parents = document.getElementsByAttributeValue("id", elementId).parents(); + if (parents.isEmpty()) { + return document.toString(); + } + return parents.get(0).toString(); + } + catch (MalformedURLException ignored) { + } + catch (IOException ignored) { + } + return null; + }); } @Override @@ -489,9 +549,27 @@ public class PythonDocumentationProvider extends AbstractDocumentationProvider i public PsiElement getCustomDocumentationElement(@NotNull Editor editor, @NotNull PsiFile file, @Nullable PsiElement contextElement) { - if (contextElement != null && - PythonDialectsTokenSetProvider.INSTANCE.getKeywordTokens().contains(contextElement.getNode().getElementType())) { - return contextElement; + if (contextElement != null) { + final IElementType elementType = contextElement.getNode().getElementType(); + if (PythonDialectsTokenSetProvider.INSTANCE.getKeywordTokens().contains(elementType)) { + return contextElement; + } + if (PyTokenTypes.LPAR == elementType || PyTokenTypes.RPAR == elementType) { + final PyCallExpression expression = PsiTreeUtil.getParentOfType(contextElement, PyCallExpression.class); + if (expression != null) { + final PyExpression callee = expression.getCallee(); + if (callee != null) { + final PsiReference reference = callee.getReference(); + if (reference != null) { + return reference.resolve(); + } + } + } + } + if (PyTokenTypes.DOCSTRING == elementType) { + final PyDocStringOwner docstringOwner = PsiTreeUtil.getParentOfType(contextElement, PyDocStringOwner.class); + if (docstringOwner != null) return docstringOwner; + } } return super.getCustomDocumentationElement(editor, file, contextElement); } diff --git a/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java index 8a1a518df04e..3013196c011d 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java @@ -588,6 +588,10 @@ public class PyTargetExpressionImpl extends PyBaseElementImpldef foo()
Inferred type: () -> None

Doc of foo.
\ No newline at end of file diff --git a/python/testData/quickdoc/ArgumentList.py b/python/testData/quickdoc/ArgumentList.py new file mode 100644 index 000000000000..6aff48527784 --- /dev/null +++ b/python/testData/quickdoc/ArgumentList.py @@ -0,0 +1,6 @@ +# directly in function +def foo(): + "Doc of foo." + pass + +foo() diff --git a/python/testData/quickdoc/BuiltinLen.html b/python/testData/quickdoc/BuiltinLen.html index db51bb72f7e6..805ad69fbbd3 100644 --- a/python/testData/quickdoc/BuiltinLen.html +++ b/python/testData/quickdoc/BuiltinLen.html @@ -1 +1 @@ -def len(o: Sized)
Inferred type: (o: Sized) -> int


len(object) -> integer

Return the number of items of a sequence or collection.

External documentation:
http://docs.python.org/2.7 Mock SDK/library/__builtin__.html#__builtin__.len +def len(o: Sized)
Inferred type: (o: Sized) -> int


len(object) -> integer

Return the number of items of a sequence or collection.
diff --git a/python/testData/quickdoc/Docstring.html b/python/testData/quickdoc/Docstring.html new file mode 100644 index 000000000000..e4cfe0fd4e36 --- /dev/null +++ b/python/testData/quickdoc/Docstring.html @@ -0,0 +1 @@ +def foo()
Inferred type: () -> None

Doc of foo.
\ No newline at end of file diff --git a/python/testData/quickdoc/Docstring.py b/python/testData/quickdoc/Docstring.py new file mode 100644 index 000000000000..a703742ebbb4 --- /dev/null +++ b/python/testData/quickdoc/Docstring.py @@ -0,0 +1,6 @@ +# directly in function +def foo(): + "Doc of foo." + pass + +foo() diff --git a/python/testSrc/com/jetbrains/python/PyQuickDocTest.java b/python/testSrc/com/jetbrains/python/PyQuickDocTest.java index 1e7823e065f5..8f0599d55795 100644 --- a/python/testSrc/com/jetbrains/python/PyQuickDocTest.java +++ b/python/testSrc/com/jetbrains/python/PyQuickDocTest.java @@ -292,6 +292,22 @@ public class PyQuickDocTest extends LightMarkedTestCase { checkHTMLOnly(); } + public void testArgumentList() { + Map marks = loadTest(); + final PsiElement originalElement = marks.get(""); + + final PsiElement element = myProvider.getCustomDocumentationElement(myFixture.getEditor(), myFile, originalElement); + checkByHTML(myProvider.generateDoc(element, originalElement)); + } + + public void testDocstring() { + Map marks = loadTest(); + final PsiElement originalElement = marks.get(""); + + final PsiElement element = myProvider.getCustomDocumentationElement(myFixture.getEditor(), myFile, originalElement); + checkByHTML(myProvider.generateDoc(element, originalElement)); + } + public void testReferenceToMethodQualifiedWithInstance() { checkHTMLOnly(); }