From aa9f4ff2a98b073848113a63fd0299b5effc39b6 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Wed, 30 Aug 2017 18:31:57 +0300 Subject: [PATCH] load and render python stdlib in scientific mode --- python/python-community.iml | 13 +++- .../python/actions/PySciViewAction.java | 12 ++++ .../PyStdlibDocumentationLinkProvider.java | 3 +- .../PyIntegratedToolsConfigurable.form | 10 ++- .../PyIntegratedToolsConfigurable.java | 8 ++- .../PyDocumentationSettings.java | 9 +++ .../PythonDocumentationProvider.java | 62 ++++++++++++++++++- 7 files changed, 110 insertions(+), 7 deletions(-) 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/com/jetbrains/python/actions/PySciViewAction.java b/python/src/com/jetbrains/python/actions/PySciViewAction.java index eb900f63169c..33364d0ee0b0 100644 --- a/python/src/com/jetbrains/python/actions/PySciViewAction.java +++ b/python/src/com/jetbrains/python/actions/PySciViewAction.java @@ -26,6 +26,8 @@ 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; @@ -35,6 +37,7 @@ 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; @@ -78,12 +81,21 @@ public class PySciViewAction extends ToggleAction implements DumbAware { 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); } } 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/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